Clawd video build plan

Project: opus5.5_cartoon_bear · Plan date: 2026-09-24 · Status: plan only. No video is made yet.

This document explains how a text prompt and a storyboard become a 15-second MP4. No person draws, and no animation program or video editor is used. The model writes code. The code draws 360 still images. FFmpeg joins the images into a video. The model looks at the images and corrects the code. The main goal of this document is to explain each part of that process. The video is the test case.

1. The process in one page

Most people think "AI video" means a model that generates video pixels directly, like Sora or Veo. This project does not use that method. Here, a language model writes a program. The program is a set of drawing instructions: "paint a beige wall", "put an orange block at x = 1250", "move it up 40 pixels at second 2.1". A browser runs the program and draws the pictures. The model never generates pixels itself.

Storyboardyour text Codewritten by the model Chrome + p5.jsdraws frame 0…359 360 PNG filesone per 1/24 s FFmpeg → MP4+ WAV soundtrack Node synthwrites score.wav Model looksat stills, fixes code

The work has 5 parts:

  1. Write. The model reads the storyboard and writes about 6 source files: the scene, the character, the timeline, the renderer, and the sound.
  2. Draw. A copy of Chrome with no window ("headless") opens the scene. A Node script asks it for the picture at time 0.000 s, then at 0.042 s, then at 0.083 s, and so on. That gives 15 × 24 = 360 pictures.
  3. Compose sound. A second Node script calculates the soundtrack as a list of numbers and saves it as a WAV file.
  4. Encode. FFmpeg joins the 360 pictures and the WAV file into one MP4 file.
  5. Check. The model opens sample frames as images, finds errors, and changes the code. Then steps 2 to 4 run again. This loop is where most of the time goes.
Where the judgment is. The tools only do what the code tells them. All decisions come from the model: where Clawd stands, how a jump feels, when a sound plays, and which frame looks wrong. You make decisions at the checkpoints in section 10.

2. The tools: what each one is and how the build uses it

I checked each version below on your machine during preflight on 2026-09-24.

ToolWhat it isHow this build uses it
Claude Code (Opus 5.5)The agent. It writes files, runs commands, and reads images.Writes all the source code. Runs the render. Looks at the frames and fixes errors. It cannot hear audio. See section 11.
Node.js v22.22.0A program that runs JavaScript outside a browser.Runs render.mjs (controls Chrome, calls FFmpeg) and score.mjs (calculates the audio).
npm 9.9.4The package installer for Node.Installs 3 packages into clawd-prose/node_modules: p5, p5.brush, puppeteer-core.
p5.js 2.2A JavaScript drawing library. See section 3.Gives p5.brush a WebGL canvas to paint on.
p5.brush 2.2.3A p5.js add-on that imitates real brushes, pens and watercolor. It needs p5 ^2.2 (from its package file) and uses simplex-noise for the natural texture.Paints the paper, wall, desk, laptop, machine, vacuum, card and the Clawd pigment texture. It paints each item one time only.
Canvas 2D APIThe drawing surface built into every browser. It needs no install.Puts the painted items together for each frame. Moves, turns and stretches them. Draws all text.
Google Chrome (your installed copy)The browser. With no window, it is called "headless Chrome".Runs the scene code and draws each frame. It uses the local GPU through ANGLE and Direct3D 11. The preflight confirmed this.
puppeteer-core 24A Node library that controls Chrome from a script, like a robot hand on the browser.Opens the scene. For frames 0 to 359, it tells the page to draw one frame and saves a screenshot of the canvas as a PNG. The "-core" version uses your Chrome, so it does not download its own copy (about 150 MB).
Small web server (Node built-in http)About 10 lines of code that send the project files to Chrome.Chrome blocks some files opened from file:// paths, for example fonts and JSON. A local server at http://localhost:8765 removes this problem.
FFmpeg 7.1.1 and ffprobeA command-line program that reads, converts and writes video and audio.Joins the PNGs into H.264 video. Adds the WAV as AAC audio. Makes contact sheets and waveform pictures for checks. ffprobe measures the result. Path: ~/AppData/Local/Microsoft/WinGet/Links/ffmpeg.exe. It is not on the Bash PATH, so the scripts use the full path.
WAV formatThe simplest audio file: a 44-byte header and a list of numbers.score.mjs writes it with no library.
Reference repo aadil6971/clawd-videoThe code from the YouTube video in your README.Reference only, as you decided. I use it for the Clawd pixel grid and for its list of known bugs. I copy no code from it.
Python 3.13.5—Preflight only (search in the Claude Code binary). The build does not use it.

