---
title: "Movie-grade Explainer — a concept → a cinematic vertical explainer with pixel-perfect HTML overlays"
tier: hero
format: short-form-video
theme: explainer | education | edutainment | science
persona: science educator, explainer creator, edutainment channel, course creator
duration: "1 topic → 45–90s vertical reel in ~6–12 min"
budget_usd: "$0.80–$4.00 per reel (5–8 beat keyframes + motion/Ken-Burns + TTS + music + hyperframes overlay)"
caps: ["generate_project", "ltx-q-i2v", "seedance-i2v", "chatterbox-tts", "music", "ffmpeg-concat", "ffmpeg-mux", "hyperframes-render"]
skills: ["short-form-video", "hyperframes-overlays"]
showcases: ["/chapters/movie-grade-explainer-example.html"]
status: "live (2026-06-09) — flagship rendered E2E. The example reel ('How a Black Hole Works') was produced exactly via this recipe (Ken-Burns base + hyperframes-render overlay)."
reliability: 3.9 # 5 − image gen .3 − i2v .6 − tts .2 − music .2 − 8-stage path .3 + .5 proven E2E showcase (black-hole reel); the Ken-Burns motion path is deterministic — taking it instead of i2v runs ~4.5; hyperframes-render overlay is deterministic
---

# Movie-grade Explainer — cinematic b-roll + pixel-perfect HTML, in one reel

Point this at any concept you want to explain — "how a black hole works", "how mRNA vaccines train your immune system", "how a transformer attends" — and it builds a **movie-grade vertical explainer**: cinematic AI b-roll for the *feel*, and a single timed-HTML overlay for the *facts*. The b-roll carries the mood; the overlay carries the title, the progress bar, and the per-beat caption cards with the key term or equation rendered crisply. AI video can never render legible text or a clean equation — so this playbook never asks it to. The text lives in the HTML layer.

## What you'll get

