@vosjs/cli 0.46.1 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -5
- package/dist/{chunk-UE66DC2K.js → chunk-ANKECV7F.js} +658 -60
- package/dist/chunk-ANKECV7F.js.map +1 -0
- package/dist/{chunk-ID4OUS5L.js → chunk-AUPGJJTL.js} +18 -1
- package/dist/chunk-AUPGJJTL.js.map +1 -0
- package/dist/{chunk-EFI3WUQB.js → chunk-P3X5KVA6.js} +15 -2
- package/dist/{chunk-EFI3WUQB.js.map → chunk-P3X5KVA6.js.map} +1 -1
- package/dist/cli.js +4 -4
- package/dist/index.js +3 -3
- package/dist/manifest-A4R367EM.js +8 -0
- package/dist/{run-2OZS25ER.js → run-CYHH56KA.js} +3 -3
- package/dist/{shotList-JC4A3FBT.js → shotList-7F2FE3RU.js} +2 -2
- package/package.json +9 -7
- package/schema/actions.schema.json +2 -2
- package/skills/VERSION +1 -0
- package/skills/launch-kit/SKILL.md +356 -0
- package/skills/launch-kit/references/channel-specs.md +56 -0
- package/skills/product-video/SKILL.md +263 -0
- package/skills/product-video/references/destinations.md +71 -0
- package/skills/product-video/references/sessions.md +226 -0
- package/skills/product-video/references/taste.md +115 -0
- package/skills/product-video/references/troubleshooting.md +116 -0
- package/skills/vos-authoring/SKILL.md +191 -0
- package/skills/vos-authoring/references/examples.md +239 -0
- package/skills/vos-authoring/references/schema-reference.md +468 -0
- package/skills/vos-create/SKILL.md +258 -0
- package/skills/vos-cut/SKILL.md +241 -0
- package/skills/vos-footage/SKILL.md +98 -0
- package/skills/vos-migrate/SKILL.md +108 -0
- package/skills/vos-remix/SKILL.md +136 -0
- package/skills/vos-remix/references/3d-recipe.md +37 -0
- package/skills/vos-remix/references/params-knobs.md +80 -0
- package/skills/vos-remix/references/remix-contract.md +81 -0
- package/dist/chunk-ID4OUS5L.js.map +0 -1
- package/dist/chunk-UE66DC2K.js.map +0 -1
- package/dist/manifest-UCS6OVWZ.js +0 -8
- /package/dist/{manifest-UCS6OVWZ.js.map → manifest-A4R367EM.js.map} +0 -0
- /package/dist/{run-2OZS25ER.js.map → run-CYHH56KA.js.map} +0 -0
- /package/dist/{shotList-JC4A3FBT.js.map → shotList-7F2FE3RU.js.map} +0 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Taste — the product-video quality bar
|
|
2
|
+
|
|
3
|
+
The rubric for judging a take before its render ships. Judge stills
|
|
4
|
+
**multimodally**: look at the images, don't just check numbers.
|
|
5
|
+
|
|
6
|
+
## The quality loop (run it, don't skip it)
|
|
7
|
+
|
|
8
|
+
1. `vos record --actions actions.json --out take --strict --json` —
|
|
9
|
+
exit 2 means the flow is broken; fix selectors, never ship around a skip.
|
|
10
|
+
2. `vos validate take` — doc lints must pass (warnings are judgment calls).
|
|
11
|
+
3. `vos frames take --at-zooms --times 0,25%,50%,75%,100%` — look at every
|
|
12
|
+
still against the rubric below. The zoom apexes are where quality lives;
|
|
13
|
+
judge those hardest.
|
|
14
|
+
4. Iterate doc.json → spot-check with `vos render take x.webm --range a..b --draft`
|
|
15
|
+
(seconds, not a full re-render) → re-frame the changed region.
|
|
16
|
+
5. Full render only when the stills pass. Verify the final with one more
|
|
17
|
+
`frames --at-zooms` against the SAME rubric before declaring done.
|
|
18
|
+
|
|
19
|
+
## Story shape
|
|
20
|
+
|
|
21
|
+
1. **One idea per clip.** A take demonstrates one flow or one feature —
|
|
22
|
+
navigation to it is overhead, not story. Open as close to the money shot
|
|
23
|
+
as possible; a cold viewer should see the point inside 3 seconds.
|
|
24
|
+
2. **≤ 30s for marketing clips**; 8–15s is the sweet spot for a page hero.
|
|
25
|
+
Launch videos may run longer but every scene must still earn its seconds.
|
|
26
|
+
3. **End settled.** Close on a resolved state (result visible, motion parked),
|
|
27
|
+
never mid-action — the loop point should read as intentional.
|
|
28
|
+
|
|
29
|
+
## Flow authoring (actions.json)
|
|
30
|
+
|
|
31
|
+
4. **Route the cursor away from hover-traps.** Nav links that live next to
|
|
32
|
+
hover-triggered menus (mega-panels, frosted backdrop-blur scrims) will
|
|
33
|
+
open them as the cursor passes — the rest of the take then plays behind a
|
|
34
|
+
blur. Unless the menu IS the subject, path the cursor below the nav
|
|
35
|
+
(`move` to page content first), or target an equivalent link outside the
|
|
36
|
+
nav.
|
|
37
|
+
5. **Pacing is the zoom plan.** Hovers of 700–900ms on the things that matter
|
|
38
|
+
become the zooms; open with `wait ≥700ms`, give navigations 1200–2000ms,
|
|
39
|
+
end with a settle wait. A rushed flow reads as a rushed video AND plans
|
|
40
|
+
no zooms.
|
|
41
|
+
6. **Real interactions only.** Click things that respond, type real strings
|
|
42
|
+
(no "asdf"), scroll to content — the witnessed-capture claim dies the
|
|
43
|
+
moment footage looks staged.
|
|
44
|
+
|
|
45
|
+
## Zoom & camera
|
|
46
|
+
|
|
47
|
+
7. **Levels 1.4–2.8 read well**; above 3× is for genuine detail (a number, a
|
|
48
|
+
toggle), and must be brief. Below 1.3× reads as drift, not intent.
|
|
49
|
+
8. **One zoom per beat.** Zooms that chain across unrelated UI read as a lost
|
|
50
|
+
camera. If the planner over-suggested, delete spans — an un-zoomed second
|
|
51
|
+
is better than an unmotivated one.
|
|
52
|
+
9. **Focus on the subject, not the cursor resting spot.** `cx/cy` are
|
|
53
|
+
normalized [0..1]; check the apex still shows the thing the beat is about,
|
|
54
|
+
with breathing room (the subject inside the middle ~60% of the crop).
|
|
55
|
+
10. **Camera style is a sentence, not a spice rack.** Default `glide` for
|
|
56
|
+
marketing calm; `snappy` only for dense click-through demos; never mix
|
|
57
|
+
intents within one short clip.
|
|
58
|
+
|
|
59
|
+
## Frame & destination
|
|
60
|
+
|
|
61
|
+
11. **Footage honesty.** Never export above the recording's capture width —
|
|
62
|
+
`validate` warns; fix the viewport at record time instead of upscaling.
|
|
63
|
+
12. **Background is ambience, not a subject.** A `frame.backgroundMedia`
|
|
64
|
+
loop should sit _behind_ the work, never compete with it: prefer a calm
|
|
65
|
+
ambient loop, and set `dim` (≈0.2–0.4) whenever the card carries dense
|
|
66
|
+
UI or the take runs long enough to loop visibly. A high-contrast or fast
|
|
67
|
+
background behind a text-heavy screen is a rejection.
|
|
68
|
+
13. **Tilt is punctuation, not texture.** `doc.tilt` spans lean the card for
|
|
69
|
+
a beat and return to rest — at most one pose change per ~5s, in the
|
|
70
|
+
±5..18° band, paired with the zoom moments (same `in`/`out` chains the
|
|
71
|
+
two camera moves; `tiltStyle` does this for you). Lean TOWARD the focus
|
|
72
|
+
(right-side focus = negative `ry`). Never oscillate — a wobbling card is
|
|
73
|
+
a rejection — and never leave the card leaning across a whole take: a
|
|
74
|
+
permanent lean reads as a rendering bug, not a style. Spans are the only
|
|
75
|
+
way to pose the card (there is no static tilt), so rest is always flat
|
|
76
|
+
and every lean has to earn its moment.
|
|
77
|
+
|
|
78
|
+
## Smoothness — the north star
|
|
79
|
+
|
|
80
|
+
A product video's baseline quality is SMOOTHNESS: the frame should always be
|
|
81
|
+
alive, and nothing should change all-at-once unless it's a deliberate cut.
|
|
82
|
+
The two enemies, both measurable:
|
|
83
|
+
|
|
84
|
+
14. **Dead time.** A still frame is not the defect: a page the viewer is
|
|
85
|
+
reading is content, and a workspace tour is mostly still. What is dead
|
|
86
|
+
is a still frame under a PARKED cursor past the beat it takes to read
|
|
87
|
+
what changed: about 1 s after a page changed, 0.6 s after a control
|
|
88
|
+
did. The record done event reports it per step as `dead` (`ms`, `pct`,
|
|
89
|
+
`steps[]` with how long each hold ran after its page settled and how
|
|
90
|
+
much of that was past the beat; `freezePct` stays as the plain fact of
|
|
91
|
+
stillness, `@vosjs/cli` 0.46 and later). Budget: **≤20% dead**, no
|
|
92
|
+
single dead hold >1.5s. The fix is the step's `ms`: a hold is what it
|
|
93
|
+
takes to read what changed, so cut the named steps to their beat. Then,
|
|
94
|
+
where a flow allows it, keep something alive in frame (a playing
|
|
95
|
+
preview, a live canvas, a scroll), and hover things that respond with
|
|
96
|
+
motion. Never choose a flow to make a stillness number smaller.
|
|
97
|
+
15. **Full-frame bangs.** Instant UI re-layouts (filter clicks, page
|
|
98
|
+
navigations) read as jump cuts — violent when zoomed. Budget: **≤1 bang
|
|
99
|
+
per ~5s**, and let them happen WIDE (place zoom spans so the camera has
|
|
100
|
+
released before a click that re-layouts; zoom into the RESULT, not the
|
|
101
|
+
trigger). Census: `ffmpeg -vf "select='gt(scene,0.12)'"` on the final
|
|
102
|
+
render — each detection should be a transition you _chose_.
|
|
103
|
+
|
|
104
|
+
## Judge each still set against
|
|
105
|
+
|
|
106
|
+
- **Blur check** — nothing unintentionally blurred (hover scrims, mid-seek
|
|
107
|
+
frames). If a still looks soft, find out why before shipping.
|
|
108
|
+
- **Chrome check** — browser bar style matches the destination (mac for
|
|
109
|
+
marketing, minimal/none inside app-like framings); no double chrome.
|
|
110
|
+
- **Cursor check** — the dot rests on or beside the subject at every apex,
|
|
111
|
+
never floating in dead space; click effects land under it.
|
|
112
|
+
- **Text legibility** — body text in the footage readable at the render size;
|
|
113
|
+
if not, the zoom level or viewport was wrong.
|
|
114
|
+
- **First/last frame** — both must stand alone as posters (the first frame IS
|
|
115
|
+
the poster; the last frame is what loops into the first).
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
## Exit codes
|
|
4
|
+
|
|
5
|
+
| Code | Meaning | Fix |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| 0 | ok | |
|
|
8
|
+
| 1 | error | read stderr; `--json` puts the message in the done event |
|
|
9
|
+
| 2 | usage error, or `--strict` failure | see below |
|
|
10
|
+
| 3 | no browser | `npx playwright install chromium`, or set `VOS_BROWSER_PATH` to a Chrome/Chromium binary |
|
|
11
|
+
| 4 | the recorder met a sign-in instead of the page (`@vosjs/cli` 0.41 and later) | it needs a SESSION, not a new script: walk `sessions.md`, then pass `--storage-state <file>`. Nothing was recorded and nothing was cleared. `--allow-wall` only when the sign-in page IS the take |
|
|
12
|
+
|
|
13
|
+
## The wall (exit 4)
|
|
14
|
+
|
|
15
|
+
`vos record`, `create` and `--dry-run` check where the first navigation
|
|
16
|
+
landed before a frame is captured. Refused always: a 401 or 403, a redirect
|
|
17
|
+
to an identity provider or a sign-in path, a sign-in form rendered in place.
|
|
18
|
+
Refused under `--strict` and in a rehearsal, warned otherwise: a redirect
|
|
19
|
+
somewhere else with no sign-in in sight, which is what a site that shows
|
|
20
|
+
strangers its marketing page looks like.
|
|
21
|
+
|
|
22
|
+
- Do not touch `actions.json`. The script is fine; the browser is a
|
|
23
|
+
stranger. One `curl -sI <url>` before you script tells you the same thing
|
|
24
|
+
sooner: a 30x away from the page you asked for is a wall.
|
|
25
|
+
- A re-record that worked last week and exits 4 today is an EXPIRED session.
|
|
26
|
+
Mint it again; the old footage is still there, the refusal clears nothing.
|
|
27
|
+
- A digest that prints `WALL` is footage recorded past a wall
|
|
28
|
+
(`--allow-wall`, or a redirect without `--strict`): it may be the wrong
|
|
29
|
+
page. Re-record with a session before cutting it.
|
|
30
|
+
|
|
31
|
+
## `setup #N … the selector never appeared` (exit 2)
|
|
32
|
+
|
|
33
|
+
The off-camera sign-in did not finish, so the take did not start. A
|
|
34
|
+
selector in `setup` is checked the same way a step's is: rehearse with
|
|
35
|
+
`--dry-run`. `the environment variable NAME is not set` means the shell
|
|
36
|
+
that ran `vos record` did not export it; the value is never read from a
|
|
37
|
+
file. A setup that ran and then met the wall (exit 4) signed in with the
|
|
38
|
+
wrong credentials or into the wrong place.
|
|
39
|
+
|
|
40
|
+
## `EXPOSED in the frame`
|
|
41
|
+
|
|
42
|
+
Not an error: the take recorded. The recorder read the visible text after
|
|
43
|
+
each step and saw something shaped like an email address, a key or a card.
|
|
44
|
+
It reports the kind and the place, never the string. Fix it in the script
|
|
45
|
+
(`mask`, or a demo account) and re-record; a blur added afterwards in
|
|
46
|
+
`doc.json` still leaves the real value in `recording.webm`, which is what
|
|
47
|
+
`vos push` uploads. `MASK hid nothing: <selector>` (exit 2) means the
|
|
48
|
+
selector matched no element, so whatever it was for may be showing.
|
|
49
|
+
|
|
50
|
+
## Strict-mode failures (exit 2)
|
|
51
|
+
|
|
52
|
+
`vos record --strict` exits 2 when a selector was skipped or a navigation
|
|
53
|
+
never reached networkidle, with `skipped[]` in the `--json` done event.
|
|
54
|
+
|
|
55
|
+
- A skipped selector means the flow is broken: the element wasn't there, the
|
|
56
|
+
selector is unstable (nth-child chains), or the page needed more settle
|
|
57
|
+
time. Fix the actions.json — never ship a take that silently skipped steps.
|
|
58
|
+
- Networkidle timeouts on pages with long-polling/websockets: add explicit
|
|
59
|
+
`wait` steps after navigation instead of relying on networkidle.
|
|
60
|
+
- Always pass `--strict`. The lenient default exits 0 over broken flows,
|
|
61
|
+
which is how bad takes ship.
|
|
62
|
+
|
|
63
|
+
## Dead time
|
|
64
|
+
|
|
65
|
+
The record done event reports `dead` (`@vosjs/cli` 0.46 and later): a
|
|
66
|
+
still frame under a parked cursor past the beat it takes to read what
|
|
67
|
+
changed, per step, with how long the hold ran after the page settled.
|
|
68
|
+
`freezePct` beside it is the plain fact of stillness and is not a defect
|
|
69
|
+
on its own (a page being read is content). Budget: ≤20% dead, no single
|
|
70
|
+
dead hold >1.5s. The take-ready line names the steps.
|
|
71
|
+
|
|
72
|
+
1. Cut the named steps' `ms` to their beat: about 1 s after a page
|
|
73
|
+
changed, 0.6 s after a control did.
|
|
74
|
+
2. Where the flow allows, keep something alive in frame, and hover things
|
|
75
|
+
that respond with motion — those dwells become zooms too.
|
|
76
|
+
3. Trim dead heads/tails with `segments`; compress slow stretches with
|
|
77
|
+
`speed` spans.
|
|
78
|
+
|
|
79
|
+
Never choose a flow to make `freezePct` smaller.
|
|
80
|
+
|
|
81
|
+
## Browser and environment
|
|
82
|
+
|
|
83
|
+
- **MP4 output needs system Chrome** (`--format mp4`); Playwright's bundled
|
|
84
|
+
Chromium lacks the H.264 encoder. WebM works everywhere.
|
|
85
|
+
- **Headless WebGL**: if a render hangs at scene init on a CI box, the
|
|
86
|
+
browser may lack GPU/SwiftShader flags. Prefer system Chrome; file an
|
|
87
|
+
issue with the `--json` output if it persists.
|
|
88
|
+
- **Network**: the render page imports three/mediabunny from esm.sh —
|
|
89
|
+
rendering needs outbound network. A fully offline sandbox can record
|
|
90
|
+
nothing and render nothing; surface that limitation rather than shipping a
|
|
91
|
+
broken artifact.
|
|
92
|
+
- Recordings served to a render page must come from a server that supports
|
|
93
|
+
HTTP Range/206 (the CLI's own take server does) — video seeking hangs
|
|
94
|
+
forever without it.
|
|
95
|
+
|
|
96
|
+
## Performance expectations
|
|
97
|
+
|
|
98
|
+
- Recording is always real-time (it drives a real browser).
|
|
99
|
+
- Render ≈ 1.5× real-time at 1080p with ~5s fixed startup; 2K is ~2× the
|
|
100
|
+
per-frame cost; `--parallel N` pays off past ~30s of footage (ignored when
|
|
101
|
+
audio is muxed).
|
|
102
|
+
|
|
103
|
+
## Take directory anatomy
|
|
104
|
+
|
|
105
|
+
| Path | What | Keep? |
|
|
106
|
+
|---|---|---|
|
|
107
|
+
| `recording.webm` | the re-render source footage | KEEP |
|
|
108
|
+
| `doc.json` | every editable decision | KEEP — this is the document |
|
|
109
|
+
| `actions.json` | the recipe that recorded it | keep for re-records |
|
|
110
|
+
| `frames/` | encode intermediate (~1GB at 2K) | deletable |
|
|
111
|
+
| `meta.json` | capture geometry | keep |
|
|
112
|
+
|
|
113
|
+
## Determinism
|
|
114
|
+
|
|
115
|
+
Renders are deterministic: same take + same doc.json = same frames, on any
|
|
116
|
+
machine. If two renders differ, the doc changed (diff it) — not the weather.
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: vos-authoring
|
|
3
|
+
description: Author a vos animation program (VosConfigJson) from scratch or debug one — declarative configs compiling into deterministic Three.js + GSAP-dialect animations on the MIT vos engine (@vosjs/core), validated locally with compileVosConfig. Covers the schema, dialect rules, 2D overlay elements, shaders, and params/presets knobs. Use when asked to write a vos animation program, create a VosConfigJson, make a programmatic motion graphic or logo animation, or fix a config that fails to compile or lint.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Vos program authoring
|
|
8
|
+
|
|
9
|
+
You generate VosConfigJson files — declarative animation configs that compile
|
|
10
|
+
into Three.js + GSAP-dialect animations on the vos engine
|
|
11
|
+
([github.com/vosjs/vos](https://github.com/vosjs/vos), MIT). The preview is
|
|
12
|
+
the render: a config produces the same frames on every machine.
|
|
13
|
+
|
|
14
|
+
## Workflow
|
|
15
|
+
|
|
16
|
+
1. **Parse the prompt**: identify the animation concept, mood, colors, and
|
|
17
|
+
any referenced images.
|
|
18
|
+
2. **Design the approach**: choose camera preset, materials, effects, and
|
|
19
|
+
animation strategy (see the category guide below).
|
|
20
|
+
3. **Generate the config**: write a complete, valid VosConfigJson to
|
|
21
|
+
`<kebab-case-name>.json` wherever the project keeps configs.
|
|
22
|
+
4. **Validate locally** (see below), then render or preview it.
|
|
23
|
+
|
|
24
|
+
If the user provides an image, analyze its visual style (colors, mood,
|
|
25
|
+
composition, lighting) and translate those qualities into the scene.
|
|
26
|
+
|
|
27
|
+
## Validate and render locally
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm i @vosjs/core # compiler + lints
|
|
31
|
+
node -e "import('@vosjs/core/compiler').then(async m => { \
|
|
32
|
+
const cfg = JSON.parse(require('fs').readFileSync('my-config.json','utf8')); \
|
|
33
|
+
m.compileVosConfig(cfg); console.error('compiles clean') })"
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`compileVosConfig(config)` must not throw — it is the same compiler every
|
|
37
|
+
player and render server runs. For visual checks, the CLI
|
|
38
|
+
(`npm i -D @vosjs/cli`):
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
vos render my-config.json out.webm # deterministic video render
|
|
42
|
+
vos still my-config.json out.webp --time 2.5
|
|
43
|
+
vos preview my-config.json # local playback page
|
|
44
|
+
vos info my-config.json
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Share for preview (no account needed)
|
|
48
|
+
|
|
49
|
+
Local render is the default — nothing leaves the machine unless the user
|
|
50
|
+
asks for sharing or hosting. When they do want to see it hosted (playable
|
|
51
|
+
link, fine-tuning in the vos.so studio) and no API key is configured, use
|
|
52
|
+
the claimable push:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
vos push my-config.json --claimable --title "…"
|
|
56
|
+
# → claim: https://vos.so/claim/… expires: <72h from now>
|
|
57
|
+
|
|
58
|
+
# No CLI, or an older one? The same thing over plain HTTP:
|
|
59
|
+
curl -s -X POST https://vos.so/api/claim \
|
|
60
|
+
-H 'content-type: application/json' \
|
|
61
|
+
-d '{"title": "…", "config": <the VosConfigJson>}'
|
|
62
|
+
# → { "claimUrl": "https://vos.so/claim/…", "expiresAt": "…" }
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Hand `claimUrl` to the user and nowhere else — it is the only reference and
|
|
66
|
+
the only credential. It lasts 72 hours; unclaimed work is deleted after
|
|
67
|
+
that (re-push if it lapses). Claiming moves the vos into the user's
|
|
68
|
+
library. With a key configured (`VOS_API_KEY` or `vos login`), prefer
|
|
69
|
+
`vos push` — keyed pushes have no expiry. Programs only, config ≤200KB,
|
|
70
|
+
5 pushes per day per network.
|
|
71
|
+
|
|
72
|
+
## VosConfigJson structure
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"version": 2,
|
|
77
|
+
"duration": 8,
|
|
78
|
+
"scene": { "background": "#0a0a1a", "fog": { "type": "exp2", "color": 0, "density": 0.02 } },
|
|
79
|
+
"camera": { "preset": "perspective", "fov": 60, "position": [0, 2, 8] },
|
|
80
|
+
"postprocessing": [{ "type": "bloom", "strength": 0.8 }, { "type": "output" }],
|
|
81
|
+
"elements": [{ "type": "text", "id": "title", "content": "Hello", "position": "center", "font": { "size": 64, "color": "#fff" } }],
|
|
82
|
+
"setup": "async (ctx) => { ... return data }",
|
|
83
|
+
"createContent": "(ctx, setupData?) => { ... return { objects, refs, dispose } }",
|
|
84
|
+
"createTimeline": "(ctx, content, duration) => { ... return tl }"
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Complete field reference: `references/schema-reference.md`. Working
|
|
89
|
+
examples of every major type: `references/examples.md`.
|
|
90
|
+
|
|
91
|
+
## Critical rules
|
|
92
|
+
|
|
93
|
+
### DO
|
|
94
|
+
1. Functions must be valid **JavaScript strings** — NO TypeScript (no `as`,
|
|
95
|
+
no type annotations)
|
|
96
|
+
2. Access Three.js via `ctx.THREE`, GSAP-dialect via `ctx.gsap`, scene via
|
|
97
|
+
`ctx.scene`
|
|
98
|
+
3. `createContent` MUST return `{ objects: [...], refs: {...}, dispose: () => {...} }`
|
|
99
|
+
4. `createTimeline` MUST return `ctx.gsap.timeline({ paused: true })`
|
|
100
|
+
5. Always include `dispose()` that cleans up geometries, materials, textures
|
|
101
|
+
6. Use `ctx.resolution.drawingBufferWidth/drawingBufferHeight` for shader
|
|
102
|
+
`iResolution`
|
|
103
|
+
7. Set `mesh.frustumCulled = false` for fullscreen shader quads
|
|
104
|
+
8. Set `depthWrite: false, depthTest: false` on fullscreen ShaderMaterial
|
|
105
|
+
9. Always include `{ "type": "output" }` as the last entry when using
|
|
106
|
+
postprocessing
|
|
107
|
+
10. Add all scene objects to both `scene` and the returned `objects` array
|
|
108
|
+
11. Tween shader uniforms directly:
|
|
109
|
+
`tl.to(uniforms.iTime, { value: duration, duration, ease: 'none' })`
|
|
110
|
+
12. For per-frame logic (InstancedMesh updates, camera math), use `onUpdate`
|
|
111
|
+
callbacks on tweens
|
|
112
|
+
|
|
113
|
+
### DON'T
|
|
114
|
+
1. **NO `${}` template literals** in function strings — they evaluate at
|
|
115
|
+
compile time. Use string concatenation.
|
|
116
|
+
2. **NO escaped backticks** — write shaders as inline strings, or use
|
|
117
|
+
single-line concatenation
|
|
118
|
+
3. **NEVER `repeat: -1`** on individual tweens — looping is handled by the
|
|
119
|
+
player; this breaks duration tracking
|
|
120
|
+
4. **NO CSS-style** `style` objects or `textContent` on elements — elements
|
|
121
|
+
are WebGL planes
|
|
122
|
+
5. **NO external imports** — everything is on `ctx` (THREE, gsap, loaders,
|
|
123
|
+
utils)
|
|
124
|
+
6. **NO `requestAnimationFrame`** or `setTimeout` — use `onUpdate` callbacks
|
|
125
|
+
7. **NO `Date.now()` / `Math.random()`** in frame paths — determinism is the
|
|
126
|
+
contract; derive everything from the timeline's time and `ctx.data`
|
|
127
|
+
8. **NO `onFrame`** for new configs — put all per-frame logic in `onUpdate`
|
|
128
|
+
|
|
129
|
+
## Params and Looks (make it remixable)
|
|
130
|
+
|
|
131
|
+
Declare 2–4 knobs via `config.params` — editors render them as live controls,
|
|
132
|
+
and other agents can retune the program without touching code. Each param
|
|
133
|
+
documents a `ctx.data` key the program reads:
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
"params": [{ "key": "hue", "label": "Hue", "kind": "number",
|
|
137
|
+
"min": 0, "max": 1, "default": 0.6,
|
|
138
|
+
"hint": "Shifts every color around the wheel",
|
|
139
|
+
"unit": "°", "group": "Color", "order": 1 }]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
- kinds: `number` (min/max/step) | `color` | `select` (options) | `toggle`
|
|
143
|
+
- `hint`: ONE sentence on what visibly changes — write it
|
|
144
|
+
- the program reads it where it animates:
|
|
145
|
+
`const d = ctx.data || {}; if (typeof d.hue === 'number') u.uHue.value = d.hue`
|
|
146
|
+
- Name knobs by INTENT (`mood`, `drama`, `pace`), never by implementation
|
|
147
|
+
(`blurRadius`). Every knob must visibly change the output — a dead knob is
|
|
148
|
+
worse than no knob.
|
|
149
|
+
|
|
150
|
+
Ship Looks via `config.presets` — named param-value sets (2–3 when the
|
|
151
|
+
program has 4+ params):
|
|
152
|
+
|
|
153
|
+
```json
|
|
154
|
+
"presets": [{ "name": "Vivid", "values": { "hue": 0.9, "speed": 2 } },
|
|
155
|
+
{ "name": "Calm", "values": { "hue": 0.3, "speed": 0.6 } }]
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Values must reference declared param keys with matching types.
|
|
159
|
+
|
|
160
|
+
## Server-render caution
|
|
161
|
+
|
|
162
|
+
Configs pushed to vos.so get preview-rendered on a software-GL fleet: no
|
|
163
|
+
`THREE.DoubleSide` on transmission materials (hard hang), no `dispersion`
|
|
164
|
+
(too slow), and any fetched asset must be reachable and CORS-open. These are
|
|
165
|
+
fine for purely local renders on a real GPU.
|
|
166
|
+
|
|
167
|
+
## Design principles
|
|
168
|
+
|
|
169
|
+
- **Visual impact**: bloom for glow (even 0.3–0.5 elevates); emissive
|
|
170
|
+
materials + bloom = light emitters; exp2 fog (0.01–0.05) for depth; dark
|
|
171
|
+
backgrounds make colors pop; glass (transmission + HDR env) reads premium;
|
|
172
|
+
subtle camera drift adds life.
|
|
173
|
+
- **Animation quality**: 4–10s loops, 10–30s narratives; `ease: 'none'` for
|
|
174
|
+
constant motion; `power2.inOut`/`sine.inOut` for transitions; stagger for
|
|
175
|
+
sequential reveals; layer tweens at different start times.
|
|
176
|
+
- **Color**: 2–3 complementary colors max; warm key + cool rim; dark tones
|
|
177
|
+
(`0x0a0a1a`, `0x1a1a2e`) for backgrounds.
|
|
178
|
+
- **Performance**: `InstancedMesh` for 10+ identical objects; 5k–20k Points,
|
|
179
|
+
100–500 InstancedMesh; dispose everything.
|
|
180
|
+
|
|
181
|
+
## Category selection guide
|
|
182
|
+
|
|
183
|
+
| Concept | Camera | Technique |
|
|
184
|
+
|---------|--------|-----------|
|
|
185
|
+
| Shader art / fractals | `fullscreen` | ShaderMaterial + uniform tweens |
|
|
186
|
+
| 3D objects | `perspective` | Mesh + Material + lights |
|
|
187
|
+
| Isometric/flat | `orthographic` | InstancedMesh + computed positions |
|
|
188
|
+
| Text showcase | `perspective` or `fullscreen` | TextGeometry (3D) or elements (2D) |
|
|
189
|
+
| Glass/refraction | `perspective` | MeshPhysicalMaterial + HDR env map |
|
|
190
|
+
| Particles | `perspective` | Points + shader or InstancedMesh |
|
|
191
|
+
| Mixed media | `perspective` | 3D scene + elements array |
|