Not used: Pixabay (its API has no music or sound endpoint), Higgsfield (your README says "no higgs"), the hyperframes skill (you chose p5.js), any AI image or video generator, and any video editor.

3. What p5.js is, and what p5.brush adds

p5.js

p5.js is a free JavaScript library for "creative coding". It is the web version of Processing, a tool that artists and teachers have used since 2001. p5.js makes drawing in a browser easy. You do not manage the low-level canvas. You write short commands:

// runs one time at the start
function setup() {
  createCanvas(1920, 1080);
}
// runs again and again, about 60 times each second
function draw() {
  background('#f3e6cf');           // clear to paper colour
  fill('#D77757');                 // Clawd orange
  rect(900 + frameCount, 500, 200, 140);  // a block that moves right
}

Two ideas are important:

This build changes one thing in the normal p5 method. It does not use the automatic draw() loop to make the video, because that loop runs on the real clock. The renderer calls renderFrame(i) itself, one frame at a time. Section 4 gives the reason.

p5.brush

Plain p5 draws clean computer shapes: exact edges and flat colour. p5.brush makes the shapes look hand-painted. It simulates pigment that bleeds at the edges, collects in darker rings, and shows paper texture. It also has pen, pencil, charcoal and marker lines that wobble like a real hand. The main commands:

brush.load();                      // start p5.brush on a WEBGL canvas
brush.fill('#e8a86b', 140);        // watercolour pigment, strength 140 of 255
brush.fillBleed(0.2);              // how far the colour spreads at the edge
brush.fillTexture(0.5, 0.35);      // paper grain inside, darker ring at the border
brush.rect(200, 200, 900, 500);    // the painted shape
brush.set('pen', '#3a2a24', 1.2);  // ink pen for outlines
brush.wash('#D77757', 255);        // flat opaque colour, no texture
Smoke test frame: a watercolour rectangle with soft bleeding edges, an ink-outlined rectangle, and a flat orange block on beige paper
The preflight test frame, rendered on your machine in 1777 ms (this time includes the page load). Left: brush.fill with bleed and texture. Centre: brush.set('pen') outline. Right: brush.wash in the Clawd colour rgb(215,119,87).
Two limits of p5.brush that set the whole design.
  1. It is random. Each paint of the same shape gives a different bleed. If the build painted the wall again in each frame, the edges would move 24 times each second. Animators call this "boiling".
  2. It is slow. One large watercolor shape can take a large fraction of a second. For 360 frames with 30 shapes, that is too slow.
The solution: paint each item one time at the start, keep the result as an image, and move the image in each frame. A printed cartoon works the same way: the background is painted one time, and the characters move on top of it.

4. How a video is made from frames

A video is a list of pictures

The output is 24 frames each second. 15 s × 24 = 360 frames, numbered 0 to 359. Frame i shows the moment t = i / 24 seconds. One frame lasts 1/24 s = 41.7 ms. Frame 72 is t = 3.000 s, the start of beat 2.

Each frame is a function of time only

This is the most important rule in the code. The picture at time t is calculated from t alone. It never depends on the previous frame. Example: Clawd's jump is not "move 5 px higher than the last frame". It is "at time t, height = 80 × sin(π × (t − 2.10) / 0.35)".

Why this matters:

Random items, for example confetti, smoke puffs and paper grain, use a seeded random generator. p5.brush has its own seed command, brush.seed(n). I found it in the installed 2.2.3 code, so the watercolor also comes out the same in each render. A seed is a start number. The same seed always gives the same "random" sequence. A particle's position is calculated from its birth time, speed and gravity: y = y0 + v·age + ½·g·age². No state carries from frame to frame.

