cd /news/ai-tools/how-i-make-motion-videos-with-claude… · home › topics › ai-tools › article
[ARTICLE · art-157864] src=dev.to ↗ pub= topic=ai-tools verified=true sentiment=↑ positive

How I make motion videos with Claude Opus 5.5 (the full setup, prompts and numbers)

A developer built a motion-design pipeline in which Claude Opus 5.5 writes a single self-contained HTML file where every frame is a pure function of time, Playwright screenshots each frame, and ffmpeg stitches the PNGs into an MP4. The approach produced a 3D product film for Slabwatch, including a modelled electricity meter, lighting, depth of field and a synthesised soundtrack, rendered on a laptop RTX 2050 at roughly 0.3 seconds per frame. The developer's render contract bans Math.random, Date.now, CSS transitions and requestAnimationFrame in render mode, and requires seeded noise and a window.renderAt(t) entry point.

by read33 min views1 publishedOct 26, 2026

TL;DR. Opus 5.5 never touches a pixel. It writes one HTML file where every frame is a pure function of time, Playwright screenshots each frame, ffmpeg stitches them. Swap every adjective in your prompt for a number, ban the defaults by name, review stills before the full render. My 3D Slabwatch film rendered on a laptop RTX 2050 at about 0.3 s a frame.

I'm not a motion designer. I've never opened After Effects. Last week I still needed a product film for Slabwatch, so I set up a motion design agent in Claude Code on Opus 5.5 and made one.

It went through a flat 2D version that I honestly didn't like, then a full 3D rebuild with a modelled electricity meter, real lighting, depth of field, a mirror floor and a synthesised soundtrack, all rendered on my laptop's RTX 2050.

Every "one prompt" motion video on your feed skips the middle part. The second prompt, the review, the "frame 84 is late" note. This article is that middle part. The exact setup, the render rules, the prompt template I use now, the numbers that make motion look expensive, and the checks that keep it sharp. Then, because my way isn't the only way, every other route I found: Blender, video models, Remotion, Manim and the rest, and when each one beats mine.

Opus 5.5 outputs text. It never touches a pixel. Every motion clip you've seen from it is a program, almost always one HTML file, where every frame is a pure function of time. You give it a time t, it draws exactly that frame.

Then a headless browser (Playwright) opens the page, freezes it at frame 0, 1, 2 and so on, screenshots each one, and ffmpeg stitches the PNGs into an MP4.

That sounds boring but it's the whole trick, and it gives you three things:

For that to work the page has to follow a few rules. I call it the render contract and it gets pasted under every motion prompt. Each line exists because something breaks without it:

RENDER CONTRACT (paste under any motion prompt)
- One self-contained HTML file. Only the libraries I name, pinned to an exact version.
- No network requests except fonts and those pinned libraries.
- Every frame is a pure function of t (seconds). Expose window.renderAt(t).
- No Date.now, no setTimeout / setInterval, no CSS transitions,
  no requestAnimationFrame in render mode.
- Seeded noise only (mulberry32). Never Math.random.
- No state carried from one frame to the next.
- Wait for document.fonts.ready before frame 0.
- Loops: render frames 0 to N-1. Frame N-1 must flow into frame 0.
- PNG frames into the final encode. Never JPEG.

Why each line matters, quickly:

Math.random makes shapes jitter between frames. One thing Movez points out that surprised me: Opus defaults to this plain "one HTML file + Playwright + ffmpeg" route even when Remotion or HyperFrames are installed. If you want a framework, say it in the prompt.

My route is plain HTML, plus three.js when it's 3D. It's not the only one. Section 14 has the full table of every route (Remotion, HyperFrames, Blender, Manim, Lottie, Rive, video models and more), with licenses and when to pick each.

And where code loses: photoreal scenes, complex characters, organic movement. seeco makes this point well. Code wins on product films, UI motion, data stories, typography and launch explainers, which is basically everything a builder needs. For the rest, section 14 covers Blender and the video models, and how to mix them with code.

No Claude Code? Start here. Florence's article has a no-install path. Open the Claude app, pick Opus 5.5, ask for the animation as one HTML file, and it plays in the preview panel. Then screen-record it (Cmd+Shift+5 on a Mac, the Snipping Tool's video mode on Windows). While you iterate, ask for a frame counter on screen and a space bar . It's not frame-exact, a screen recording can drop frames, but it's fine for a first post.

For the real thing: I run everything in Claude Code on Linux, but this works the same on a Mac. You need Node, ffmpeg and a headless Chromium.

