Playbooks 02AI video16 min read

Animated Science Videos with Claude Code Every frame drawn in code, every move on a spoken word

This prompt doesn't say "make a beautiful video". It specifies the colours, the character, the camera, how narration lines up with the picture, the sound and the render, and has Claude Code build a coded animation system. Here it is taken apart step by step, with a small version of it running live on this page.

1920×1080resolution
30frames per second
18beats in 30 seconds
Bruce McCandless floating untethered in a white spacesuit above the blue Earth, 1984
Bruce McCandless, the first untethered spacewalk, 1984. The real astronaut; the one in the video is drawn from rounded rectangles.Image credit: NASA · Public domain · Wikimedia Commons
You end up with

A rendered 1080p explainer video in the flat, bright style of channels like Kurzgesagt, built from one HTML file whose every frame is a function of time.

You need
Comfortable with a terminal, Python and Node. Expect a few rounds of fixes, not one prompt.
Cost
Free with the local voice (kokoro-onnx). ElevenLabs costs per character once you pass its free quota.
Last checked

Live, on this page

What if the Sun vanished?

These 30 seconds are drawn the way the prompt says: one canvas and a function that builds each frame from the time alone. So you can jump anywhere, go back or pause, and always get the same picture.

0.00sstory clock 0.00s

Drag the timeline, or click a beat. Every frame is drawn from the time alone.

The beats

Camera zoom over time (log scale)

0.3x1x2.25x0s10s20s30s

A simplified version: Earth is a 2D disc rather than a WebGL sphere, and there is no audio or motion blur. The prompt builds the real thing.

The workflow

Three questions, two approvals, one render

Claude asks three questions, shows you the narration and the event timeline, and before the final render a contact sheet of every scene. The two stops are the cheapest places to change your mind.

  1. 01Three questionsTopic, length, ElevenLabs key or not
  2. 02Narration and timelineEach word tied to the beat it triggers
  3. ◆You approveScript and beats
  4. 03One HTML fileCanvas, WebGL, seek(t)
  5. 04Contact sheetOne still per beat
  6. ◆You approveBefore the long render
  7. 05Render and mixFrames, voice, sound, -14 LUFS
Stops for approval

1 · The look

Give colours as numbers, not adjectives

Instead of "a beautiful space", the prompt asks for a gradient from #081d2c to #102348, five nebula blobs at set opacities, and three star layers at depths .12, .35 and .6. That precision is what turns "an animation" into a style. Click any colour to copy it.

Space

Sun

Earth

Mascot and type

Real and drawn

The Sun in extreme ultraviolet, glowing orange with bright active regions
The Sun at 304 Å, NASA's Solar Dynamics Observatory.Image credit: NASA/SDO (AIA) · Public domain · Wikimedia Commons

The real Sun in extreme ultraviolet, from NASA's Solar Dynamics Observatory. The drawn one is deliberately simple: a three-colour gradient, four halo rings, nothing on the edge.

The Blue Marble: the whole Earth seen from Apollo 17, Africa and Antarctica visible
The Blue Marble, Apollo 17, 1972.Image credit: NASA/Apollo 17 crew · Public domain · Wikimedia Commons

The real Earth from Apollo 17. In the drawn one each land type is a flat colour, and the light is recomputed from the Sun's position every frame.

2 · Character and type

Text rises through a clip; it never just fades

All on-screen text is Fredoka 600 and 700, uppercase, 3 to 6 px letter spacing, in rounded pills. Each line rises 1.3 times its own height through a clip. See the difference side by side:

THIS LIGHT LEFT THE SUN8 MIN 20 SEC AGO
Rising through a clip
THIS LIGHT LEFT THE SUN8 MIN 20 SEC AGO
A plain fade
  • The character must be your own. The prompt explicitly forbids copying other channels' characters.
  • No full stops in on-screen text.
  • To finish: a soft vignette and 6% film grain, re-randomised 24 times a second.

3 · Story and camera

A narrative shape that works for any topic

