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.
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 to motion-method.
- Check tools:
ffmpeg -version, ffprobe -version, bun --version, a Chrome
binary (or CHROME_BIN); vhs --version + ttyd for terminal films. Missing tool →
say so, never fake a render.
- Scaffold the scene:
motion-art-direction templates FIRST —
composition-grid (layout), kinetic-type (word behaviours), cut-rhythm (beat map) —
only after look.md and the key frames exist. This skill then adds the mechanics:
the shell, the seek clock, capture and encode (never hand-roll a capture).
- Render
stills → STOP at G1. draft → motion-critic → STOP at G2.
master → verify the FILE. The harness motion scope denies draft/master until
the owner types MOTION-APPROVE — stop at each gate anyway, never lean on the deny.
- Hand off: lead runs
fuse-ai-pilot:sniper on 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 — same t, same pixels, any order. No CSS animation, no wall
clock, no unseeded randomness, no state between frames.
--stage defaults to stills — 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 FPS frames, 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
--jobs past ~25 s; frames keep a global index
- Run the
motion-review pack (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-craft anti-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-craft holds