mkdir motion-studio && cd motion-studio
npm init -y
npm i -D playwright
npx playwright install chromium
claude   # then /model and pick Opus 5.5

Then a renderer. Mine is scripts/render.mjs, Claude wrote it, and the core is about this big:

// node render.mjs anim.html 6 30   (file, seconds, fps)
import { chromium } from 'playwright';
import { pathToFileURL } from 'node:url';
import { mkdirSync } from 'node:fs';

const [file = 'anim.html', secs = '6', fps = '30'] = process.argv.slice(2);
const FPS = Number(fps), N = Math.round(Number(secs) * FPS); // frames 0..N-1
mkdirSync('out/frames', { recursive: true });

const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1920, height: 1080 } });
await page.goto(pathToFileURL(file).href, { waitUntil: 'networkidle' });
await page.evaluate(() => document.fonts.ready);

for (let i = 0; i < N; i++) {
  await page.evaluate((t) => {
    if (window.renderAt) window.renderAt(t);
    document.getAnimations().forEach((a) => { a.(); a.currentTime = t * 1000; });
  }, i / FPS);
  await page.screenshot({ path: `out/frames/${String(i).padStart(4, '0')}.png` });
}
await browser.close();

Then frames to video:

ffmpeg -framerate 30 -i out/frames/%04d.png -c:v libx264 -profile:v high \
  -pix_fmt yuv420p -crf 18 -movflags +faststart out/anim.mp4

Every frame is exact because every PNG is the page frozen at one precise t, no matter how slow your machine is. A screen recording drops frames, this doesn't.

Next to the renderer I keep two more scripts:

scripts/qa.sh: makes a contact sheet, a frame strip, a phone-size test and a loop check from any MP4 (the ffmpeg lines are in section 10)scripts/beats.py: librosa beat detection, so cuts can land on the music. In the smoke test it found 120 BPM on a 120 BPM click track, so it at least knows what a beat is And a CLAUDE.md in the studio folder that holds the render contract, the ban list and the QA rules. Every time something goes wrong, the fix goes in there as a rule, so the next piece doesn't hit it again. Honestly that file ends up mattering more than any single prompt.

Each piece gets its own folder: pieces/<name>/ with the brief, style guide, shot list, assets, src, reviews and out.

From then on you barely type commands. You tell Claude Code "render this, open frames 0, 30 and 60, tell me what you see before you change anything" and it runs the loop itself.

Movez and seeco both say this and it's the rule I'd tattoo on the agent: never let the model invent your product screens or numbers. A fake UI looks fine for about two seconds, until someone opens the real app and nothing matches.

What I did for Slabwatch: the agent didn't redraw my UI at all. It loaded the site's actual HTML and CSS in an iframe inside the film, with live numbers. Same fonts, same layout, same everything, because it IS the page.

If you're doing a brand or launch reel, the instructions that work (mostly from Movez and seeco):

ASSETS
- Here is the URL: <your site>. Use the actual product screens, logo and assets.
- Capture them with Playwright into ./assets and list them before you start.
- Never redraw the UI from imagination.
- If a real asset is missing, stop and ask me. Never draw a convincing fake.
- Use my real numbers only. Never invent metrics.

Same for references. Naming a style beats describing it. If you give it a frame from a film you like, say what to take (palette, type, grain, shot length) and what NOT to take (the subject). Movez's line for this is basically "take the grammar, never the content".

This is the single most useful idea in all of this, and it's from Florence.

Florence ran the same 3D product shot twice. The vague prompt was three adjectives ("cool", "premium", "modern"). It came back as navy space, a glowing halo ring, bloom, particles, a spinning product and a tagline nobody asked for. That's the default look, and adjectives are where the default lives.

So you swap every adjective for a number:

You'd say Say this instead
"premium" materials: roughness 0.38, clearcoat 0.35; ease-out-quint, no overshoot on text
"cool lighting" key, fill and rim lights, each with a hex color and an intensity
"modern camera" fov 30, never parked, one push-in on ease-out-expo at 2.0 s
"snappy" durations in frames and a spring damping ratio
"make it pop" scale 1.0 to 1.08 to 1.0 over 6 frames, peak on the beat
"it drags" cut each hold by one beat
"too busy" one hero move, everything else 150 ms later at 60% of the travel

Here's the template I use now. It's Florence's structure with a couple of lines from Movez and seeco. Fill every line before you hit enter:

GOAL:    one sentence. What the viewer feels or does after watching.
OUTPUT:  engine + size. e.g. "A three.js scene in one HTML file, 1920x1080"
TIME:    seconds x fps = frames. Loop: yes / no.
LOOK:    hex palette, ONE accent color, typeface / weight / size / tracking.
MOTION:  cubic-beziers for enter / move / exit, springs as damping ratios,
         durations in frames.
SHOTS:   frame ranges, one hero move per shot, and the handoff
         (cut / match cut / mask wipe). Default to the cut.
MODEL / CAMERA (3D only): what the object is, fov, the one camera move.
IDENTITY LOCK: what may never change (logo proportions, mascot shape).
ASSETS:  real logo, real UI screens, real numbers. If something is
         missing, stop and ask.
BANNED:  see section 06.
QA:      stills at frames [..], a contact sheet, my OK, then the full render.

Write GOAL first. If you can't say in one sentence what the viewer should feel, Opus can't animate it, and no easing curve saves it. Then TIME, because every other number hangs off the frame count.

The QA line matters too. It makes Opus show you stills BEFORE it renders the whole clip, which is where all the real work happens (section 10).

For a short product film, seeco's default story shape is a good starting SHOTS line:

0-2 s    HOOK      one thing that stops the scroll
2-5 s    PROBLEM   shown on screen, not told
5-9 s    REVEAL    the product, through a real interaction
9-13 s   PROOF     one screen or one number
13-16 s  CLOSE     logo + a CTA people can actually read

Every beat needs an entry state, an exit state and a reason. No reason, cut it.

For Slabwatch my hook was literally the problem: an LCD ticking past 200 units and a Rs 32,000 bill. If you're in Pakistan you already know why that's scary, the 200-unit slab is where your bill jumps.

Motion designers talk in curves and frames, so give Opus curves and frames. Screenshot this section, it's the one I go back to most. Almost all of it is from Florence's cheat sheet.

Easing

Name cubic-bezier Use it for
ease-out-expo 0.16, 1, 0.3, 1 text and logo reveals
ease-out-quint 0.22, 1, 0.36, 1 premium entrances
ease-in-out-quart 0.76, 0, 0.24, 1 moves and camera pushes
ease-in-expo 0.7, 0, 0.84, 0 fast exits
ease-out-back 0.34, 1.56, 0.64, 1 a playful pop with overshoot

Rules: enter with ease-out, exit with ease-in, and make exits 40 to 50% shorter than entrances (400 ms in, 200 ms out). Linear only for rotation, progress bars and the phase of a loop. Linear easing on a move is the fastest way to make a render look like a render.

Springs

Every library names its spring knobs differently. One number translates between all of them, the damping ratio:

// damping ratio from any library's numbers. 1 = no bounce
const dampingRatio = (stiffness, damping, mass = 1) =>
  damping / (2 * Math.sqrt(stiffness * mass));

Remotion's default spring works out to 0.5, react-spring's default to 1.0. Position and size may bounce, opacity and color NEVER do. Tiny overshoot on UI, none on type.

Ask for springs as a closed-form function of t (no simulation), so frame 84 can render on its own without computing frames 0 to 83. That keeps the render contract happy. Both Movez and Florence have the code for this, you can just tell Opus "write the spring as a pure function of t".

seeco adds a good rule on top: one spring personality per kind of object. Small UI snaps, panels settle, the camera glides almost invisibly, headlines land hard and hold, mascots bounce on purpose. If everything overshoots, nothing feels precise.

Durations and stagger

UI feedback        100-200 ms
UI moves           200-500 ms
hero reveals       600-900 ms
letters            1 frame apart
words              60-120 ms apart
list items         40-80 ms apart
next element starts when the previous one is 30-50% done

For video, say it in frames. At 30 fps a frame is about 33 ms, so a 400 ms move is 12 frames.

Hierarchy

One hero move per shot. Secondary moves travel 50 to 70% as far and start 100 to 200 ms later. Ambient motion stays under 5% of the hero's travel on a slow sine. Something new every 2 to 4 s, and the hook has to land in the first 2 s.

Holds

On-screen text stays up at least 0.3 s per word plus 0.5 s. A two-word title needs 1.1 s. Keep a slow 2% push going during the hold so the frame doesn't feel dead while people read.

Beat grid

Frames per beat = fps x 60 / BPM. At 120 BPM and 30 fps that's 15 frames. Compute each beat from time and round each one separately, because adding a rounded number again and again drifts off the music. Cut on the beat, change the layout on the bar (every 4 beats). Changing everything on every beat wears people out.