The Sun script is only an example. What to keep is its shape: the event on its word, pull back for scale, dive in to "you", pull back out, consequences on a live counter, escalate, a joke, a callback and the title. The line below is the camera's zoom at each step.

2.25x1x2.3x0.3x0.3x0.26x1.25x1x1x
The Americas at night: city lights as gold dots on a dark globe
The Americas at night, NASA's Black Marble. The ~380 city lights in the prompt are a cartoon of this.Image credit: NASA Earth Observatory · Public domain · Wikimedia Commons

4 · Narration and picture

Every event on a word

The core rule: every sentence comes with a camera move or a change in the picture, and nothing stays still for long. That needs the exact time of each word, and there are two ways to get it.

The words, and what each one triggers
What0.00sif0.22sthe0.36sSun0.50sjust0.86svanished?1.08sSun collapsesFor2.20seight2.42s8:20 slams inminutes,2.74snothing.3.30sDotted circleThen4.30sthe4.50sdark4.64shits4.96sNight sweeps Earthyou.5.20sYOU pin drops

The gold line is time; words with an event under them fire something on screen.

ElevenLabseleven_v4stability 0.5similarity 0.75with-timestamps
Cost
Paid past the free quota, per character
Timing
Timestamps per character; group them into words
Most natural voice, needs an API key
kokoro-onnx + faster-whisperKokoro-82MONNX on CPUword_timestamps=True
Cost
Free, runs on your machine
Timing
Whisper listens back and returns word times
About 300 MB of model files, no key
  • 1About 165 words a minute.
  • 20.24 s between sentences.
  • 3A 0.36 s beat before the big reveal.
  • 4If the read runs long, cut the script; never stretch the voice to fit the picture.

5 · Architecture

Every frame, a function of time

The whole project is one HTML file and one function: window.seek(t) draws frame t. No timers, no CSS animations, so every frame is reproducible and any second can be rebuilt on its own. The player at the top of this page works exactly this way.

index.html
// Everything on screen comes from t alone
window.seek = (t) => {
  const st = storyTime(t)      // the story clock, which can run backwards
  const cam = camera(st)       // easeInOutCubic, zoom in log space
  drawSpace(t, cam)            // stars on the ambient clock
  drawSun(st)
  drawEarth(st, t)
  drawCaptions(t)
}

Two separate clocks

Stars, Earth's spin and the astronaut's bob run on an ambient clock that only goes forward. Story events run on a story clock. When the story rewinds 0.8 s, a single clock would snap the stars back too and the picture would stutter.

One clock: everything jumps back
Two clocks: only the story rewinds

A damped spring for every pop

Camera moves glide on easeInOutCubic, but every pop (a counter slamming in, a pin dropping) is a damped spring. The prompt asks for damping .42 to .5 and frequency 12 to 16. Try it:

8:20
Inside the prompt's range

6 · Render

From HTML to MP4

Playwright opens Edge without a window, draws each frame with seek(t) and screenshots it. For motion blur, two subframes are blended with a 0.6 shutter. ffmpeg puts the frames together as H.264.

  1. seek(t)900 frames for 30 seconds
  2. Playwright + EdgeHeadless, with the GPU
  3. Two subframesShutter 0.6 for motion blur
  4. libx264Master CRF 16, delivery CRF 20
  5. Mix-14 LUFS
The last two commands, as an example
ffmpeg -framerate 30 -i frames/%05d.png -i mix.wav -c:v libx264 -crf 16 -pix_fmt yuv420p -c:a aac master.mp4
ffmpeg -i master.mp4 -c:v libx264 -crf 20 -af loudnorm=I=-14:TP=-2.2 delivery.mp4

7 · Music, effects and mix

Give the sound as numbers too

A calm, royalty-free space music bed 7 dB under the voice, with its natural ending landing on the final title. One effect per event type, peaking on its own frame, 4 to 10 dB under the voice. Check every audio file's licence before use.

