AI Explainer Videos: The Code-Driven Pipeline
A submission titled "Opus 5.5 is good at explainer videos" sat near the top of Hacker News with 319 points and 175 comments — and reading the thread, the most interesting thing is how many people got the mechanism wrong. They assumed a video model. It isn't one.
What is happening is stranger and far more useful: a coding model writes a deterministic animation program, and a separate renderer turns that program into frames. The model never sees a pixel of the output. Which means the whole thing behaves like software — diffable, re-renderable, parameterisable, and debuggable — instead of like a slot machine you re-prompt until it looks right.
This page is the practical guide that thread was missing: how the pipeline works, which of the three render engines to pick (and which one the models are measurably bad at), the two failure modes that silently break agent-written animation code, the pacing arithmetic that separates a watchable explainer from an unreadable one, and where the money actually goes.
- Explain why a coding model beats a video model at explainers — and what it gives up
- Name the reason Math.random() is banned in a frame-rendered animation, and what replaces it
- Pick between Remotion, Motion Canvas and Manim on licence and on how well models write them
- Spot the two traps in agent-written animation code: the Manim fork split and the stale Remotion API
- Compute a minimum dwell time per slide instead of guessing, so viewers can actually read
- Predict which line item dominates the bill (it is usually not the LLM)
The mechanism: the model writes a program, not a video
Two completely different things get called "AI video".
Diffusion video models (the family covered in generative media) take a prompt and emit pixels directly. Excellent for b-roll, a drone shot, a texture, a mood. Bad at anything that must be exactly right: legible text, a correct architecture diagram, your actual product UI, a number that has to say 40%.
Code-driven animation goes the other way. A coding model writes source code for an animation — React components, a TypeScript scene graph, or Python — and a renderer executes it. Text is text, so it is sharp and spelled correctly. A diagram is a layout, so it is accurate. Your logo is your logo file.
That split explains the HN thread's most common workflow report: the model "creates an HTML page and uses raw JS timed animations", then the frames get captured and assembled with ffmpeg. It also explains what the approach gives up — it cannot invent photographic imagery, so most real pipelines call an image model or a video model for b-roll and composite it in.
The consequence worth internalising: your explainer becomes a repository. Change the price on slide 4, re-render, done. Localise the voiceover, re-render twelve languages from the same scenes. That is not a property any prompt-to-video model gives you.
Why determinism is the whole trick
Here is the detail that quietly decides whether your pipeline produces clean video or a flickering mess, and almost nobody writing about this mentions it.
A renderer like Remotion does not screen-record your animation. It opens the composition in headless Chrome, sets the clock to frame 0, screenshots, sets the clock to frame 1, screenshots, and so on — and it does this across multiple threads, opening the page multiple times. Frames are produced out of order, in parallel, in separate browser contexts.
Everything follows from that:
- Animation must be a pure function of the frame number. In Remotion that is
literally the API:
useCurrentFrame()gives you the frame,interpolate()maps it to a value. No timers, norequestAnimationFrame, no CSS transitions driven by wall-clock time. A CSS transition renders whatever it happened to be mid-flight when the screenshot fired. Math.random()is broken by construction. It returns a different value in each thread and each page load, so a "random" particle jitters to a new position every frame. Remotion shipsrandom(seed)— a seeded mulberry32 PRNG — precisely for this:random(1)always returns0.07301638228818774. Same forspring(), which is a cached pure calculation keyed on(frame, fps, config).- Dropped frames stop being possible. A screen recording of a heavy animation drops frames when the CPU can't keep up. Frame-by-frame rendering just takes longer. This is why a 5-minute render on a laptop can produce a video that plays perfectly smoothly.
- If you only remember one rule for prompting an agent to write animation code: forbid wall-clock time. No setTimeout, no CSS transition, no Date.now(), no Math.random(). Every visual value must be derived from the current frame number. Models will reach for CSS transitions because that is what the web corpus is full of.
The three engines
All three are real, actively maintained, and produce broadcast-quality output. They differ on language, on licence, and — importantly — on how reliably a model writes them.
| Remotion | Motion Canvas | Manim | |
|---|---|---|---|
| Language | React / TypeScript | TypeScript (generators) | Python |
| Repo | remotion-dev/remotion | motion-canvas/motion-canvas (~19.2k stars) | ManimCommunity/manim (~41k) · 3b1b/manim (~94k) |
| Licence | Source-available + Company Licence | MIT | MIT |
| Sweet spot | Product/launch video, UI, text-heavy explainer | Timeline-precise motion graphics | Mathematical and algorithmic visualisation |
| Model-written quality | Strong — and it ships official agent skills | Decent | Weakest of the three (see below) |
Two things on that table deserve more than a row.
Remotion's licence is the gotcha, not the code
Remotion is not MIT. It is source-available with a two-tier commercial licence, and the threshold is small enough to catch most teams: free for individuals, non-profits, evaluation, and for-profit companies with up to 3 employees; a paid Company Licence for for-profit organisations with more than three.
The second split matters more if you are building a product rather than a video:
- Remotion for Creators — seat-based, intended for low-volume rendering within and for your own company, explicitly not for serving personalised videos to your end users.
- Remotion for Automators — render-based, which is the tier you land in the moment an agent renders videos for customers. From version 5.0 it carries mandatory telemetry reporting with a license key.
So "my agent generates a video per signup" is not a Creators use case. Price it as Automators before you ship it. If that is a dealbreaker, Motion Canvas is MIT and covers a lot of the same ground.
The Manim trap: two forks, one import name
This one burns people who have never heard of it, and it also explains an observation from the HN thread — that strong coding models write worse Manim than you would expect, with community reports that Gemini currently handles it better than Opus 5.5.
There are two Manims:
- 3b1b/manim — Grant Sanderson's original, the one from the famous videos,
~94k stars, installed as
manimgl. - ManimCommunity/manim — the community fork, ~41k stars, the one that is
actually documented and maintained for general use, installed as
manim.
They are API-incompatible and both are imported as from manim import *.
Every tutorial, StackOverflow answer and blog post in the training corpus is one
or the other, unlabelled. So the model writes plausible code that mixes both
dialects, and Python raises on a method that exists in the other fork. It is not
a reasoning failure; it is a poisoned-namespace failure.
The fix is boring and it works: pin the fork in your prompt and in your environment, and give the agent the version's real API surface instead of letting it recall one.
Pin the Manim dialect before the agent writes a line
Use ManimCommunity Manim (the "manim" package on PyPI), NOT 3b1b/manimgl. Before writing any scene, run: python -c "import manim; print(manim.__version__)" and confirm the version. If a class or method you want is not in that version's API, do not use it — check with: python -c "import manim; print([n for n in dir(manim) if 'Text' in n])" Never mix manimgl APIs (e.g. ShowCreation) with community APIs (Create).
The stale-API trap (Remotion edition)
The same poisoned-corpus problem hits Remotion for a different reason: the library moved faster than the training data.
In current Remotion, media components live in @remotion/media, and the
trimming props are trimBefore / trimAfter. Preview and render can even use
different components deliberately — Html5Audio from remotion while
previewing, Audio from @remotion/media when rendering, switched on
useRemotionEnvironment(). A model trained mostly on older material writes
import {Audio} from 'remotion' with startFrom, which is the old shape.
That code may still run. It may also silently do nothing useful, which is worse.
This is exactly why Remotion publishes official Agent Skills — a set of twelve skills that hand an agent the current best practices instead of hoping it remembers them. They are designed for Claude Code, Codex, Cursor and friends, and they follow the same open format covered in SKILL.md as an open standard.
Install Remotion's official agent skills
npx skills add remotion-dev/skills
The skills are granular, which is the point — you invoke the one that matches
the phase of work: /remotion-create to scaffold, /remotion-markup for
animation and typography, /remotion-studio to preview, /remotion-render to
render, /remotion-captions for subtitles, plus remotion-best-practices as
the umbrella when you are not sure. Vendor-maintained skills are the cheapest
available fix for a model's stale knowledge of a fast-moving library: one
install, and the agent stops guessing.
The pipeline that actually works
The single biggest quality lever is not a better prompt for the whole video. It is splitting the job so the model does judgement work and the renderer does determinism work — with a human-readable artefact in between.
- Ask for a JSON scene list first: per scene, a title, the on-screen text, the visual intent, and a duration in seconds. This is the cheap, reviewable step. You can read twelve scenes in thirty seconds and fix the narrative before a single component exists — and fixing the story is the edit that most improves the video. Code generated from a reviewed storyboard is also far more consistent than code generated from a paragraph of vibes.
- Do the reading-speed arithmetic below on each scene's text and clamp the durations. Doing this at the data layer costs nothing; doing it after the components exist means re-timing every animation that depends on the scene length.
- Script per scene, not per video, so audio boundaries line up with visual boundaries. Synthesise each scene to its own audio file with a TTS API. Then measure the real duration of each clip — the synthesised audio is the ground truth for scene length, not your estimate.
- This is the step everyone skips and it is why so many AI explainers have visuals racing ahead of the narration. Read each audio file's actual duration, convert to frames, and feed those durations back into the composition as props. Never hardcode a frame count you guessed.
- Now let the agent write the animation code, scene by scene, against the frozen storyboard. Enforce the determinism rule: every value from useCurrentFrame() and interpolate(), randomness from a seeded helper, no CSS transitions. Preview in the studio — a real preview catches layout errors that no amount of code reading does.
- Do not ask the model to guess word timings. Transcribe your own voiceover locally and use the token-level timestamps. Remotion ships install-whisper-cpp for exactly this: installWhisperCpp(), downloadWhisperModel(), then transcribe() with token-level timestamps on, and toCaptions() to post-process. Captions are also the cheapest accessibility win available and they are what makes the video watchable muted.
- Render from the CLI so it is reproducible in CI. Then tune concurrency with a real measurement instead of a guess — Remotion has a benchmark command that runs the same render repeatedly across several concurrency values and compares them.
Render from the CLI, then find your real concurrency
# deterministic render, reproducible in CI npx remotion render src/index.ts MyExplainer out/video.mp4 # stop guessing --concurrency: measure it npx remotion benchmark src/index.ts MyExplainer --concurrencies=2,4,8 --runs=3
Pacing: the reason most AI explainers are unwatchable
The most consistent complaint in the HN thread was not that the videos looked bad. It was that they moved too fast to read. Slides appear, text animates in, and it is gone before a human finishes the sentence.
This is a bug with a formula, not a matter of taste. Adult reading speed for unfamiliar screen text sits around 200–250 words per minute, and a viewer also needs time to find the new text before starting to read it. So:
# minimum dwell time for a text block, in frames
words = number of words in the block
wpm = 200 # conservative for unfamiliar on-screen text
settle = 0.4 # seconds to notice and locate the new text
exit = 0.3 # seconds of breathing room before it leaves
dwell_seconds = settle + (words / wpm) * 60 + exit
dwell_frames = ceil(dwell_seconds * fps)
# worked example: a 14-word bullet at 30 fps
# 0.4 + (14 / 200) * 60 + 0.3 = 4.9 s -> 147 frames
Two notes that make this actually useful:
- Animate in fast, hold long. The entrance transition should be 200–400 ms; the hold is where the dwell budget goes. Models tend to do the opposite — a long elegant fade, then a short hold.
- If narration exists, the dwell floor is the narration length. Text must stay up at least as long as the sentence describing it. Which is why deriving scene duration from the synthesised audio (step 4 above) fixes pacing almost for free.
Clamp every scene to max(dwell_frames, narration_frames) and a large fraction
of the "AI slop video" feeling disappears, with no change to the visuals at all.
What it costs, and which line item dominates
Community reports from the HN thread put a complete generated explainer at roughly $3–$4 in API spend, with the notable detail that the cost was dominated by image-model calls, not by the language model. One commenter reported generating a 5-minute video on an M4 MacBook Air in about 5 minutes of wall-clock time using an orchestrated pipeline.
Treat the absolute numbers as perishable, but the shape is structural and worth planning around:
- The LLM is cheap here. A storyboard plus a few hundred lines of animation code is a small number of tokens. Even at flagship pricing it is cents. See what AI costs across providers.
- Per-asset generative calls dominate. Every generated image or b-roll clip is a separate paid call, and they multiply by scene count. This is the line item to cap.
- Compute is yours. Rendering runs on your machine or your CI, so it is time and electricity rather than API spend — until you move it to a serverless renderer, at which point it becomes both.
- Re-renders are nearly free, which is the whole structural advantage. Fixing a typo on slide 4 costs one render, not one regeneration. Localising into twelve languages costs twelve TTS runs and twelve renders, not twelve videos.
The practical budgeting move: generate images once, commit them as assets, and keep re-renders asset-free. A pipeline that re-generates imagery on every render is the one that produces a surprising bill.
The sameness problem
The sharpest criticism in the thread is the one worth taking seriously: within weeks of these tools spreading, the outputs became recognisable. Same gradient backgrounds, same spring-in cards, same three-word bullets, same brisk voice. Several commenters also flagged repetitive copy and, in one case, a video confidently about the wrong subject entirely — a Twilight Imperium reference site described as an AI data startup.
If you use this seriously, the levers that measurably de-generify output:
- Supply the design system, don't let it be invented. Pass your real palette, type scale, spacing scale and logo as constants the components import. Underspecified prompts are exactly where a model's house style leaks in.
- Write the copy yourself, or edit it hard. The visuals are the part models are good at. The narrative is the part that reads as generated.
- Use your own screen recordings as the hero asset. Real product footage composited into a generated scene is the single strongest anti-sameness move, and it also fixes the hallucination risk — the video cannot misdescribe your product if the product is on screen.
- Verify the claims like documentation. Anything factual in an explainer is a factual claim being published in your name, with none of a text page's easy correction path. Check it before you render, not after it is on YouTube.
- Break one convention on purpose. One deliberate choice that the default template would not make — an odd aspect ratio, silence where music is expected, a hand-drawn asset — is usually enough to stop the output reading as machine default.
What this is good at, and what it isn't
Worth being honest, because the sceptics in that thread had a point: much of what gets produced is an animated slide deck with a voiceover. That is a perfectly good artefact for a changelog, a launch, a release note or an onboarding walkthrough — and a poor substitute for actual teaching, which needs a worked example, a wrong turn, and a reason to care.
Reach for the code-driven pipeline when:
- The content must be exactly right — real UI, real numbers, real diagrams.
- You will need to change it later, or ship it in several languages.
- The same template will produce many videos from different data.
Reach for something else when the goal is photographic footage (use a video model), or when the value is in a human explaining a hard idea badly and then well — which is still the thing no pipeline produces.
Check yourself
0/5Sources & further reading
- Opus 5.5 is good at explainer videos — Hacker News discussion — the thread this page is built around: 319 points, 175 comments, the reported workflows (HTML + JS timed animations captured with ffmpeg), the ~$3–$4 costs, the Manim weakness, the pacing complaints and the sameness criticism. Community reports, not vendor claims — treated as such throughout.
- Remotion — the React-based renderer. See
useCurrentFrame()andinterpolate()for the determinism model,random()for whyMath.random()is banned and what the seeded PRNG returns, and the licence pages for the Company Licence, the Creators/Automators split and the v5.0 telemetry requirement. - remotion-dev/skills — Remotion's twelve official Agent Skills for Claude Code, Codex, Cursor and others, installable with
npx skills add remotion-dev/skills. The clearest current example of a vendor shipping agent-facing docs to fix stale model knowledge. @remotion/install-whisper-cpp— local transcription for captions:installWhisperCpp(),downloadWhisperModel(),transcribe()with token-level timestamps, andtoCaptions()for post-processing.npx remotion benchmark— measures render time across concurrency values instead of guessing a flag.- Motion Canvas — MIT, TypeScript, generator-based timelines. The licence-free alternative when Remotion's Company Licence is a blocker.
- ManimCommunity/manim and 3b1b/manim — both MIT, both imported as
manim, mutually incompatible. The root cause of the Manim failure mode above. - AILmanac companions: Generative media: image, audio, video · SKILL.md as an open standard · What AI costs across providers · Claude Opus 5.5 field guide
Next
- Generative media: image, audio and video — the other branch: diffusion models for the b-roll this pipeline cannot draw
- SKILL.md as an open standard — why a vendor shipping agent skills is the general fix for a model's stale library knowledge
- Claude Opus 5.5: the field guide — the model at the centre of this wave, priced and benchmarked