How Claude makes a video
Claude Opus 5.5 reads text and images and writes text. So when people post "videos made with Opus 5.5", what Claude actually wrote is code: a web page that can draw any moment of the animation on request.
A browser then steps through time one frame at a time and saves each picture. ffmpeg stitches the pictures into an MP4. The animation on the right works exactly this way. Drag the slider: every frame comes from one function of t.
frame 000/360 · t=0.00s The whole "video": one function of time
// drawFrame(ctx, t): same t in, same pixels out.
// A recorder steps t by 1/60 and saves each frame.
const ease = (x) => 1 - Math.pow(1 - Math.min(Math.max(x, 0), 1), 3)
const span = (t, a, b) => ease((t - a) / (b - a))
function drawFrame(ctx, t) {
const W = 1280, H = 720
ctx.fillStyle = '#fff7ed'
ctx.fillRect(0, 0, W, H)
const out = span(t, 5.2, 5.9)
ctx.save()
ctx.translate(0, -out * H)
const r = span(t, 0, 0.8) * 230
ctx.fillStyle = '#f97316'
ctx.beginPath()
ctx.arc(W - 250, H / 2, r, 0, Math.PI * 2)
ctx.fill()
const words = ['CODE', 'IS THE', 'CAMERA']
ctx.font = '900 104px Inter, sans-serif'
words.forEach((word, i) => {
const k = span(t, 0.6 + i * 0.45, 1.1 + i * 0.45)
ctx.globalAlpha = k
ctx.fillStyle = i === 2 ? '#f97316' : '#000'
ctx.fillText(word, 90 - (1 - k) * 120, 230 + i * 120)
})
ctx.globalAlpha = 1
const bars = [0.35, 0.6, 0.45, 0.85, 0.7, 1]
bars.forEach((v, i) => {
const h = v * 300 * span(t, 1.9 + i * 0.12, 2.7 + i * 0.12)
if (h < 1) return
ctx.fillStyle = i === bars.length - 1 ? '#000' : '#fff'
ctx.fillRect(W - 420 + i * 55, H / 2 + 150 - h, 40, h)
ctx.strokeStyle = '#000'
ctx.lineWidth = 4
ctx.strokeRect(W - 420 + i * 55, H / 2 + 150 - h, 40, h)
})
const line = span(t, 3.1, 4.1)
ctx.fillStyle = '#000'
ctx.fillRect(90, 600, 560 * line, 10)
ctx.font = '700 30px Inter, sans-serif'
ctx.globalAlpha = span(t, 3.6, 4.2)
ctx.fillText('360 frames. 0 video models.', 90, 660)
ctx.restore()
}The workflow in five steps
- 1
Write the brief
Say what the viewer should understand, how long it runs, and where it will play. Duration is the one spec nearly everyone gives. Aspect ratio is the one most people forget, and it decides the whole layout.
- 2
Get a storyboard first
Ask for a shot list with timestamps before any code. Fixing a line in a storyboard costs one message. Fixing a finished scene costs a rewrite.
- 3
Claude writes the scene as code
Usually one HTML file with Canvas, SVG, Three.js or shaders. The rule that makes export work: every frame is a pure function of time, so render(t) always draws the same picture for the same t.
- 4
Review stills, not the live preview
Have Claude render a contact sheet, one frame per beat or per second, and look at it. In Claude Code it can read the PNGs itself and fix what it sees before you spend minutes on a full render.
- 5
Render frame by frame to MP4
A headless browser steps time one frame at a time, screenshots each frame and pipes it to ffmpeg. No dropped frames, any resolution, and the audio track is muxed in at the end instead of recorded.
Step 1 is where most videos are won or lost. The prompt builder walks you through it.
Where to make it: chat, Claude Code, or a skill
claude.ai chat
- 1.Paste a prompt and ask for a single HTML file
- 2.It plays in the chat as an artifact
- 3.Screen-record it, or download the HTML and render it with the script below
Screen recording drops frames and caps you at your screen resolution. No audio muxing, no self-review.
Claude Code
- 1.Empty folder, Opus 5.5, effort high or xhigh
- 2.Claude writes the scene, the render script and runs both
- 3.It reads its own contact sheets and fixes problems before the final render
Needs Node.js, Chrome and ffmpeg on your machine. Long films can take a big share of a weekly allowance.
A style skill
- 1.Drop a SKILL.md with your style rules into .claude/skills/
- 2.Say "make a 15s launch video in the swiss grid style"
- 3.Claude loads the style, asks for missing facts, then builds
Only as good as the rules in it. Start from ours on the motion graphics page.
Building with Claude? Get paid for it.
Forward Deployed Engineers ship AI agents inside real customer systems. Open roles at Anthropic, OpenAI, Cognizant and more.
Which code stack fits which video
You rarely need to choose. In the creator prompts we studied, only 38% named a library, and Claude picked a sensible one for the rest. Name one when you want a specific look. Share = how often each stack appeared in the 703 published results.
| Stack | Share | Best for | Render note |
|---|---|---|---|
| Canvas 2D | 38% | Motion graphics, kinetic type, particles, charts, explainers | The default Claude reaches for. Easy to make deterministic because you redraw the whole frame from t. |
| SVG + GSAP | 26% | Logo reveals, UI animation, line drawing, icon systems | GSAP timelines can be seeked: build the timeline paused and call timeline.seek(t) per frame. |
| Three.js / WebGL | 19% | 3D worlds, product turntables, flythroughs, games | Needs a GPU-backed headless browser for speed. Read pixels back after each render, not from a realtime loop. |
| GLSL shaders | 13% | Abstract loops, backgrounds, fluid and light effects | Already a function of a time uniform. Pass t in and it exports cleanly. |
| p5.js + p5.brush | - | Hand-painted cartoons, watercolour, storybook shorts | Slow without a GPU (seconds per frame). Plain p5 shapes look like 2000s Flash; paint everything with brushes. |
| Remotion / HyperFrames | - | Long promos, multi-scene films, React teams | Frameworks that already give you frame-based time and a renderer. Worth it past about a minute of video. |
Render to MP4, frame by frame
Screen recording drops frames and is stuck at your screen size. This script loads the animation in headless Chrome and swaps the clock out, so time moves exactly one frame at a time and the rest of the code doesn't notice. It works for requestAnimationFrame loops, GSAP and CSS animations without changing them.
Six rules that make it work
- Read time, never count framesPosition = f(t). A variable that increments every frame breaks the moment frames render out of order or in parallel.
- Seed every random numberMath.random() gives different noise on every render. Use a hash of the index or a seeded generator so frame 212 is always frame 212.
- No physics that integrates step by stepSimulations that add velocity each tick drift. Precompute them once, or use closed-form motion.
- Wait for fonts before frame 0await document.fonts.ready, or the first frames render in a fallback font.
- Let ffmpeg add the audioNever record sound from the browser. Mux the original track into the MP4 at the end, so sync is exact.
- Tag colour as BT.709Untagged HD video gets decoded with the wrong matrix on some players, shifting colours. Add the bt709 filter shown below.
// Render any HTML animation to MP4, frame by frame, with a virtual clock.
// npm i playwright-core (uses your installed Chrome; needs ffmpeg on PATH)
// node claude-video-render.mjs scene.html out.mp4 [seconds=6] [fps=60] [width=1920] [height=1080]
import { chromium } from 'playwright-core'
import { spawn } from 'node:child_process'
import { pathToFileURL } from 'node:url'
import { resolve } from 'node:path'
const [input, output = 'out.mp4', seconds = '6', fps = '60', width = '1920', height = '1080'] = process.argv.slice(2)
const frameCount = Math.round(Number(seconds) * Number(fps))
const frameMs = 1000 / Number(fps)
const virtualClock = () => {
let now = 0
let queue = []
const startDate = Date.now()
performance.now = () => now
Date.now = () => startDate + now
window.requestAnimationFrame = (cb) => queue.push(cb)
window.cancelAnimationFrame = () => {}
window.__advance = (ms) => {
now += ms
const callbacks = queue
queue = []
callbacks.forEach((cb) => cb(now))
document.getAnimations().forEach((a) => { a.pause(); a.currentTime = now })
}
}
const browser = await chromium.launch({ channel: 'chrome' })
const page = await browser.newPage({ viewport: { width: Number(width), height: Number(height) } })
await page.addInitScript(virtualClock)
const url = input.startsWith('http') ? input : pathToFileURL(resolve(input)).href
await page.goto(url, { waitUntil: 'networkidle' })
await page.evaluate(() => document.fonts.ready)
const ffmpeg = spawn('ffmpeg', ['-y', '-f', 'image2pipe', '-framerate', fps, '-i', '-',
'-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-crf', '18', '-movflags', '+faststart', output], { stdio: ['pipe', 'inherit', 'inherit'] })
for (let i = 0; i < frameCount; i++) {
await page.evaluate((ms) => window.__advance(ms), i === 0 ? 0 : frameMs)
ffmpeg.stdin.write(await page.screenshot({ type: 'png' }))
if (i % Number(fps) === 0) console.log(`frame ${i}/${frameCount}`)
}
ffmpeg.stdin.end()
await new Promise((done) => ffmpeg.on('close', done))
await browser.close()
console.log(`wrote ${output}`)
Tested on Linux with Chrome and ffmpeg 7: a 2 second, 30fps clip renders in about 6 seconds, and frame 45 lands on exactly t = 1.500.
ffmpeg one-liners you will need next
What the best Claude videos have in common
We read 496 full prompts from 616 creators, plus the source code and production notes of the most-starred Opus 5.5 video projects. These patterns kept coming up.
Short prompts work
Almost half of the 496 full prompts we analysed are under 200 characters. Claude picks the stack itself: only 38% name a library.
Ask for a storyboard before code
The best-produced pieces all start with a shot list on a beat grid. Edit single shots after, one change per message.
Count the reads, not the seconds
Claude knows what happens because it wrote the code. The viewer does not. List each thing the viewer must understand and give each one its own time slot.
Ban the template look
Particle bursts, lens flares, neon glow, RGB split and bouncy easing are what make AI motion look like AI motion. Name them as off-limits.
Let it watch its own work
Only 7% of prompts ask Claude to check frames, but nearly every high-end result did. Contact sheets catch overlapping text and dead time.
Bring the music and the facts
Generated music gets the most complaints. Supply a licensed track and its BPM. For explainers, check every number yourself.
Effort level shows on screen
Creators report that higher reasoning effort makes scenes more detailed and more extravagant. Use high or xhigh for a final piece.
Budget for rounds
A "one-shot" result usually took two or more rounds. Plan for a draft at half resolution, notes, then the final render.
How long it takes and what it costs
Numbers creators reported publicly. They are single data points, not benchmarks, but they show the range: a short clip is cheap, a polished music video is not.
| Video | Time | Cost |
|---|---|---|
| 15s launch spot from a one-line prompt | ~1 min | ~$2 API |
| Promo built with Remotion | ~10 min | not stated |
| 5 min explainer with TTS and subtitles | 26 min | "very low tokens" |
| 3 min history film, ~7,400 lines | ~1 h | 7% of a weekly quota |
| Hand-painted music video, 156s, two generations | not stated | 10% of a Max 5x week |
| Lyric music video at xhigh effort | 45 min first cut | ~$200 API |
| Full song as 4K60 three.js MV, render only | ~57 min on an M5 Pro | local |
Frequently asked questions
Can Claude generate video?
Not directly. Claude Opus 5.5 takes text and images in and writes text out, so it has no video model. What it does very well is write code that draws every frame (Canvas, SVG, Three.js, shaders). A headless browser renders that code frame by frame and ffmpeg turns the frames into an MP4.
How do I export an MP4 from a Claude animation?
Two ways. Quick: play the HTML in a browser and screen-record it. Proper: render it frame by frame. Our free script on this page loads the HTML in headless Chrome, replaces the clock so time advances exactly 1/60 s per frame, screenshots every frame and pipes them into ffmpeg. You need Node.js, Chrome and ffmpeg.
Do I need Claude Code, or does claude.ai work?
claude.ai works for previews: ask for a single HTML file and it plays in the chat. Claude Code is better for finished videos because it can run the renderer, add audio, read its own contact sheets and fix what it sees.
Is making videos with Opus 5.5 free?
The rendering is free and runs on your machine. The model time is not. Creators report anything from about $2 of API usage for a 15 second clip to around $200 for a full music video, or 5 to 10% of a weekly Pro or Max allowance.
What kind of video is Claude good at?
Motion graphics, product launch spots, explainers, kinetic typography, data stories, logo reveals, hand-drawn style cartoons, 3D scenes and game-like pieces. It is weak at photoreal footage of people and places. Creators who need that combine it with a video model.
Which aspect ratio and frame rate should I ask for?
Match the platform: 1920x1080 for YouTube and landing pages, 1080x1920 for Reels, Shorts and TikTok, 1080x1080 or 1080x1350 for feeds. 60fps for motion graphics and UI, 24fps for a hand-drawn or filmic look. Only 8% of creator prompts state the aspect ratio, which is why so many clips end up letterboxed.
Can Claude add music and voiceover?
Yes, through code. It can synthesize music with Web Audio or Python, and call a TTS service for narration. In practice a licensed track you choose sounds better. Give Claude its BPM so cuts land on the beat, and let ffmpeg mux it in.
Should I use Opus or Sonnet for video?
Opus 5.5 for the final piece. Single creator tests found Sonnet close on charts, cards and simple 3D at roughly a quarter to half the cost, and weaker on hand-drawn and ink styles. A common pattern is Opus for the storyboard and review, cheaper models for individual scenes.
Why does my exported video flicker or jump?
The animation keeps state between frames: a counter, Math.random(), CSS transitions, or physics that steps forward each tick. When frames render out of order or in parallel that state is wrong. Make every frame a pure function of time and seed all randomness.
How long does it take?
A 15 second motion graphic can be a single prompt and a few minutes. A polished one minute piece is usually several rounds over an hour or two. Rendering 4K60 of a full song took one creator about an hour on a fast laptop.

