Terminal subjects use VHS tapes; sound, when the brief asks for it, follows the audio
reference. Templates are generalized from the proven docs/demo/statusline-mod/ and
docs/demo/hook-sniper.tape pipelines and were run end to end before shipping.
</objective>
Motion Render
Agent Workflow (MANDATORY)
- Read the locked inputs:
.motion/brief.md(surfaces, byte budgets, aspect, duration) and.motion/beats.md— if either is missing, go back tomotion-method. - Check tools:
ffmpeg -version,ffprobe -version,bun --version, a Chrome binary (orCHROME_BIN);vhs --version+ttydfor terminal films. Missing tool → say so, never fake a render. - Scaffold the scene:
motion-art-directiontemplates FIRST — composition-grid (layout), kinetic-type (word behaviours), cut-rhythm (beat map) — only afterlook.mdand the key frames exist. This skill then adds the mechanics: the shell, theseekclock, capture and encode (never hand-roll a capture). - Render
stills→ STOP at G1.draft→motion-critic→ STOP at G2.master→ verify the FILE. The harnessmotionscope denies draft/master until the owner typesMOTION-APPROVE— stop at each gate anyway, never lean on the deny. - Hand off: lead runs
fuse-ai-pilot:sniperon the scripts and the challenger on the "done" claim.
Overview
| Concern | Choice | Reference |
|---------|--------|-----------|
| Scene | DOM, 2D canvas or a WebGL layer with window.seek(t), DURATION, FPS, STILLS, ready; a gesture in progress at every moment (validateDensity); look from motion-art-direction | seek-contract.md |
| Reproducibility | Time-driven only, seeded PRNG, double rAF, proven vs reported flags; CSS 3D and lit WebGL paths | determinism.md |
| Stages | stills (default, one per beat) → draft → master, budget log, --jobs chunks | stages-budgets.md |
| Encoding | libx264 yuv420p BT.709 faststart; GIF palettegen/paletteuse; master motion blur from subframes + tmix | encoding.md |
| Delivery | GIF inline + mp4 link on GitHub | delivery-github.md |
| Terminal | VHS tape + scripted demo | vhs-terminal.md |
| Proof | ffprobe facts, exact frames, budgets, SSIM, spikes, color tags | verification.md |
| Sound | Licensed track ~120 BPM, beat grid measured from the song, start on a downbeat, SFX by measured peak, -14 LUFS, silent cut | audio.md |
Critical Rules
seek(t)is pure — samet, same pixels, any order. No CSS animation, no wall clock, no unseeded randomness, no state between frames.--stagedefaults tostills— omitting it cannot skip a gate; unknown values exit 2. Pass a literal stage: the harness counts--stage "$S"as master. Stop at G1 and G2.- Verify the file, not the frames — exact
DURATION x FPSframes, h264/yuv420p, budget, SSIM vs approved stills. "ffmpeg exited 0" is not proof. - No invented flags or APIs — every Chrome/ffmpeg option is either proven by the reference pipeline or labelled "reported, verify".
- Facts from files only — on-screen numbers from
.motion/facts.md, labelled example data; no client names, no real spend, real masked screenshots (motion-assets). - Fix in the scene, not with re-renders — a critic finding is a code change + a new draft, never a sequence of masters.
Architecture
.motion/
├── brief.md, beats.md, facts.md, assets.json ← motion-method / motion-assets
├── scene/index.html + lib.js, beats.js|rhythm.js, grid.js, kinetic.js, timeline.js
│ ← classic scripts in that order (each within `FUSE_SOLID_MAX_LINES`):
│ index-skeleton.md shell + timeline-seek.md clock
├── capture.js ← capture-cdp.md
├── render.js + encode.js ← render-cli.md
├── terminal.tape + terminal-demo.sh ← vhs-tape.md (terminal films)
├── stills/ still-1..N.png, contact.png (G1 artifact)
├── out/ draft.mp4 (G2 artifact), master.mp4, master.gif
├── review/ review pack (motion-review)
├── work/ frames + Chrome profiles (gitignore)
└── budget.json renders per stage + wall time
→ See render-cli.md for the complete CLI
Reference Guide
Concepts
| Topic | Reference | When to Consult | |-------|-----------|-----------------| | Seek contract | seek-contract.md | Writing/porting a scene; GSAP/WAAPI adapters; canvas camera | | Determinism | determinism.md | Drift, stutter, blank frames, cuts on frame boundaries | | Stages & budgets | stages-budgets.md | Choosing a stage, long films, render counts | | Encoding | encoding.md | mp4/GIF settings, color shift, banding, over budget | | GitHub delivery | delivery-github.md | README/docs embedding, size limits | | VHS terminal | vhs-terminal.md | Terminal/CLI films | | Verification | verification.md | After every master, before "done" | | Audio | audio.md | Brief asks for music/SFX |
Templates
| Template | When to Use |
|----------|-------------|
| capture-cdp.md | .motion/capture.js — zero-dep Chrome capture |
| render-cli.md | .motion/render.js + encode.js — stages, encoders, verification |
| timeline-seek.md | The scene split into classic scripts (lib.js → beats.js → blocks → timeline.js): springs ζ ≥ 0.85, springSum, stagger, validateDensity (0.75 s / 1.2 s / 1 per s), the recurring actor, a flat CSS 3D cube; or the dense 2D-canvas scene |
| interaction-kit.md | One continuous object driven by a cursor: dual-edge springs, click/drag with spring-back, swapBlur (≤ 0.1 s, ≤ 8 px), loop seam check |
| webgl-scene.md | A lit three.js beat: pinned local three r186, SwiftShader, twice-rendered sha256 proof or CSS 3D fallback |
| index-skeleton.md | .motion/scene/index.html mechanics — stage = VIEW, kill-switch, local fonts, hidden shots, actor, rule, CSS 3D cube, script order |
| vhs-tape.md | Terminal film: tape + scripted demo |
Best Practices
DO
- Render stills twice when touching the scene's clock: the contact sha256 must not change
- Put hard cuts on frame boundaries (
t = k / FPS) so a cut lands on the same frame at every rate and in every chunk - Use
--jobspast ~25 s; frames keep a global index - Run the
motion-reviewpack (spikes, phone view) on the draft and the master - Report renders per stage, wall time, sizes vs budgets, SSIM and spikes
DON'T
- Capture the preview loop or record the screen in real time
- Add motion blur to stills or the draft — master only,
--subframes(4 default, ≥ 12 if any move > ~2 % W/frame — encoding.md), cuts on frame boundaries - Ship a lit WebGL beat without the twice-rendered sha256 proof, or load three from a CDN at render time
- Build a crossfade or dissolve, or an opacity-only entrance — banned (
motion-craftanti-slop-video.md) - Write a scene file over the hook ceiling
FUSE_SOLID_MAX_LINES— split it into another classic script - Leave frame 0 blank or fading in —
seek(0)shows the opening statement at full value - Add a "reported" Chrome flag without the twice-rendered hash test
- Lower the SSIM threshold to make a master pass — investigate the change
- Commit
.motion/work/frames or Chrome profiles - Use Remotion as a dependency (license — mention it only as an alternative)
- Copy design-motion's UI micro-durations into film holds — films use
motion-craftholds