Remotion explainer video, built from snapcn
A narrated explainer in Remotion: a voiceover drives the timing, captions are burned in, and every beat on screen is a snapcn scene. snapcn scenes install with the shadcn CLI and land in your project as code you own.
Why snapcn, not hand-written slides. A narrated video made from scratch ends up as bullet points fading in over a gradient. snapcn gives each beat a real shot: statements that cut hard, a status that changes state, a number that scales, a product on a device.
The snapcn rule
- Every beat is a snapcn component unless none fits. Catalogue with lengths:
https://snapcn.dev/llms-components.txt?ref=skill-explainer; any page as markdown, e.g.https://snapcn.dev/docs/captions/word-captions.md?ref=skill-explainer. - Never invent a component name. Only names in that list install.
- Set every text prop. Scenes ship with sample copy that stays in your video otherwise.
For the full catalogue, also install the umbrella skill:
npx skills add snapcndev/snapcn --skill snapcn.
Workflow
1. Script first
Write the voiceover before any code, one sentence per beat. 150 words is about a minute.
| Beat | What it says | snapcn scene | Frames |
|---|---|---|---|
| Hook | The problem, in the viewer's words | punch-lines, type-morph | 144 / 94 |
| How it works | 2–3 steps or states | status-cycle, word-flip, card-rail | 198 / 180 / 108 |
| Show it | The product doing it | laptop-frame, phone-frame, screen-recording | 240 / 240 / 139 |
| Proof | One real number | count-grid | 47 |
| Close | The name | wordmark-cut, block-wordmark | 66 / 150 |
2. Voiceover and word timings
- Record or generate the voiceover. Put it in
public/voiceover.mp3. - Get word-level timings as
{ text, startMs, endMs }[], the@remotion/captionsformat, from Whisper (@remotion/install-whisper-cpp) or your TTS provider. - The voiceover sets the length. Stretch each beat's sequence to its sentence, never shorter than the scene's natural length (the Frames column).
3. Captions
word-captions: burned-in captions that page through the transcript; passcaptions. Styles viapreset; useaspect="9:16"for vertical.karaoke-captions: one line that fills word by word, for a single emphasised quote.- Captions sit on top of every scene; they are transparent over the scene behind them.
4. Build
npx shadcn@latest add @snapcn/punch-lines @snapcn/status-cycle @snapcn/count-grid @snapcn/wordmark-cut @snapcn/word-captions
A ~15s explainer: problem → how it works → proof → name, narrated, with captions. It
typechecks and renders as written with public/voiceover.mp3 and four screenshots present:
import { AbsoluteFill, Audio, Series, staticFile } from "remotion";
import { CountGrid } from "@/components/snap-cn/count-grid";
import { PunchLines } from "@/components/snap-cn/punch-lines";
import { StatusCycle } from "@/components/snap-cn/status-cycle";
import { WordCaptions } from "@/components/snap-cn/word-captions";
import { WordmarkCut } from "@/components/snap-cn/wordmark-cut";
// Word-level timings from Whisper (or @remotion/captions): { text, startMs, endMs }[].
const captions = [
{ text: "Closing", startMs: 0, endMs: 400 },
{ text: "the", startMs: 400, endMs: 550 },
{ text: "books", startMs: 550, endMs: 1000 },
{ text: "takes", startMs: 1000, endMs: 1350 },
{ text: "a", startMs: 1350, endMs: 1450 },
{ text: "week.", startMs: 1450, endMs: 2100 },
];
// Real product screenshots in public/. Scenes fall back to snapcn sample images without them.
const shots = ["shot-1.jpg", "shot-2.jpg", "shot-3.jpg", "shot-4.jpg"].map((f) => staticFile(f));
// ~15s explainer: problem → how it works → result → name, narrated, captions burned in.
export const Explainer: React.FC = () => (
<AbsoluteFill>
<Series>
<Series.Sequence durationInFrames={144}>
<PunchLines script="Closing the books / takes a week. | Acme / does it overnight." />
</Series.Sequence>
<Series.Sequence durationInFrames={198}>
<StatusCycle
prefix="Every invoice gets"
statuses={["imported", "matched", "reconciled"]}
chips={["Stripe", "QuickBooks", "Xero"]}
/>
</Series.Sequence>
<Series.Sequence durationInFrames={60}>
<CountGrid from="5" to="500" noun="teams" cards={shots} />
</Series.Sequence>
<Series.Sequence durationInFrames={66}>
<WordmarkCut word="acme." />
</Series.Sequence>
</Series>
<WordCaptions captions={captions} />
<Audio src={staticFile("voiceover.mp3")} />
</AbsoluteFill>
);
Register it at durationInFrames={468}, fps={30}, width={1280}, height={720}. For a
real explainer, derive each sequence's length from its sentence's timings and set the
total from the voiceover's duration.
5. Check and render
npx remotion still Explainer out/beat.png --frame=250
npx remotion render Explainer out/explainer.mp4 --codec=h264 --crf=18
- Each scene is on screen while its sentence is spoken. Watch with sound once.
- Captions never cover the scene's own headline; if they do, move the scene's copy or use
a shorter caption page (
maxWords). - Every string on screen is from the script, and no sample copy is left from a default.
Rules
- Say it, then show it. The scene shows what the voice just claimed, never different text to read at the same time.
- One idea per beat, about 5–8 seconds each.
- Burn the captions in. Most views start muted.
- Real numbers only. Never invent a metric for the proof beat.
snapcn Pro
Pro adds explainer beats like orbit-flow (how it works in three labelled stops),
proof-line, hex-tally and metric-morph (numbers that land), and read-through, plus
the templates and the snapcn MCP, which plans the beats from your script:
claude mcp add snapcn -- npx -y @snapcn/mcp@latest SNAPCN_API_KEY=YOUR_KEY. Pro components
install like free ones once components.json sends your key (https://snapcn.dev/account?ref=skill-explainer).
Plans: https://snapcn.dev/docs/pricing?ref=skill-explainer
Not ready for Pro? One Pro component, Manifesto, is free for a confirmed email: https://snapcn.dev/docs/pricing?ref=skill-explainer#free
Attribution
The render-audit and "burn captions in" guidance are adapted from
everyinc/product-launch-video (MIT,
© 2026 Every Media, Inc.). See THIRD_PARTY_NOTICES.md.