● motion Mackenzie Bowes

A video engine for coding agents

motion.

A film is a Markdown screenplay plus one code file per scene. The engine checks the code against the script and shows the agent its own frames, so Claude Code can write and render a whole film on its own.

Example output · Redline explainer · 1:07 · 1080p60

A product explainer for Redline Takeoff, written and rendered with motion in Claude Code. The score is mine, made in Ableton from the engine's stems and MIDI, and synced back to the picture.

01 · How a film is written

The script is the source of truth

Timing lives in one human-readable file. Scenes are plain code that read their cues by name, so moving a scene moves its cues, and a typo throws instead of drifting silently.

screenplay.mdtiming
---
title: Redline explainer
size: 1920x1080   fps: 60   bpm: 120
---

## 01 · cold-open   0:00 → 0:06.5
> It's late. The bid closes at 9.

| cue      | at          | what                      | sound     |
|----------|-------------|---------------------------|-----------|
| tubes-on | +0.9        | four clock tubes ignite   | ignite ×4 |
| night    | +2.5 → +5.1 | clock races 22:48 → 02:13 | ticks     |
scenes/cold-open.mjspicture
export default defineScene({
  setup({ cues, motion }) {
    // components + anime.js timelines at cue times
    return { bank: NixieBank("22:48") };
  },
  draw(g, t, s, { cues, W, H }) {
    // pure function of t: same t, same frame
    s.bank.draw(g, t, { glow: cues.p(t, "tubes-on", 0.4) });
  },
  sound({ cues }) {
    return [{ at: "tubes-on", sfx: "ignite" }];
  },
});

02 · The agent's loop

Write, check, look, render

Every step is a command the agent can run and read the result of. It never has to guess whether a scene looks right.

  1. 1

    Write

    Edit the screenplay and the scene files.

  2. 2

    Check

    mo check

    Gaps, overlaps, unknown or unused cues, missing sounds, unlicensed clips.

  3. 3

    Look

    mo stills

    PNGs and a contact sheet of any scene, so the agent can see its frames.

  4. 4

    Draft

    --draft

    A 540p30 render of one scene in seconds.

  5. 5

    Render

    mo render

    The full film at 1080p60 with mixed audio.

03 · Design decisions

Built for an agent to drive

Deterministic frames

Everything is a pure function of time. No clocks, no unseeded randomness, nothing carried between frames. Any frame can be rendered alone and checked.

Mistakes fail loudly

Unknown cue names throw. Cues the code never reads get flagged. Scenes must butt up exactly, so timing bugs show up in mo check, not in the final cut.

A visual feedback loop

Stills and contact sheets turn "does this look right?" into something the agent can answer itself. When its timestamps drifted, it added mo scout to fix them.

Known traps have tests

Rendering pitfalls the agent hit once, like canvas replay and a library transform bug, now have regression tests so they can't come back.

Humans stay in the loop

mo handoff exports six stems, a mix, every sound event as MIDI, and a cue sheet for a DAW. Finished audio drops back in with one line of front matter.

No browser in the pipeline

Frames are drawn natively with Skia and piped raw into the Mac's hardware encoder. No headless browser, no PNG sequences.

04 · Frames from the film

Contact sheet

Click a frame to jump the film there.

05 · Under the hood

Specs

Output
1080p60 H.264 + AAC; renders at 70–90 fps on one process
Drawing
skia-canvas, with Clay flexbox layout through a 146 KB WASM shim
Animation
anime.js, run headless: timelines never autoplay, the renderer seeks them
Encoding
Raw RGBA frames into Apple VideoToolbox
Audio
Synthesized sound design, Strudel patterns on the film's bar grid, bus mixing with reverb and kick-ducking
Footage
Clips listed in clips.md with source and licence, fetched by section, decoded frame-exactly
Runtime
Node, about 2,200 lines of engine and tests