Why not record the screen?

A screen recorder captures what the browser shows on the real clock. If one frame takes 200 ms to draw, the recorder misses frames, and the motion stutters. Frame-by-frame capture has no such problem.

The capture loop

// render.mjs (outline)
start local server on port 8765
launch Chrome (headless, GPU on, window 1920×1080, deviceScaleFactor 1)
open http://localhost:8765/?render
wait until the page says "assets painted"          // the one-time p5.brush painting
for i = 0 … 359:
    page.evaluate(renderFrame(i))                  // draw frame i on the canvas
    screenshot the canvas → out/frames/f_0000.png … f_0359.png
close Chrome
run FFmpeg: PNGs → out/video.mp4
run score.mjs → out/score.wav
run FFmpeg: video.mp4 + score.wav → out/clawd-prose.mp4
run ffprobe: print frames, fps, size, duration

The FFmpeg commands

# 1. pictures to video
ffmpeg -framerate 24 -i out/frames/f_%04d.png \
  -c:v libx264 -preset slow -crf 16 -pix_fmt yuv420p \
  -vf "scale=out_color_matrix=bt709:out_range=tv" \
  -colorspace bt709 -color_primaries bt709 -color_trc bt709 \
  -movflags +faststart out/video.mp4

# 2. add the soundtrack; cut both to exactly 15 s
ffmpeg -i out/video.mp4 -i out/score.wav -c:v copy -c:a aac -b:a 192k -t 15 out/clawd-prose.mp4

# 3. measure the result
ffprobe -v error -count_frames -show_entries stream=codec_name,width,height,r_frame_rate,nb_read_frames,duration -of compact out/clawd-prose.mp4

What the options do, in plain words:

5. What goes into one frame

Two canvases

  1. The paint canvas (hidden, WebGL, p5 + p5.brush). At the start, it paints each item one time: paper, wall, desk, laptop, machine parts, vacuum, system card, buttons, avatar. After each item, the code copies the result into its own small 2D canvas. These copies are the sprites.
  2. The stage canvas (visible, 1920×1080, Canvas 2D). For each frame, it clears and draws the sprites and text in order, with the positions, angles and sizes for time t. This is fast: copying an image takes about a millisecond (estimate, not measured).

The layer stack, back to front

Each frame draws these layers in this order. A later layer covers an earlier one, like cels in a hand-drawn cartoon.

LayerContentChanges over time?
1. Paper and roomWarm watercolor wall, window light, desk top. Painted one time.No. Moves only when the camera shakes.
2. Back propsThe laptop, or the prose machine when the laptop has changed. The pile of prose paper.Yes: grows, shakes, changes shape.
3. Laptop screenThe chat: user bubble, Clawd's typed line, cursor.Yes: typing, deleting, sending.
4. ClawdThe pixel character. Its pose comes from pose(t).Yes: every frame.
5. Flying gagsBeam, seams, wedge, em dashes, QUIETLY, bullets, text ribbon.Yes.
6. ForegroundUser avatar, glasses, magnifier, system card, vacuum nozzle, stamp.Yes.
7. EffectsSiren colour wash, speed lines, impact stars, smoke puffs, sweat drop.Yes.
8. Paper finishPaper texture multiplied over the whole frame, soft vignette at the corners. This makes the flat pixel Clawd sit inside the watercolor world.No.
CameraOne shift and scale on the whole stack, for a shake on the beam drop and the button slam.Yes, at a few moments.

Example: frame 132 (t = 5.500 s)