> **A finished reel = vertical 9:16 + voiceover + full-length music bed + crisp HTML text overlay + 45–90s.** Pass `aspect_ratio:"9:16"` to `generate_project` (it threads to every scene), animate in waves of ≤4–5 (or use the Ken-Burns stills fallback when fal's video tier is flaky), stitch with `ffmpeg-concat`, mux `chatterbox-tts` narration over a ducked `music` bed with `audio_fill:"loop"` so the score covers every second, then lay a single `hyperframes-render` document over the whole reel. A silent montage — or one whose music cuts out, or with the equation baked into a blurry frame — is a draft, not a deliverable. See the `short-form-video` skill's *definition of done* and the `hyperframes-overlays` skill for the overlay pattern.

A 45–90s **vertical (9:16, 1080×1920)** explainer: a hook → analogy → 3-step mechanism → payoff script, 5–8 cinematic beat keyframes, slow motion (i2v or Ken-Burns), authoritative VO over an instrumental bed, **and** a pixel-perfect HTML overlay — persistent top title, CSS progress bar, and per-beat caption cards (title + key term/equation) that fade in/out at their timestamps.

## The cap chain (the PROVEN recipe)

```
topic (the concept to explain)
  → agent writes the script: hook → analogy → 3-step mechanism → payoff
       (this is BOTH the VO narration AND the on-screen beat captions)
  → generate_project           # one cinematic 9:16 keyframe per beat
       aspect_ratio: "9:16"
       image: gpt-image | flux-dev | cosmos-3-image (deep-space) | recraft-v4
       PICK ON-POLICY SUBJECTS — science / space / nature / abstract sidestep fal's content filter
  → motion (pick ONE):
       (a) ltx-q-i2v           # slow cinematic push per keyframe (resolution bug fixed)
                               # — or alt video caps kling-i2v / pixverse-i2v / cosmos-3-i2v
       (b) Ken-Burns fallback  # ffmpeg zoompan slow-zoom on each 9:16 still → 8s clips
                               # RELIABLE + exactly 9:16. The flagship reel used THIS.
  → chatterbox-tts             # authoritative explainer narrator
  → music                      # instrumental bed ("ambient cinematic, building, no vocals")
       lyrics_prompt:"[Instrumental]"   # MANDATORY — else the bed is sung aloud
  → ffmpeg-concat              # stitch the clips → silent base reel
  → ffmpeg-mux  audio_fill:"loop"   # VO over a ducked, LOOPED music bed across the FULL reel
                               #   — without audio_fill:"loop" a short bed cuts out and the tail runs silent
  → hyperframes-render         # THE HYBRID LAYER — one timed HTML doc over the whole base reel
                               # ≤15s reel → create_media model_override:"hyperframes-render"
                               # >15s reel → call the cap directly (tool /run/hyperframes-render
                               #             or raw SDK /inference) with duration:40
```

## How the agent should run this (interaction contract)

1. **CONFIRM (one message, ≤1 question):** restate in 1 line ("60s 9:16 explainer on <concept>, 6 beats + crisp HTML overlay, ~$1–4, ~10 min") + real choices: Ken-Burns motion (proven, the flagship's path) vs i2v (prettier, flakier) · 5 vs 8 beats · gpt-image vs flux-dev keyframes. If the concept is clear, state assumptions and start.
2. **PREVIEW CHECKPOINT:** show the SCRIPT + the overlay HTML beats (titles, terms, EQUATIONS — $0, plain text) and then the still keyframes (~$0.30) BEFORE any motion fires. A wrong equation is this playbook's worst failure — it gets caught in the HTML draft, free.
3. **NARRATE:** per wave: "animating beats 1–4 (ltx-q-i2v, ~2–4 min each), job …"; poll `get_create_media` every 10–15s, ≤4–5 i2v in flight; `hyperframes-render` on a 40s reel takes ~1–2 min (direct cap call for >15s) — say so. Never silent >2 min.
4. **FAIL GRACEFULLY:** i2v 422/flaky → soften to on-policy phrasing → alternate caps (`kling-i2v`/`pixverse-i2v`/`cosmos-3-i2v`) → **Ken-Burns stills** (the flagship honestly shipped on this — it's a recipe, not a defeat); gpt-image 422 → soften → nano-banana/flux-dev; `hyperframes-render` fail → retry once → deliver the base reel + a note that the overlay is pending. ≤2 retries per stage; a reel without the overlay beats nothing.
5. **DELIVER:** final MP4 + one honest line ("motion was Ken-Burns on N of 6 beats; all terms/equations verified and rendered in the HTML layer, never in-image") + ONE next step ("tighten the overlay timings, or run the next concept").

## The differentiator — the timed-overlay HTML pattern

`hyperframes-render` runs an agent-generated HTML document in Chromium and composites it over the base reel for `duration` seconds. The whole layer is **one** HTML file driven by CSS `@keyframes` on a fixed timeline: a persistent top title, a progress bar that fills across the full duration, and per-beat caption cards that fade in/out at their timestamps. This is what AI video physically cannot do — render an equation or a term legibly. Here it's deterministic, vector-crisp text.

```html
<!-- 40s reel, 5 beats. Each card fades in at its timestamp, holds, fades out. -->
<style>
  body { margin:0; width:1080px; height:1920px; font-family:'Inter',sans-serif; color:#fff; }
  /* persistent top title */
  .title { position:absolute; top:80px; width:100%; text-align:center;
           font-size:52px; font-weight:700; text-shadow:0 4px 24px rgba(0,0,0,.7); }
  /* CSS progress bar across the whole reel */
  .bar  { position:absolute; bottom:120px; left:8%; width:84%; height:6px;
          background:rgba(255,255,255,.2); border-radius:3px; overflow:hidden; }
  .fill { height:100%; background:#7cf; width:0;
          animation:fill 40s linear forwards; }
  @keyframes fill { to { width:100%; } }
  /* per-beat caption card — one class per beat, staggered by animation-delay */
  .beat { position:absolute; bottom:200px; left:8%; width:84%; opacity:0;
          background:rgba(10,12,20,.72); border:1px solid rgba(124,204,255,.4);
          border-radius:16px; padding:28px 32px; backdrop-filter:blur(8px); }
  .beat h2 { margin:0 0 8px; font-size:40px; }
  .beat .term { font-family:'JetBrains Mono',monospace; font-size:34px; color:#7cf; }
  @keyframes show { 0%{opacity:0;transform:translateY(20px)} 10%,90%{opacity:1;transform:none} 100%{opacity:0} }
  .b1 { animation:show 8s 0s   forwards; }   /* beat 1: 0–8s   */
  .b2 { animation:show 8s 8s   forwards; }   /* beat 2: 8–16s  */
  .b3 { animation:show 8s 16s  forwards; }   /* beat 3: 16–24s */
  .b4 { animation:show 8s 24s  forwards; }   /* beat 4: 24–32s */
  .b5 { animation:show 8s 32s  forwards; }   /* beat 5: 32–40s */
</style>
<div class="title">How a Black Hole Works</div>
<div class="bar"><div class="fill"></div></div>
<div class="beat b1"><h2>The Hook</h2><div class="term">Gravity that light can't escape</div></div>
<div class="beat b2"><h2>The Analogy</h2><div class="term">A funnel in spacetime</div></div>
<div class="beat b3"><h2>The Event Horizon</h2><div class="term">r&#8347; = 2GM / c²</div></div>
<div class="beat b4"><h2>Spaghettification</h2><div class="term">Tidal force ∝ 1 / r³</div></div>
<div class="beat b5"><h2>The Payoff</h2><div class="term">Time itself slows at the edge</div></div>
```

**The 15s cap split:** a 40s reel exceeds `create_media`'s 15s `duration` cap. For long reels, call the capability directly — the tool endpoint `/run/hyperframes-render` or raw SDK `/inference` — with `duration: 40`. Reels ≤15s can go through `create_media model_override: "hyperframes-render"`.

## Script structure (beats)

1. **Hook (0–4s)** — the curiosity gap, stated cold. "What's actually inside a black hole?"
2. **Analogy (1 beat)** — map the unfamiliar onto the familiar. "Think of spacetime as a stretched sheet."
3. **Mechanism (3 steps, 1 beat each)** — the how, one idea per beat. Each beat gets a caption card with its key term or equation.
4. **Payoff (last beat)** — the "so that's why…" that makes it click and shareable.

## Quality bar

- **Native 9:16** — every keyframe and every clip is 1080×1920, NOT post-cropped from 16:9.
- **Deterministic HTML text** — titles, terms, and equations live in the `hyperframes-render` layer, never baked into a generated image.
- **VO + full-length music** — `chatterbox-tts` narration over a ducked instrumental bed, muxed with `audio_fill:"loop"` so the score covers every second; silent — or a music tail that cuts out — is a draft.
- **45–90s** — long enough to teach one mechanism, short enough to finish.

## Watch-outs

- **fal video tier is flaky on dark prompts — mitigate, don't fight it.** Dark / violent / "black hole devouring" phrasing trips fal's content filter (422), and the video tier has real latency. Mitigations, in order: (1) prompt **on-policy, image-led** subjects — science, space, nature, abstract diagrams sidestep the filter; (2) try alternate video caps (`kling-i2v`, `pixverse-i2v`, `cosmos-3-i2v`, `ltx-q-i2v`); (3) render in **waves of ≤4–5** and poll each to *done* before firing the next; (4) fall back to the **Ken-Burns stills** path (ffmpeg `zoompan` slow-zoom on each 9:16 keyframe → 8s clips). The flagship reel honestly used the Ken-Burns fallback — it's reliable and exactly 9:16.

- **`hyperframes-render` runs agent-generated HTML in Chromium with `file://` access.** Only ever feed it **agent-authored** HTML — never paste untrusted third-party markup into the overlay. Treat the overlay document as code you wrote.

- **The lead-in trim is automatic.** `hyperframes-render` returns the video-playing portion of the composite; you don't trim a black lead-in by hand.

- **Don't put the equation in the image.** This is the whole point of the playbook. AI text rendering is the hardest known failure mode — a garbled "Schwarzchild" or a melted integral sign kills credibility. Render it in the HTML layer.

- **Facts must be right.** This is education — a confident wrong equation is worse than no video. Verify the mechanism, the terms, and the equations before narrating. The pipeline won't fact-check for you.

## Cross-surface (MCP / CLI / Webapp)

- **MCP:** `generate_project` (`aspect_ratio:"9:16"`, beats) → animate in waves of ≤4–5 (`create_media` action:"animate", `ltx-q-i2v`/`seedance-i2v` at `resolution:"auto"`) or Ken-Burns stills → `create_media` action:"tts" + action:"music" (`lyrics_prompt:"[Instrumental]"`) → `ffmpeg-concat` → `create_media model_override:"ffmpeg-mux"` with `audio_fill:"loop"` → `hyperframes-render` overlay (≤15s via `create_media model_override`, >15s via the cap directly with `duration:40`).
- **CLI:** `livepeer story "<concept> explainer, 9:16, 5 beats"` → TTS + music via `livepeer media create` → `livepeer media export` to stitch → call `hyperframes-render` for the overlay (long reels via raw `/inference duration:40`). Ken-Burns fallback via `ffmpeg zoompan` if the video tier rejects.
- **Webapp chat:** `make a movie-grade explainer about <concept> with an HTML overlay` — the agent drafts the hook→analogy→mechanism→payoff beats, renders 9:16 b-roll, and offers the timed-overlay finishing as a follow-up.

## Execution mode (autopilot / director / auto)

This is a multi-scene project, so pass a `mode` to `generate_project` to control the **pre-finish gate** (after every scene renders, before delivery): `autopilot` (certain brief → ship in one call, no pause), `director` (always pause for a human pick/steer before delivery), or `auto` (recommended — ship UNLESS the auto-critic is unsure: score < `confidence_threshold` (default 0.8), verdict `iterate`, or a flagged shot). Setting `mode` runs the project async (returns a `job_id`); resume a director/auto pause with `review_checkpoint` + `resume_from_checkpoint` (decision `continue` to deliver / `abort` to discard). Omit `mode` for legacy straight-through behavior.