Two accessibility numbers

Text at 4.5:1 contrast on every frame, including over moving backgrounds. Never more than 3 flashes a second. And for phone size, push key poses 10 to 30% further than feels right, the feed shrinks everything.

The trick that ties this together: put all of it in one motion.js tokens file (EASE, durations, stagger) and tell Opus "use only these tokens, no inline numbers". Then the whole film moves like one thing.

A generic "avoid the AI look" doesn't work. It just swaps one default for another. What works is banning the defaults BY NAME. This is the list I start from, merged from Movez and Florence:

BANNED
- centered title on a gradient
- everything fading in at once
- fade-and-slide-up on every element
- centered-everything layouts
- purple or blue gradients
- glow (incl. on UI), glow orbs, bloom, neon, lens flares
- decorative particles, particle bursts
- corner labels, frame borders
- generic spinning turntables
- text that moves while it's being read
- dead time

Then keep it alive. After every first render, look at what defaults it used anyway, and add them to the list. My list is longer than this now, and it gets longer every piece.

OK, here's what actually happened when I used all of this.

v1 of the Slabwatch film was 60 seconds, 1920x1080, 30 fps, all flat 2D. A seekable HTML page rendered frame by frame with Playwright. The beats:

Sound was a fallback. The Openverse API timed out, so the agent built a calm sound kit with earshot (more on that in section 11) and a synthesised pad in C, mixed to -14 LUFS.

On X I said it one shotted my demo video. And the first render did come out in one go. It just wasn't the one I ended up using.

My honest verdict on v1: it was flat and not that amusing. Everything was correct and nothing made you want to keep watching.

The lesson I gave my agent from this: for a product film, aim high by default. Real 3D, camera moves, light, depth. And show me the style in stills before rendering anything long. So I told it to go all out and make it 3D.

v2 was three.js 0.186 scenes rendered in headless Chromium on my laptop's RTX 2050. The scenes:

PBR lighting, depth of field, grain. The agent checked stills over 4 rounds before it rendered the full film.

Things that will cost you time if nobody tells you:

Headless Chromium renders on the CPU by default. It uses SwiftShader, which is software rendering and MUCH slower. These flags put it on your actual GPU through Vulkan:

const browser = await chromium.launch({
  args: ['--use-angle=vulkan', '--enable-gpu', '--ignore-gpu-blocklist'],
});

With them, my RTX 2050 rendered about 0.3 s per frame at 1080p with depth of field and shadows. That's a laptop GPU, so you don't need a 4090 for this.

ES modules need a local http server. file:// won't load them, and three.js examples are all modules now. Something like npx http-server and point the renderer at localhost.

Pin your three.js version and know what changed. PCFSoftShadowMap is gone in three 0.186, so older examples (and older model knowledge) break on it. Florence hit a similar trap in r160 where TextGeometry reads height and ignores depth. Pin the version, name the right parameter in the prompt, and the trap is gone.

Motion blur comes from subframes. A real camera's shutter stays open while things move, a frame from renderAt(t) is frozen at one instant, so fast moves look like they strobe. The fix Florence's 3D pieces and Movez's renderer both use: render 4 subframes per frame and blend them. Spread them over half a frame, which is how a 180-degree film shutter records blur. In the renderer:

// 4 subframes per output frame, spread over half a frame (180° shutter)
const SUB = 4, SHUTTER = 0.5;
let n = 0;
for (let i = 0; i < N; i++) {
  for (let k = 0; k < SUB; k++) {
    const t = (i + (k / SUB) * SHUTTER) / FPS;
    await page.evaluate((t) => window.renderAt(t), t);
    await page.screenshot({ path: `out/frames/${String(n++).padStart(5, '0')}.png` });
  }
}

Then blend each group of 4 into one frame:

ffmpeg -framerate 120 -i out/frames/%05d.png \
  -vf "tmix=frames=4,select='eq(mod(n\,4)\,3)',setpts=N/30/TB" -r 30 \
  -c:v libx264 -pix_fmt yuv420p -crf 16 out/anim.mp4

It costs 4x the render time, so turn it on for the final, not while iterating. Anything that isn't moving stays exactly as sharp, since 4 identical frames blend into the same frame.

Sound for v2 was earshot again, this time with generated risers, whooshes and sub hits over a synth bed, mixed to -14.6 LUFS. Earshot's critique scored the mix 19 out of 21, "launch-grade".

3D gives you depth of field, grain and close-ups, and every one of those eats text if you let it. A product film lives or dies on whether people can read the screen and the end card. So these four go in the render contract BEFORE the first frame:

1. Text and UI never go through depth of field or any blur. Draw them on their own layer after the DOF pass. Otherwise the bokeh drags ink out of the letters and you get grey halos, and the end card is the one frame everybody actually reads.

2. At least 1 texel per output pixel at the closest shot. If the camera pushes into your UI, capture the page at 2x or more (3x for real close-ups). You can't zoom into pixels that aren't there.

3. Grain at .02 or less, PNG frames all the way to the encoder. JPEG frames before x264 means every frame gets compressed twice, and heavy grain makes it worse. Encode the final at crf 18 or lower.

4. Look at full-size frames before you call it done. Pull the closest shot and the end card at full size and crop the text at 200%. A contact sheet won't show soft text and neither will a self-score. Only a full-size frame does. My agent's QA script also measures it: a sharpness score per text box (the strongest edges divided by the text's contrast), 0.50 or higher to pass, 0.60+ for big type. It's not done until that passes, whatever any self-score says.

Here's the block I paste:

SHARPNESS
- Text and UI on their own layer, drawn AFTER depth of field / any blur.
- Screens and textures captured at >= 1 texel per output pixel at the
  closest framing (2x+, 3x for close-ups).
- Grain <= .02. Chromatic aberration ~.0025. Max blur ~.006.
- PNG frames only, final encode crf <= 18 (16 by default, 14 for detailed 3D).
- Before "done": full-size closest shot + end card, crop text at 200%.

Two web-side extras from the sources: never put will-change on anything the camera scales (it rasterizes once and stays blurry), and snap resting text to whole pixels or render at 2x and scale down.

The first render is a draft. Every source says it and it matched what happened to me. The loop that finishes a film:

The scorecard I use, from Movez, each 1 to 10, keep going until everything is 8+:

SCORE EACH 1-10, THEN FIX THE 3 WORST WITH FRAME NUMBERS
- hook in the first 2 s
- readability at 360 px wide (phone)
- motion quality
- variety
- composition
- brand accuracy (real assets, right colors)
- sound sync

And the hunt list, things to look for on every pass: text overlapping during swaps, sliding where it should ease, corner labels and borders sneaking back in, centered-on-gradient, blurry scaled text, dead beats, a stutter at the loop seam.

Measure instead of eyeballing where you can. Florence caught a glint flooding 178,864 pixels and an anticipation pose landing ONE frame late, just by measuring. You can ask Opus for an onion skin (frames stacked at low opacity, even spacing means robotic motion) or a plot of position per frame (sharp corners mean dead starts and stops). It can draw both and read both.

Your notes should point at frames, not feelings. "The punch at frame 84 peaks late, move it 2 frames earlier" works. "Make it better" doesn't.

When a fix doesn't hold, render frames N-1 and N+1 side by side, have it list the differences, and fix only that line.

These ffmpeg one-liners do most of my reviewing:

ffmpeg -i out/final.mp4 -vf "fps=2,scale=270:-1,tile=6x5" -frames:v 1 out/contact.png

ffmpeg -ss 4.1 -i out/final.mp4 -vf "scale=320:-1,tile=12x1" -frames:v 1 out/strip.png

ffmpeg -i out/final.mp4 -vf "fps=1,scale=360:-1,tile=5x3" -frames:v 1 out/phone.png

ffmpeg -stream_loop 2 -i out/anim.mp4 -c copy out/loop_check.mp4

Freeze any frame by URL (from Florence). Put this before </body> and anim.html?t=2.5 shows exactly that frame, frozen. Opus can screenshot it without rendering anything else, which makes every round of the loop cheap:

<script>
  // anim.html?t=2.5 -> render that exact frame and freeze everything
  const q = new URLSearchParams(location.search).get('t');
  if (q !== null) addEventListener('load', async () => {
    await document.fonts.ready;
    const t = Number(q);
    if (window.renderAt) window.renderAt(t);
    document.getAnimations().forEach((a) => { a.(); a.currentTime = t * 1000; });
  });
</script>

Loops that actually loop. "Render 0 to N-1" fixes the double frame, but the motion still has to come back to where it started. Drive everything from one phase, and only use whole cycles:

const N = 180;                        // loop length in frames
const phase = (frame % N) / N;        // 0..1, wraps exactly
const angle = phase * Math.PI * 2;    // whole turns only, never 1.5
// organic drift that comes back: walk seeded noise around a circle
const drift = noise2D(Math.cos(angle) * 0.8, Math.sin(angle) * 0.8);

A rotation of 2 full turns loops, 1.5 turns jumps. For organic wobble, sampling noise around a circle means it ends exactly where it began. And make frame 0 a finished composition, because it's what people see while a GIF loads.

One more trick while iterating: ask for a playhead and frame labels on screen, pinned to frames. A move that runs late shows up as a label that lights before its move. Remove them for the final.

And every fix that works goes into the studio's CLAUDE.md as a rule, so the next film starts with it.

Claude can't hear. So every "this sound fits" needs a number behind it, and that's what earshot by Leon Lin is for. It's an open source tool ("ears for coding agents") that ranks and describes sounds, measures loudness, generates sound effects in code and critiques a full mix against professional launch films. That's where my 19/21 came from. It only judges the audio, so it tells you nothing about the picture.

The order my agent sources sound in:

Rules that held up:

Voice. One of the examples in Movez's article gave a mascot a voice with ElevenLabs. If you do that, the key lives in .env and the prompt only names the variable. Never paste a real key into a prompt, you'll screenshot that prompt for a post one day. HyperFrames also has built-in TTS if you don't want another account.

VOICE
- The ElevenLabs key is in .env as ELEVENLABS_API_KEY. Read it from there,
  never print it, never write it into a file. .env stays in .gitignore.
- Generate the voice-over first, then time every beat to the words.

Licenses matter if you post this. Fine: CC0, public domain, CC BY 2.0 to 4.0, Mixkit free. Not fine: anything NC, ND or SA. And read the actual item page, the license field in an API is a lead, not proof. CC BY credit goes in the post.

Muxing it all, with loudness normalisation in the same pass:

ffmpeg -i out/anim.mp4 -i music.wav -i sfx.wav \
  -filter_complex "[1:a][2:a]amix=inputs=2:duration=first,loudnorm=I=-14:TP=-1:LRA=11[a]" \
  -map 0:v -map "[a]" -c:v copy -c:a aac -b:a 192k -shortest \
  -movflags +faststart out/final.mp4

For anything longer than a short loop, the agent works for hours, and you don't want it polishing 900 wrong frames. So you put gates in, places where it stops and waits for your OK. This structure is from seeco and Movez:

GATES (stop and wait for my OK at each)
1  references + assets approved
2  style guide + shot list approved
3  stills per shot, critiqued
4  animatic at 960x540 with placeholder audio
5  full pass, polish, sound
6  formats, loop check, poster, README

DELIVER
final.mp4, loop_check.mp4, poster.png, contact.png, README

The brief itself: the film in one line, references, tools and budget, an identity lock for any character or logo, a beat sheet with timestamps, text-on-screen rules, then the gates above and the critique loop.

If you split chapters across subagents, have it write an ANIMATION_GUIDE.md first so they all code in one style.

ffmpeg -framerate 30 -i out/frames/%04d.png -c:v libx264 -profile:v high \
  -pix_fmt yuv420p -crf 18 -movflags +faststart out/anim.mp4

Sizes: 1920x1080 (16:9), 1080x1080 (1:1), 1080x1350 (4:5), 1080x1920 (9:16). Write scenes against a layout function and render every format from one timeline, then reframe per format, never crop. Vertical gets bigger type and fewer elements.

Without Premium, X caps video at 140 s.

GIFs. X allows 15 MB, up to 1280x1080, and caps a GIF at 350 frames (about 14 s at 25 fps). Gradients band in GIFs, so avoid them there. The palette command for a clean one:

ffmpeg -i out/anim.mp4 -vf "fps=25,scale=1200:-1:flags=lanczos,split[a][b];[a]palettegen=stats_mode=diff[p];[b][p]paletteuse=dither=bayer:bayer_scale=5:diff_mode=rectangle" -loop 0 out/anim.gif

The timing trap: GIF frame delays are in hundredths of a second. 25 fps (4/100 s) and 20 fps (5/100 s) are exact. 24 fps isn't, every frame gets 40 ms and the loop runs 4% fast. If you need 24, the delays have to go five frames at 40 ms, then one at 50 ms. Easier to export the GIF at 25.

The three articles I learned from barely touch Blender. It shows up once, as a caption in Florence's article: Shikhar's 3D browser world (1.75M views) was built "from Blender assets, generated images and Three.js", on Opus 5.5 medium, third iteration. No Blender how-to anywhere, and nothing on Manim, Motion Canvas, Lottie, Rive, After Effects scripting or Veo either. So I looked into everything else too.

My way (HTML and three.js, rendered frame by frame) is one route. Here's every route I found and when to pick it. Versions and prices are as of 2026-10-26, they change fast, so check before you commit.

You're making Use License / cost
One idea under 10 s, product film, UI motion, type, data, launch explainer HTML + Playwright + ffmpeg (my rig), or HyperFrames free; HyperFrames is Apache 2.0
A templated series, data-driven videos, a React team Remotion free up to 3 employees, then $25/seat/month (Creators) or $0.01/render with a $100/month minimum (Automators)
3D in the browser, abstract or product shots three.js MIT, free
Photoreal 3D, real light, physics, rigged characters Blender (Claude writes Python, Blender renders with no window) free, GPL; your renders are yours
Math, equations, diagrams Manim Community MIT, free
Vector explainers synced to a voice-over Motion Canvas (or its fork Revideo) MIT, free
Motion inside an app: icons, s Lottie (dotLottie) MIT runtimes
Interactive, state-driven app motion Rive editor free with a splash screen on exports, $9/seat to remove it; runtimes MIT
Motion inside a live website Motion or GSAP free, GSAP plugins included
You already live in After Effects AE + ExtendScript + aerender Adobe subscription
People, photoreal scenes, organic movement a video model (Veo 3.1 / Gemini Omni, Kling 4.0, Seedance 2.5, Runway Gen-4.5) paid credits, check the commercial terms
Hand-drawn, storybook style the claude-animation skill (node canvas) free

Blender does the stuff three.js fakes: real path-traced light (Cycles), physics sims (cloth, fluid, rigid body), rigged characters and geometry nodes. Current version is Blender 5.2 LTS, released 2026-07-14 and supported until July 2028.

There are two ways to get Claude driving it.

Way 1, scripts. This is the one I'd use, because it's the same mindset as the render contract. Claude writes a Python (bpy) file that builds the whole scene, Blender runs it with no window, then renders the frames. Re-runnable, so an edit is still a changed number and a re-render.

blender -b -P build_scene.py

blender -b scene.blend -E CYCLES -o //out/frame_#### -F PNG -s 1 -e 240 -a -- --cycles-device OPTIX

The gotcha, straight from the Blender manual: arguments run in the order you write them. -a (or -f 10 for one frame) goes LAST, after the output path and format, or your settings get ignored. Use CUDA instead of OPTIX on older NVIDIA cards, HIP on AMD, METAL on a Mac. blender -E help lists the engines.

Way 2, MCP (Claude drives a live Blender). There's an official one from Blender Lab, the Blender Foundation's experimental arm: add-on v1.0.3, needs Blender 5.1+. Its own page warns it "will execute LLM generated code in Blender without any guards" and recommends a VM, so take that seriously. The popular community one is ahujasid/blender-mcp (MIT, around 32k GitHub stars). It's a socket add-on that gives Claude viewport screenshots, runs Python, and pulls assets from Poly Haven, Sketchfab and 3D generators.

Engines. Cycles is the path tracer, accurate and slow, and it renders fine with no display. EEVEE is real-time and fast but needs a GPU context. Headless on Linux works through EGL and 5.2 added gpu.init() for GPU in background mode, but test it on your own machine first. Workbench is for quick previews.

The version trap, same lesson as three.js. Blender 5.0 renamed EEVEE's engine id from BLENDER_EEVEE_NEXT back to BLENDER_EEVEE, so older scripts (and older model knowledge) break on it. Put the Blender version in the prompt.

What it costs you. Cycles frames take seconds to minutes each, nowhere near the ~0.3 s my three.js frames took. A 4 GB laptop GPU like mine limits how big a scene can get. And Claude can't see the viewport in Way 1, so the loop from section 10 still applies: render stills, contact sheet, fix, then the full render.

Assets. Poly Haven is the safe default (CC0 models, textures and HDRIs). Sketchfab licenses vary per model, so read each one before it goes in a film you post.

Hybrids are the strongest move. Model and light it in Blender, export glTF (built in), and load it in three.js with GLTF. That's literally how Shikhar's piece was made. Or render Blender plates as backgrounds and put the UI and text on top in HTML, so text never goes through Blender's blur at all (that's rule 1 from section 09 again).