This is the list of drawing steps for one real frame in beat 2. Every other frame uses the same code with a different t.

  1. Clear the stage. Apply the camera: the beam landed at 4.6 s, so the shake is almost finished. Offset = 2 px.
  2. Draw the room sprite at (0, 0).
  3. Draw the prose machine. It shakes: x offset = 6 × sin(2π × 11 × t) px. The chimney puffs 3 smoke sprites. Each puff grows and fades with its own age.
  4. Draw the text ribbon. It comes out of the machine along a wave path. At 5.5 s, about 60% of the sentence is out. The code places each letter on the path one by one, because the 2D canvas cannot draw text along a curve.
  5. Draw the LOAD-BEARING beam. It landed at 4.6 s and now rests across the pile.
  6. Draw the SEAMS stitch line. It started at 5.0 s. At 5.5 s it is about 80% drawn.
  7. Draw the WEDGE. The hammer hit at 5.3 s. The wedge is now between two lines of text. The two lines are pushed 12 px apart.
  8. Draw two em dashes. Each flies on an ellipse path like a boomerang and spins.
  9. Draw Clawd, typing at double speed: arms alternate each 2 frames, eyes are happy chevrons (^ ^), body bobs 4 px.
  10. Draw the paper finish over everything.

How text is drawn

Most of the jokes are text, so text needs care:

6. How Clawd is built and animated

The reference

Clawd is the small orange mascot in the Claude Code terminal. The terminal draws it with block characters:

 ▐▛███▜▌
▝▜█████▛▘
  ▘▘ ▝▝

Preflight found these facts in your installed ~/.local/bin/claude.exe:

From characters to a pixel grid

Each block character is one terminal cell. Each cell has 2 × 2 "quarter" squares that can be on or off. For example, ▛ has 3 quarters on and the lower-right quarter off. A terminal cell is about twice as tall as it is wide, so each quarter is a tall rectangle. When I decode the 3 lines above, the result is a grid 18 quarters wide and 6 quarters tall:

What the grid shows:

To verify before the build: the decode of the 3 lines is correct. But my search of your local binary did not find the head row ▐▛███▜▌ as one string. That row comes from the reference repo. Before phase 3, I will compare it with the logo that Claude Code shows in your terminal.

Size in the video: 1 quarter = 22 × 44 px. Clawd is then 264 px wide (body) and 220 px tall (with legs). That is about 20% of the frame height. The body is a fixed size, so it looks the same in all shots.

Drawing the pixel body in a watercolor world