Voice
0 dB
Sound effects
-10…-4 dB
Music bed
-7 dB
Music under the title
-2 dB
relative to the voice

The effects, one per event type

  • collapse
  • impact
  • whoosh
  • tick
  • dive
  • pin tap
  • photon
  • play click
  • light hit
  • reveal
  • frost
  • rewind
A green and red aurora over Antarctica seen from the International Space Station
Aurora australis from the ISS, Expedition 52.Image credit: NASA/Jack Fischer · Public domain · Wikimedia Commons

The science in the script

How true is the sample script?

The prompt is about building, not science. But a science video has to get its numbers right, so each claim in the Sun script was checked against a source:

  1. Sunlight takes 8 min 20 s to reach usTrue1 AU ÷ c ≈ 499 s, so 8 min 19 s. source
  2. Earth keeps orbiting for those 8 minutes tooTrueChanges in gravity travel at the speed of light; the 2017 neutron-star merger GW170817 pinned the difference below one part in 10^15. source
  3. Then Earth flies off in a straight line at 30 km/sTrueMean orbital speed 29.78 km/s; with no central pull, it continues along the tangent. source
  4. Jupiter stays visible for 1 hour+TrueLight needs ~43 min to reach Jupiter and 33–54 min to come back to us: Jupiter stays lit 67–87 min after our sky goes dark. source
  5. Temperature falls from 15 to -70°CEstimatePopular estimates: about -18°C within a week, about -73°C within a year. The video squeezes a year into 1.4 seconds; label it. source
  6. City lights come on as the dark arrivesPoeticPoetic, not literal: the lights are always on at night, as NASA's Black Marble shows. source
Jupiter with its cream and rust bands and the Great Red Spot
Jupiter, Hubble, June 2019: still lit for 67–87 minutes after our sky goes dark.Image credit: NASA, ESA, and A. Simon (NASA Goddard) · Public domain · Wikimedia Commons
Sunlit clouds fading into night along Earth's day–night line, seen from orbit
The terminator from the ISS. The coral band in the prompt is this line, drawn.Image credit: ISS Crew, Earth Sciences and Image Analysis Lab, JSC, NASA · Public domain · Wikimedia Commons

What the prompt insists on

Where it usually breaks

  1. 1

    Earth's edge gets clipped

    In close-ups the globe canvas must be at least 2 × radius × zoom × 1.18 px, or the edges are cut off.

  2. 2

    White hooks on the Sun

    Anything on the Sun's edge reads as a white hook on video. Don't draw it at all.

  3. 3

    A stretched voice

    If the narration runs long, tighten the script. Never stretch the voice to fit the picture.

  4. 4

    Darkness that never arrives

    The darkness front must speed up near Earth, from about 47 to 700 px/s, so it crosses the planet in 0.7 s.

  5. 5

    A rewind that breaks everything

    Keep ambient and story time apart so a rewind doesn't wreck the stars and the spin.

  6. 6

    Rendering blind

    Look at a contact sheet of every beat before the final render. A full render is slow and mistakes surface late.

The full prompt

The prompt, ready to copy

It is in English, because Claude follows this level of precision best there and the colours and numbers must stay untouched. A few things are fixed against the version that circulated (listed at the end). Paste it into Claude Code; it starts with three questions.