Where it fits: if the shot needs real materials, physics or a character, Blender. If it's UI, type or a product screen, stay in HTML.

seeco says code loses on photoreal scenes, characters and organic motion. Here's what to use instead:

Don't go looking for Sora. OpenAI shut the Sora app on 2026-04-26 and the API on 2026-09-24.

What you give up compared to code: you can't seek, you re-roll instead of editing, text and UI come out wrong, and you can't hit a beat on an exact frame. Check each tool's commercial terms before you use it for a paid launch.

The pattern that gets you both, from Movez and seeco, is generate-then-trace. Seedance renders the base shots for characters and physics, then Opus redraws the whole film in JavaScript on top of them, so the viewer only ever sees the code layer. Or keep it simpler: generated footage as background plates, with code-drawn type over it.

Remotion. v4 (4.0.538 as of 2026-10-22). Videos are React components, it has official agent skills and mature cloud rendering on Lambda. License in the table above. Remember Movez's point from section 01: Opus won't use it unless you say so.

HyperFrames (by HeyGen). Plain HTML and GSAP, no build step. It renders by seeking each frame in headless Chrome and encoding with FFmpeg, locally or on AWS Lambda. Apache 2.0, "no per-render fees or commercial-use thresholds". Needs Node 22+ and FFmpeg. It's basically my rig as a framework, and it's the default in my agent's studio now.