A flat, sharp orange block looks strange in a soft painted room. The plan: fill the pixel shape with an orange p5.brush texture (painted one time, in Clawd's exact colour), keep the edges sharp, and add a soft painted shadow on the desk. The paper finish (layer 8) goes over Clawd too. Clawd stays a pixel character, and the texture joins it to the room.

How a pose works

The function pose(t) returns a few numbers for each moment: position x and y, squash, lean angle, arm angles, eye mode, eye offset. The drawing code turns these numbers into rectangles. The origin is at Clawd's feet, so squash keeps the feet on the desk.

function pose(t) {
  const p = { x: 1250, y: 760, sx: 1, sy: 1, lean: 0, armL: 0, armR: 0,
              eye: 'normal', eyeDx: -6, sweat: 0 };       // default: looks at laptop
  if (t >= 1.20 && t < 1.60) {                          // horrified freeze
    const k = easeOutBack(clamp01((t - 1.20) / 0.12));
    p.sy = 1 + 0.15 * k;  p.sx = 1 / Math.sqrt(p.sy);   // stretch up, keep volume
    p.eye = 'wide';  p.sweat = k;
  }
  // … one block like this for each action in the shot list
  return p;
}

The animation principles, as numbers

PrincipleWhat it meansHow the code does it
EasingReal objects speed up and slow down. They do not move at one constant speed.Functions that bend time: easeInOutCubic for smooth moves, easeOutBack for a small overshoot and settle.
Squash and stretchA soft body flattens when it lands and grows tall when it jumps.Scale y by sy and scale x by sx = 1/√sy, so the area stays about the same. Typical range: sy 0.8 to 1.2.
AnticipationA small move in the opposite direction before the main move.Before the CONCISE slam, Clawd pulls back and crouches (sy 0.85) for about 0.2 s, then hits.
Follow-throughParts continue to move after the body stops.The arms settle 2 to 3 frames after the body lands.
StagingOne clear action at a time. The viewer must know where to look.Only one gag moves fast at a time. The others move slowly or hold.
Eyes leadA character looks at a thing before it acts on it.Eye offset (±6 to 9 px) moves 2 to 4 frames before the body turns.
Smear and speed linesAt 24 fps, a very fast move looks like a jump.Stretch the shape along the direction of the move, and add speed lines. This is used for the vacuum suction.

Acting with no face

Clawd has no mouth and no hands. The storyboard asks for "horrified", "cracks his fingers", "straightens an imaginary bow tie" and "raises one finger". The plan:

Known bug from the reference project. Arm nubs that turn around a point inside the body disappear into the body. The pivot must be at the outer edge of the body, and the arm gets longer when it lifts.

7. The shot list: second by second

Your storyboard gives 5 beats and their times. Those times do not change. Inside each beat, I set the time of each action. Beat 2 had about 11 actions in 4 s (about 0.36 s each), so the small gags overlap there, as you decided.

Staging. The laptop is left of centre, and its screen faces the camera at a 3/4 angle, so you can read it. This is a normal cartoon cheat. Clawd sits right of the laptop and faces it. The user avatar floats at the upper left with its chat bubbles. The vacuum enters from the right, so it pulls Clawd out on the right side.

Beat 1: The innocent question (0.0–3.0 s, frames 0–71)

Time (s)FramesActionClawd
0.00–0.250–5Establish: room, laptop, Clawd. Music-box motif starts.Idle bob, one blink.
0.25–0.706–16User bubble pops in: "Explain this simply."Eyes go to the bubble.
0.70–1.2017–28Clawd types fast: "Change the parameter."Arms tap. Eyes normal.
1.20–1.6029–38Freeze. Music stops. Low sting.Stretch up, wide eyes, sweat drop.
1.60–1.9038–45Slow delete, letter by letter.One arm taps. Eyes squint.
1.90–2.2046–52Imaginary bow tie (ghost ink bow tie).Nubs meet at the lower centre, twitch.
2.20–2.4553–58Finger crack: stars and crack sound.Nubs push forward.
2.45–3.0059–71Brass switch labelled PROSE rises from the desk. Clawd pulls it. Sparks.Anticipation lean back, then pull.

Beat 2: The Mannered Prose Machine awakens (3.0–7.0 s, frames 72–167)

Time (s)FramesActionClawd
3.00–3.6072–86The laptop changes into a Victorian machine: brass pipes, gears, a chimney. The parts pop out with overshoot and smoke puffs.Hops back, delighted eyes.
3.60–4.0086–95Clawd feeds the paper strip "Change the parameter." into the hopper.Carries the strip on both nubs.
4.00–4.4096–105The machine shakes hard. Steam.Watches, bounces.
4.40–7.00106–167The ribbon unrolls: "It's not merely a parameter — it's a LOAD-BEARING dial worth turning..."At the keys. Typing speed grows: arm swap each 4 frames, then each 2 frames.
4.60110Overlap: the LOAD-BEARING beam drops. Thud. Camera shake.
5.00–5.60120–134Overlap: SEAMS stitch across the frame.
5.30127Overlap: a hammer drives the WEDGE between two sentences.
5.40–6.80130–163Overlap: 2 or 3 huge em dashes fly like boomerangs.
5.80–6.60139–158Overlap: the tiny word QUIETLY tiptoes along the bottom on small legs.
6.00–6.90144–165Overlap: one bullet splits into two, then three.

Beat 3: Concision makes everything worse (7.0–10.0 s, frames 168–239)

Time (s)FramesActionClawd
7.00–7.30168–175Paper pile covers the laptop. The machine is gone.Sits on the pile, proud.
7.30–7.80175–187User bubble: "Can you be concise?"Looks up, nods.
7.80–8.05187–193Red button labelled CONCISE. Clawd winds up.Anticipation crouch, sy 0.85.
8.05193Slam. Bang. Camera shake.Squash on impact.
8.10–8.50194–204The huge paragraph compresses into a small, very dense block with the same words.Proud eyes.
8.60206Stamp: "AND THAT MATTERS." Thunk.Stamp in both nubs.
8.90 / 9.30 / 9.70214 / 223 / 233Avatar puts on reading glasses, then stronger glasses, then a magnifying glass.Waits, happy.

Beat 4: Anthropic intervenes (10.0–13.0 s, frames 240–311)

Time (s)FramesActionClawd
10.00–10.50240–252A large system-message card comes down from the top with light rays: "PLEASE REMOVE ALL MANNERED PROSE." Choir chord.Freezes, looks up.
10.40–11.00250–264Sirens: red colour wash pulses 2 times each second.Panic: shake eyes, runs in place.
11.00–12.00264–288A large vacuum nozzle enters from the right. The words fly into it one after another and stretch as they go: LOAD-BEARING!, THE REAL UNLOCK!, SEAMS!, WEDGE!, QUIETLY!, then the em dashes, the flourishes, and the third bullet.Holds onto the desk.
12.00–12.30288–295Vacuum leaves. The laptop is normal again. One line: "Change the parameter."Drops back to the desk.
12.30–13.00295–311Silence: no music, no sound.Stares. No blink, then one slow blink.

Beat 5: Recovery… almost (13.0–15.0 s, frames 312–359)

Time (s)FramesActionClawd
13.00–13.15312–315Clawd presses SEND, slowly. The message flies up.One nub, droopy.
13.20317Avatar: green thumbs-up. Ding.
13.35–13.55320–325Astonished.Wide eyes.
13.55–13.70325–328Pleased.Happy chevrons.
13.70–13.75329–330Turns to camera, raises one "finger".Eyes centred, one nub up.
13.75–14.20330–340Types: "The real lesson isn't brevity. It's—"Typing, smug.
14.20–14.40341–345The vacuum comes back and pulls Clawd out on the right. Smear and speed lines.Stretches toward the nozzle.
14.40–14.92346–357The laptop stays. Final text: "Be direct."Gone.
14.92–15.00358–359Cut to black.
Reading time is the largest risk. Subtitle guides often use about 15 to 20 characters each second. This is a common rule of thumb, and I did not check it for this plan. At that rate, "Be direct." (10 characters) needs about 0.5 to 0.7 s. It gets 0.52 s. The line "The real lesson isn't brevity. It's—" has 36 characters and gets 0.45 s, so most viewers will catch only the first words. The joke still works, because the line is cut off. The beat 2 ribbon has about 70 characters and shows for about 2.6 s, which is less than the rule of thumb. The capitalised words carry the joke, and they each get their own object and moment. If the first draft reads too fast, the fix is to add 1 to 2 s to beat 5. That breaks the 15 s limit, so I will ask you first.

8. How the music and sound effects are made in code

Sound is a list of numbers

A speaker cone moves in and out. Digital audio records its position many times each second. This build uses 48,000 positions each second (48 kHz). Each position is a number from −1 to +1. 15 s × 48,000 = 720,000 numbers. One video frame = 48,000 / 24 = 2,000 samples exactly, so frame i starts at sample i × 2,000. That exact match makes sync simple.

Node has no browser audio engine, so score.mjs does all the maths itself. It needs about 150 lines and no library (estimate).

The building blocks

BlockWhat it isFormula or method
PitchA note is a frequency. A4 = 440 Hz. Each semitone up multiplies the frequency by 2^(1/12).hz = 440 × 2^((midi − 69) / 12). Middle C (midi 60) = 261.6 Hz.
OscillatorThe basic wave. Its shape sets the tone colour.Sine = pure, like a flute. Square = hollow, like a chiptune. Sawtooth = bright and buzzy, like brass. Triangle = soft.
Envelope (ADSR)How the loudness changes during one note: Attack, Decay, Sustain, Release.A pluck: attack 5 ms, fast decay. A pad: attack 300 ms, long release.
NoiseRandom numbers. It sounds like "shhh".The base of steam, wind, the vacuum, snare drums and whooshes.
FilterRemoves high or low frequencies.A one-pole low-pass: y += a × (x − y). It makes a saw wave sound like a tuba, and noise sound like wind.
MixMany sounds at the same time.Add the numbers. Then limit the peaks and scale the loudest point to −1 dBFS.
WAV fileThe container.A 44-byte header, then each sample as a 16-bit whole number.
const SR = 48000, N = SR * 15;
const mix = new Float32Array(N);                  // 720,000 samples of silence
const hz = (m) => 440 * 2 ** ((m - 69) / 12);

function note(t0, dur, midi, amp, wave = 'sine') {
  const s0 = Math.round(t0 * SR), n = Math.round(dur * SR);
  for (let i = 0; i < n; i++) {
    const t = i / SR, ph = (hz(midi) * t) % 1;
    const w = wave === 'saw' ? 2 * ph - 1 : Math.sin(2 * Math.PI * ph);
    const env = Math.min(1, t / 0.005) * Math.exp(-t * 6);   // 5 ms attack, decay
    mix[s0 + i] += amp * w * env;
  }
}
note(0.0, 0.4, 72, 0.3);   // C5 on the music box at t = 0.0 s

The musical grid

The tempo is 120 beats each minute. One beat = 0.5 s = 12 frames = 24,000 samples. The storyboard beats start at 0, 3, 7, 10 and 13 s. Those are musical beats 0, 6, 14, 20 and 26. Every scene change falls exactly on a beat, so the music and the pictures change together.

The score, section by section

Time (s)MusicSound effects
0.0–1.2"Innocent" music-box motif in C major: sine plus a bell overtone, notes C–E–G–E.Pop (bubble), soft key clicks.
1.2–3.0Music stops at 1.2 s. A low two-note sting.Backspace clicks, cloth rustle (filtered noise) for the bow tie, knuckle crack, ratchet and spark for the switch.
3.0–7.0A pompous Victorian march: "oom-pah" bass (low-pass saw, like a tuba) and harpsichord arpeggios (bright pluck). At 5.5 s the notes double in speed to match the faster typing.Gear clanks, steam hiss, machine rattle, beam thud, stitch zips, hammer tink, boomerang whooshes, tiptoe plinks for QUIETLY, "blip-blip" for the bullets.
7.0–10.0The march continues, quieter. At 8.1 s the same melody plays at double speed and one octave higher for 0.4 s: "same content, compressed".Ping (user message), slam, compressor zip (falling pitch), stamp thunk, 3 glasses clicks that rise in pitch.
10.0–12.3Music stops. A "heavenly" choir chord: stacked sine waves with slow vibrato.Siren: pitch sweeps 600 to 1200 Hz, 2 times each second. Vacuum roar: noise with a filter that opens as it gets louder. One "fwip" for each word, higher each time.
12.3–13.0Silence: all samples are zero.None.
13.0–14.4The music-box motif comes back, happy.Send whoosh, ding (two sine partials), key clicks, vacuum again.
14.4–14.92One soft resolved C note under "Be direct."None.
14.92–15.0Cut to silence with a 10 ms fade, to prevent a click.

Sync

The cue times live in one file, cues.json. The browser scene reads it, and score.mjs reads it. If the stamp moves from 8.60 s to 8.70 s, the picture and the thunk move together. Sound can start on any sample, but the picture changes only every 41.7 ms. So each hit sound starts on the first sample of its impact frame.

9. How the model checks its own work

The model can read images, but it cannot watch motion and it cannot hear. So each check turns the video into things it can read: still images and numbers.

CheckHowIt finds
Key stillsRender single frames with ?t= at about 12 key moments, and read them as images.Wrong layer order, hidden arms, text that is not readable, colours that do not look right.
Contact sheetffmpeg -i out/clawd-prose.mp4 -vf "select='not(mod(n,12))',scale=384:-1,tile=6x5" -frames:v 1 out/sheet.png. This gives 30 thumbnails, one each 0.5 s.The flow of the story, empty frames, missing props.
Motion checkA script records Clawd's pose numbers for all 360 frames and marks each large jump between two frames."Pops": a pose that changes in one frame by mistake.
Formatffprobe: frames = 360, 24/1 fps, 1920×1080, h264, yuv420p, duration 15.000 s, audio aac 15.000 s.Wrong length, wrong frame rate, audio longer than the video.
Audio shapeshowwavespic (waveform picture) and volumedetect (peak and mean level).Clipping, sounds at the wrong time, the silent gap missing at 12.3–13.0 s.
YouWatch and listen to the MP4.Timing that feels wrong, and every problem in the sound. The model cannot hear it.

Rendering time is not known yet. The preflight frame took 1777 ms, but that includes the page load and a small paint. I will measure the time for one frame in phase 1 and give you the total before the full render.

10. The build, step by step

File layout. All files go in a new subfolder, clawd-prose/:

clawd-prose/
  package.json       p5, p5.brush, puppeteer-core; scripts: render, stills, audio
  index.html         loads p5, p5.brush, then the scene files
  cues.json          every cue time, shared by picture and sound
  scene.js           painting (one time), layer stack, text, effects, renderFrame(i)
  clawd.js           the pixel grid, pose(t), eyes, arms
  render.mjs         server + Chrome + frames + FFmpeg + ffprobe
  score.mjs          synthesizer + score → out/score.wav
  README.md          how to run it
  out/               frames/, video.mp4, score.wav, clawd-prose.mp4, sheet.png
  1. Phase 1: pipeline proof. Make the folder. Run npm install. Write index.html, a renderFrame(i) that only prints the frame number, and render.mjs. Render all 360 frames and encode them.
    Checkpoint: ffprobe shows 360 frames, 24 fps, 15.000 s. I measure the time for one frame.
  2. Phase 2: paint the world. Write the p5.brush painting for the room, desk, laptop, machine parts, PROSE switch, CONCISE button, stamp, system card, vacuum, avatar, glasses and magnifier. Each one is painted one time with a fixed seed.
    Checkpoint: one "asset sheet" PNG with all items. I send it to you.
  3. Phase 3: build Clawd. Write the grid, the textured fill, eye modes and arm nubs. Compare the grid with the terminal logo.
    Checkpoint: a "pose sheet" PNG with 8 poses: neutral, horrified, delighted typing, proud, panic, stare, astonished, pulled by the vacuum. I send it to you.
  4. Phase 4: animate the 5 beats. Write cues.json and the timeline code, one beat at a time. After each beat, render and read 2 or 3 stills.
    Checkpoint: first silent draft MP4 and contact sheet.
  5. Phase 5: sound. Write score.mjs: instruments, sound effects, score, mix, WAV.
    Checkpoint: you listen to score.wav. I also check the waveform picture and the levels.
  6. Phase 6: full render. Render 360 frames, encode, add audio, measure with ffprobe.
  7. Phase 7: check and fix. Contact sheet, key stills, motion check, audio check. Fix the errors and render again. Expect several passes.
  8. Phase 8: deliver. The final MP4, the source, and a README with the 3 commands to run it. Delete out/frames/ (360 PNGs, about 0.3 MB to 3 MB each, so up to about 1 GB).

11. Unknown unknowns: what a newcomer does not know to ask

Each item below is a question that a person new to this process usually does not know to ask. Each answer says how this plan handles it.

About the process

About the pictures

About the video file

About Windows and the tools

12. Decisions made, and open risks

Your decisions (2026-09-24)

My defaults (change them if you want)

Open risks

RiskEffectWhat I will do
Text reads too fast in beat 5Viewers miss "Be direct."Check in the first draft. If needed, ask you to allow 1 to 2 s more.
Clawd grid decoded by handWrong eye or leg positionCompare with the terminal logo in phase 3.
p5.brush 2.x behaviourTransparency and speed not yet measuredMeasure in phase 2.
Render time unknownSlow check loopMeasure in phase 1. Use short test renders of 1 beat.
The model cannot hearSound problems are found lateYou listen to score.wav in phase 5, before the full render.