prompt.xml
<prompt>  <inputs>    Ask me for:    - One topic as a "what if" question (default: "What if the Sun vanished?")    - The length (default: 30 seconds)    - Whether I have an ElevenLabs API key. If not, use a free local voice.  </inputs>  <direction>    A cinematic animated science explainer at 1920x1080, 30fps, drawn entirely in code: one 2D Canvas plus one WebGL Canvas for planets. Use original characters and artwork. Do not copy existing channels' characters, styles or proprietary assets.    <space>      Vertical gradient #081d2c to #102348.      Five huge parallax nebula blobs: teal rgb(24,150,160) at 20%, blue rgb(40,95,195) at 16%, coral rgb(220,95,110) at 10%, indigo rgb(60,70,175) at 15%.      Three star layers at parallax depths .12, .35, .6 that twinkle. Draw 40% of near stars as 4-point sparkles.    </space>    <sun>      Radius 300px, half off the left edge.      Radial gradient #fff7dc to #ffc857 to #ff8f4a, with an offset highlight.      Additive halo rings at 1.23, 1.48, 1.76 and 2.1 times the radius.      18 faint rotating ray wedges. 28 drifting glowing granules on its surface.      Never draw prominences on the Sun's edge: no white hooks, flares or loops attached to the edge.    </sun>    <earth>      Orthographic sphere in a fragment shader, 4x supersampled, from a 2048x1024 map painted from longitude/latitude polygons.      Land #5fc47a, forest #48ad6b, desert #f5c75f, rainforest #3a9d62, ice #f1f8ff, ocean gradient #4dc7eb to #1a66b8. White capsule clouds.      Tilt .26 rad, spin .55°/s. Recompute the light direction from Sun to Earth every frame.      Day/night: smoothstep(-.12, .24, N·L). Navy night side with ~380 warm city lights. Coral band on the terminator.      Cyan rim, ocean specular, atmosphere halo stronger on the lit side.    </earth>    <astronaut>      Small original mascot, ~180px tall, built from rounded rectangles. Suit #f3f7fb, orange #ff8f4a boots, visor gradient #1f5a73 to #0a1c2c.      Glowing cyan eyes that look at whatever matters. Blinking antenna light. Idle bob. Squash and stretch on every pose.    </astronaut>    <typography>      Fredoka 600/700 only. Uppercase captions, 3-6px letter spacing, in teal #8fd8e6, gold #ffd98a or white.      Rounded pills, background rgb(6,20,40) at 62%.      Every line rises 1.3 times its own height through a clip with ease-out-quint. Never fade a whole line in at once.    </typography>    <finish>      Vignette rgba(4,4,20,.45). 6% film grain, re-randomised 24 times a second.      No static infographic strips. No full stops in on-screen text. No AI-generated images or video. No copied characters. No music with vocals, no bird sounds.    </finish>  </direction>  <structure>    For the default 30-second topic, "What if the Sun vanished?":    0s     Tight Sun shot at 2.25x.    1s     On "vanished": the Sun swells 6% for 0.22s, collapses over 0.33s, white flash, shockwave ring.    1.6-3s Pull back to 1x: Earth still lit, the astronaut staring at the empty spot.    3.2s   On "eight": an 8:20 countdown slams in from 1.7x to 1x and ticks. LAST LIGHT rides the edge of the growing dark disc.    5.6s   Dive to 2.3x on Earth, light streaks flowing in.    7s     On "you": a YOU pin drops.    7.5s   On "left": a photon lands on the pin. THIS LIGHT LEFT THE SUN / 8 MIN 20 SEC AGO.    8.8s   On "play": the pin morphs into a play button. YOU PRESSED PLAY / 15 SEC AGO.    9.6-12.5s Pull back from 2.4x to a 0.3x orbit view. LAST LIGHT and GRAVITY ride the ring.    13.4s  On "nothing": a dotted circle at the empty centre.    15.8s  On "hits": darkness sweeps Earth in 0.7s, city lights pop on, the countdown turns gold at 0:00.    17.4s  On "black": the background fades almost to black.    17.9s  Jupiter appears: STILL SHINING FOR 1 HOUR+.    20.8s  Zoom out, dashed tangent line: 30 KM/S. Earth flies straight off.    22.8s  A thermometer counts from 15 to -70°C while frost grows from the poles. Label it as an estimate over about a year.    24.3s  A 0.8s rewind on a separate story clock.    25.2s  Sunglasses drop onto the astronaut.    27.7s  A photon tagged 8:20 OLD, then the title.    For any other topic, keep the shape: the event on its spoken word; pull back to show scale; dive in to make it personal ("you"); pull back out; consequences on a live counter; escalate; a funny mascot beat; a callback to an earlier visual; the title.  </structure>  <build>    <architecture>      One HTML file. Every frame is a pure function of time: window.seek(t) draws frame t. No timers, no CSS animations.    </architecture>    <camera>      Camera moves use easeInOutCubic. Zoom interpolates in log space. Every pop uses a damped spring, damping .42-.5, frequency 12-16.    </camera>    <voice>      With an ElevenLabs key: eleven_v4; a calm, warm, curious male voice, late 30s, crisp, light neutral European English; stability .5, similarity .75; use the with-timestamps endpoint. It returns character timings: group them into words yourself.      Without a key: kokoro-onnx locally, then faster-whisper with word_timestamps=True for word times.      About 165 words per minute, 0.24s between sentences, a 0.36s beat before the major reveal. Every animation event fires on its spoken word. Never pad the voice to fit the visuals; tighten the read instead.    </voice>    <darkness>      Pixels within the darkness front's radius from the Sun turn to night with a 30px soft edge. Start at ~47px/s, ramp to 700px/s near Earth, tuned to cross Earth in 0.7s.    </darkness>    <clocks>      Two clocks: ambient time for twinkle, spin and bob; story time for narrative events. A rewind must not corrupt ambient motion.    </clocks>    <rendering>      Playwright with Microsoft Edge (channel "msedge"), headless. Headless browsers disable the GPU by default: launch with GPU flags, then read the WebGL renderer string, and tell me if it is software (SwiftShader) rather than pretend it is accelerated.      Seek every frame at 30fps. Blend two subframes with shutter 0.6 for motion blur.      Encode with libx264: master CRF 16, delivery CRF 20.      Show a contact sheet of every beat before the final render.    </rendering>    <sound>      A calm, royalty-free space music bed 7dB under the voice, its natural ending timed to the final title, swelling to 5dB under the voice there.      One royalty-free effect per event type: collapse, impact, pull-back whoosh, countdown tick, dive, pin tap, photon land, play click, light hit, reveal, frost, rewind. Peak on its frame, 4-10dB under the voice.      Master with gain and a limiter to -14 LUFS integrated, true peak -2.2dB. Verify every asset's licence before use.    </sound>  </build>  <gotchas>    Make the WebGL globe canvas at least 2 x radius x zoom x 1.18 pixels so close-ups do not clip.    Never draw prominences on the Sun's edge; they read as white hooks.    Never pad the voice to fit the visuals; tighten the read.    Speed the darkness front up near Earth or it takes too long to cross.    Keep ambient time separate from story time so a rewind stays consistent.    ElevenLabs timestamps are per character; build word times from them.    Check the WebGL renderer string in headless mode; software rendering is slow and may look different.    Verify dependencies, fonts, GPU access, browser, audio and encoding tools first. Report anything unavailable instead of pretending it worked.  </gotchas>  <start>    Ask me for the topic, the length and whether I have an ElevenLabs key.    Write the narration with the beat each word triggers. Show me the narration and event timeline for approval.    Build the HTML animation and the render pipeline. Show a contact sheet of every major beat.    Wait for my approval, then render and give me the output files.  </start></prompt>

Checklist

Your first run

Ticks stay in this browser.

0 of 12 done

Method

Checks, corrections and sources

Changelog

  1. First edition. Model names, render flags, loudness targets and the science in the sample script checked against primary sources.
  • In the version that circulated, window.seek(t) had been turned into a link ("http://window.seek(t)"). It is a JavaScript function.
  • ElevenLabs timings are per character, not per word; the prompt now says so.
  • Headless Chrome and Edge start with the GPU off; the prompt now asks for the renderer to be checked and reported.
  • -14 LUFS is Spotify's stated target; for YouTube it is a measured figure, not a published one.
  • The drop from 15 to -70°C is an estimate over about a year, not seconds; the prompt now asks for it to be labelled on screen.
  • The original said "no music" and also asked for a music bed. The version here forbids music with vocals.
Animated Science Videos with Claude Code | Farjad