Manim Community. Python, the 3Blue1Brown-style engine, v0.22.0 (2026-10-08). Best for anything with equations or graphs.

manim -pqh scene.py SceneName   # render the scene in high quality and preview it

Motion Canvas. TypeScript generators with a live editor, made for explainers synced to a voice-over. The catch: the last tagged release (v3.17.2) was December 2024 and development has slowed. Revideo is an MIT fork that adds rendering from code or an API.

Lottie. Vector animation stored as JSON, usually exported from After Effects with Bodymovin, or written by code. Use it for motion inside apps. dotLottie is the maintained runtime (lottie-web hasn't moved since 2025-09).

Rive. Interactive animations with state machines, for apps and games. Pricing's in the table.

After Effects. If you already use it, Claude can write ExtendScript (.jsx) and render with aerender, AE's command-line renderer. There are community AE MCP servers but none are official and I haven't vetted any, so I'm not naming one.

claude-animation skill (from Movez's setup). Hand-drawn, storybook-style films drawn in node canvas, with synthesised sound.

The rule across all of it: pick the route by the shot, mix routes when one film needs two, and keep text in HTML wherever you can.

All three source articles say it in different ways, and it matches what happened to me: "one prompt" is mostly a myth.

seeco's numbers are the clearest. The best "one-prompt" films started from 9,500 to 19,000 character briefs and ran for hours. One watercolor short took 163 model calls and about 7 hours. Florence's own pieces took 4 to 15 follow-ups and around 2 hours each, plus 45 to 90 minutes for sound. The biggest piece in Florence's article, Vox's black-hole lab, took 5 h 28 min end to end, about $90 at API prices. /fast is up to 2.5x faster but double the price ($8/$40 instead of $4/$20 per million tokens, Florence's numbers).

On effort settings the sources disagree. Florence keeps Opus 5.5 on medium for everything and only raises it when a from-scratch piece misses twice. Movez and seeco go xhigh for new films and max when the first 3 seconds carry a launch. What I do: medium for fixes, xhigh for a new piece.

The sources also sell things (a Substack, a "sell this as a service" angle). The techniques hold up. The business stuff is anecdotes, skip it.

Step What you do The one rule
Pixels Opus writes a program, Playwright renders, ffmpeg encodes every frame is a pure function of t
Rig renderer + QA scripts + a studio CLAUDE.md every fix becomes a rule in CLAUDE.md
Assets load the real site, real logo, real numbers never let it draw your product
Direction fill the template, GOAL first, then TIME swap every adjective for a number
Numbers easing, springs, durations from one tokens file position may bounce, opacity never
Bans start from the list, extend after every render ban defaults by name
3D GPU flags, local server, pinned version aim high, show style in stills first
Sharpness text after DOF, 2x+ textures, PNG, low grain check full-size frames, crop at 200%
Loop stills, score, fix the 3 worst, 2 to 4 rounds notes point at frames, not feelings
Sound earshot, measured, licensed picture works muted, hits 1 frame late
Ship H.264 yuv420p, one timeline per format, palette GIF at 25 fps reframe, never crop
Alternatives Blender for real 3D/physics, Manim for math, Remotion for templates, video models for organic shots pick by the shot, mix routes, keep text in HTML

If you try this, send me what you make, I wanna see it.

── more in #ai-tools 4 stories · sorted by recency
── more on @claude opus 5.5 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/how-i-make-motion-vi…] indexed:0 read:33min 2026-10-26 · —