Merge pull request #2109 from heygen-com/fix/prompt-guide-validation-bugs

docs: Prompt Guide as a novice-to-capstone arc + text corrections from validation
This commit is contained in:
Vance Ingalls
2026-07-30 05:09:56 -07:00
committed by GitHub
47 changed files with 3571 additions and 314 deletions
+2
View File
@@ -37,6 +37,8 @@ You can embed one composition inside another in two ways: loading from an extern
<Tab title="External file">
Reference another HTML file with `data-composition-src`. The framework automatically fetches the file, extracts the `<template>` content, mounts it, executes scripts, and registers the timeline.
`data-composition-src` paths resolve relative to the **project root**, not the referencing file — a nested composition one level deep still writes `compositions/foo.html`, never `../compositions/foo.html`.
```html index.html
<div
id="el-5"
+85 -2
View File
@@ -71,6 +71,83 @@
"concepts/determinism"
]
},
{
"group": "Prompt Guide",
"pages": [
"prompting/overview",
{
"group": "Level 1 — Your first video",
"pages": [
"prompting/product-launch",
"prompting/explainers",
"prompting/code-and-prs",
"prompting/captions-and-talking-heads",
"prompting/music-and-slideshows",
"prompting/motion-graphics"
]
},
{
"group": "Level 2 — Control",
"pages": [
"prompting/anatomy",
"prompting/specification-dial",
"prompting/vocabulary",
"prompting/visual-specs",
"prompting/examples"
]
},
{
"group": "Level 3 — Life",
"pages": [
"prompting/motion",
"prompting/transitions"
]
},
{
"group": "Level 4 — Substance",
"pages": [
"prompting/code-blocks",
"prompting/data-and-maps",
"prompting/overlays-and-lower-thirds",
"prompting/captions-catalog",
"prompting/generated-artwork",
"prompting/vfx-and-liquid-glass",
"prompting/runtimes-and-3d"
]
},
{
"group": "Level 5 — Voice and sound",
"pages": [
"prompting/media-and-audio"
]
},
{
"group": "Level 6 — Scale",
"pages": [
"prompting/design-systems",
"prompting/variables-and-templating",
"prompting/storyboards",
"prompting/editing-existing-videos",
"prompting/iterating",
"prompting/recreating-references",
"prompting/rendering-and-output",
"prompting/remotion-migration"
]
},
{
"group": "Level 7 — Capstone",
"pages": [
"prompting/capstone"
]
},
{
"group": "Appendix",
"pages": [
"prompting/rules-and-anti-patterns"
]
}
]
},
{
"group": "Guides",
"pages": [
@@ -89,7 +166,6 @@
"guides/claude-design",
"guides/claude-design-send-to-hyperframes",
"guides/open-design",
"guides/prompting",
"guides/hyperframes-vs-remotion",
"guides/gsap-animation",
"guides/keyframes",
@@ -413,5 +489,12 @@
"socials": {
"github": "https://github.com/heygen-com/hyperframes"
}
}
},
"redirects": [
{
"source": "/guides/prompting",
"destination": "/prompting/overview",
"permanent": true
}
]
}
+1 -1
View File
@@ -135,7 +135,7 @@ HyperFrames projects created with `npx hyperframes init` scaffold both `CLAUDE.m
## Next steps
<CardGroup cols={2}>
<Card title="Prompting guide" icon="message" href="/guides/prompting">
<Card title="Prompting guide" icon="message" href="/prompting/overview">
Vocabulary and patterns that produce better compositions.
</Card>
<Card title="Catalog" icon="grid-2" href="/catalog/blocks/data-chart">
+1 -1
View File
@@ -143,7 +143,7 @@ Then iterate:
## Next steps
<CardGroup cols={2}>
<Card title="Prompt Guide" icon="message" href="/guides/prompting">
<Card title="Prompt Guide" icon="message" href="/prompting/overview">
More prompt patterns for HyperFrames across Claude Code, Claude Design, and other agents.
</Card>
<Card title="@hyperframes/player" icon="play" href="/packages/player">
+1 -1
View File
@@ -175,7 +175,7 @@ Authorize via OAuth when prompted. See the [MCP guide](/guides/mcp) for full det
## Next steps
<CardGroup cols={2}>
<Card title="Prompting guide" icon="message" href="/guides/prompting">
<Card title="Prompting guide" icon="message" href="/prompting/overview">
Vocabulary and patterns that produce better compositions.
</Card>
<Card title="Catalog" icon="grid-2" href="/catalog/blocks/data-chart">
+1 -1
View File
@@ -320,7 +320,7 @@ The MCP is a HeyGen-hosted product that wraps:
<Card title="Quickstart" href="/quickstart">
Try HyperFrames locally with the open-source CLI.
</Card>
<Card title="Prompting guide" href="/guides/prompting">
<Card title="Prompting guide" href="/prompting/overview">
Tips for getting the best results when working with AI agents.
</Card>
<Card title="Catalog" href="/catalog/blocks/data-chart">
+1 -1
View File
@@ -169,7 +169,7 @@ Then iterate the same way as the Claude Design path:
<Card title="Claude Design Guide" icon="message" href="/guides/claude-design">
The closed-source flavor of the same workflow — useful when you don't have a CLI on your laptop.
</Card>
<Card title="Prompt Guide" icon="message" href="/guides/prompting">
<Card title="Prompt Guide" icon="message" href="/prompting/overview">
More prompt patterns for HyperFrames across Claude Code, Claude Design, Open Design, and other agents.
</Card>
</CardGroup>
+3 -3
View File
@@ -141,7 +141,7 @@ npx hyperframes transcribe narration.wav
| `narration.txt` | The exact spoken text with pronunciation substitutions applied (`API` → `A P I`, `$2T` → `two trillion`). Distinct from `SCRIPT.md` so you can regenerate the audio later with a different voice without redoing the substitutions. |
| `transcript.json`| `[{ text, start, end }]` for every word. Every later step reads this for timing. |
Hyperframes ships multiple TTS adapters (Kokoro, ElevenLabs, HeyGen); see [`/media-use`](/guides/prompting) for the skill that picks one. After generating audio, update `STORYBOARD.md` with the real beat boundaries from `transcript.json`.
Hyperframes ships multiple TTS adapters (Kokoro, ElevenLabs, HeyGen); see [`/media-use`](/guides/skills#domain-skills-loaded-on-demand) for the skill that picks one. After generating audio, update `STORYBOARD.md` with the real beat boundaries from `transcript.json`.
**Gate:** `narration.wav`, `narration.txt`, and `transcript.json` exist. `STORYBOARD.md` beat timings reference real timestamps, not estimates.
@@ -153,7 +153,7 @@ This is where the storyboard becomes runnable HTML. Each composition is a self-c
For multi-beat videos, spawn a focused sub-agent per beat. Each one gets fresh context, the storyboard section for its beat, the asset paths it needs, and the relevant technique references. That produces noticeably better output than building every beat in one long-running context.
After each composition is built, run a self-review for layout, asset placement, and animation quality. The [`/hyperframes-core`](/guides/prompting) skill encodes the composition rules — required `class="clip"` attributes, `data-*` attribute semantics — and [`/hyperframes-animation`](/guides/prompting) covers GSAP timeline registration and adapter registries.
After each composition is built, run a self-review for layout, asset placement, and animation quality. The [`/hyperframes-core`](/guides/skills#domain-skills-loaded-on-demand) skill encodes the composition rules — required `class="clip"` attributes, `data-*` attribute semantics — and [`/hyperframes-animation`](/guides/skills#domain-skills-loaded-on-demand) covers GSAP timeline registration and adapter registries.
**Gate:** Every composition is self-reviewed. No overlapping elements, no misplaced assets, no static images sitting unanimated.
@@ -209,7 +209,7 @@ For a 5-second one-shot animation, a single hand-authored composition is fine; t
<Card title="Website to Video" icon="globe" href="/guides/website-to-video">
The full website-to-video guide built on this pipeline.
</Card>
<Card title="Prompting" icon="comment" href="/guides/prompting">
<Card title="Prompting" icon="comment" href="/prompting/overview">
How to invoke the pipeline through your AI agent.
</Card>
<Card title="Launch Videos" icon="rocket" href="/launch-videos">
-297
View File
@@ -1,297 +0,0 @@
---
title: Prompt Guide
description: "How to prompt Claude Code, Cursor, Codex, Google Antigravity, GitHub Copilot CLI, and other AI agents to author Hyperframes compositions — with copy-pasteable examples and vocabulary tables."
---
Hyperframes is built for AI agents — compositions are plain HTML, the CLI is non-interactive, and the framework ships [skills](https://github.com/vercel-labs/skills) that teach agents the patterns docs alone don't cover. This guide shows how to prompt agents effectively once skills are installed — the vocabulary that changes output, the iteration patterns that save time, and the rules that prevent breakage.
## One-time setup
Install the skills in your project (or globally for your agent):
```bash
npx skills add heygen-com/hyperframes
```
The installer shows a picker. Select the **core skills** below — every project needs them. In Claude Code, restart the session after installing; the skills register as **slash commands**. Start at `/hyperframes`: it orients you to the whole surface and routes "make me a video" requests to the right workflow.
**Core skills — install all of these**
| Slash command | What it loads |
| ----------------------- | -------------------------------------------------------------------------- |
| `/hyperframes` | **Read first.** The entry skill — capability map + video router; sends "make me a video" intent to the right workflow |
| `/hyperframes-core` | Composition contract — HTML structure, `data-*` attributes, clips, tracks |
| `/hyperframes-animation`| All animation — motion rules, scene blueprints, transitions, and the runtime adapters (GSAP, Lottie, Three.js, Anime.js, CSS, WAAPI, TypeGPU) |
| `/hyperframes-creative` | Creative direction — design spec, palettes, typography, narration, beats |
| `/hyperframes-cli` | Dev-loop CLI — `init`, `lint`, `validate`, `inspect`, `preview`, `render`, `doctor` |
| `/media-use` | Asset preprocessing — `tts`, `transcribe`, `remove-background` |
| `/hyperframes-registry` | Block and component installation via `hyperframes add` |
| `/general-video` | The general authoring workflow — fallback for any video that doesn't match a specific workflow below |
**Optional workflows — add the ones that match your inputs** (`/hyperframes` routes to whichever you've installed)
| Slash command | Input → output |
| ------------------------ | --------------------------------------------------------------------------- |
| `/product-launch-video` | Any website URL / brief / script → launch or promo video, or a site tour / showcase |
| `/faceless-explainer` | Arbitrary text (no URL) → faceless explainer with its own TTS narration |
| `/pr-to-video` | A GitHub PR → code-change explainer |
| `/embedded-captions` | An existing talking-head video → the same footage with captions / subtitles |
| `/talking-head-recut` | An existing talking-head video → footage packaged with designed graphic cards |
| `/motion-graphics` | A short, unnarrated, design-led motion graphic (logo sting, kinetic type, stat / chart) |
| `/music-to-video` | A music track (audio file or video) → a beat-synced video (lyric, slideshow, or kinetic promo) |
| `/slideshow` | A presentation / pitch deck / interactive deck — discrete slides, fragment reveals, branching |
| `/remotion-to-hyperframes` | Port an existing Remotion (React) composition to HyperFrames HTML |
<Tip>
To skip the picker and install everything (core + every workflow) in one shot, run `npx skills add heygen-com/hyperframes --all`. And start Hyperframes prompts with `/hyperframes` (or invoke the skill another way for non-Claude agents) — it loads the routing + composition context explicitly so the agent picks the right workflow and gets the rules right the first time.
</Tip>
## Claude Design
Claude Design uses a different setup. Download [`claude-design-hyperframes.md`](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/claude-design-hyperframes.md) from GitHub (click the ↓ button), then **attach it to your chat** (don't paste the URL — file attachments produce better output):
```text
Use the attached skill. 25-second LinkedIn video for my startup.
Problem: Sales teams waste 3 hours/day on manual CRM updates.
Solution: AutoCRM — AI that logs every call, email, and meeting.
Traction: 200+ teams, $1.2M ARR, 18% MoM growth.
CTA: autocrmhq.com
```
Claude Design produces a valid first draft (brand identity, scene content, animations, transitions). Download the ZIP and refine in any AI coding agent with `npx hyperframes preview` running. See the [Claude Design guide](/guides/claude-design) for the full workflow.
## The two prompt shapes
Most successful Hyperframes prompts fall into one of two shapes.
### Cold start — describe the video
You tell the agent what you want from scratch. Best for greenfield work where you have the creative direction in your head.
> Using `/hyperframes`, create a 10-second product intro with a fade-in title over a dark background and subtle background music.
> Make a 9:16 TikTok-style hook video about [topic] using `/hyperframes`, with bouncy captions synced to a TTS narration.
Cold-start prompts work best when you specify:
- **Duration** (e.g. "10 seconds", "30s", "5 scenes of 3s each")
- **Aspect ratio** ("16:9", "9:16 vertical", "1:1 square") — defaults to 1920x1080 otherwise
- **Mood / style** ("minimal Swiss grid", "warm grain analog", "high-energy social")
- **Key elements** (title, lower third, captions, background video, music)
### Warm start — turn context into a video
You give the agent something to work with — a URL, a doc, a CSV, a transcript — and ask it to synthesize that into a video. This is where Hyperframes shines because the agent does the research/summarization step *and* the production step in one flow.
> Take a look at this GitHub repo https://github.com/heygen-com/hyperframes and explain its uses and architecture to me using `/hyperframes`.
> Summarize the attached PDF into a 45-second pitch video using `/hyperframes`.
> Read this changelog and turn the top three changes into a 30-second release announcement video using `/hyperframes`.
> Turn this CSV into an animated bar chart race using `/hyperframes`.
Warm-start prompts produce richer, more grounded videos because the agent is writing about *something specific* instead of inventing copy.
## Iterating
Hyperframes is a conversation. After the first render, talk to the agent the way you'd talk to a video editor — don't re-prompt from scratch:
> Make the title 2x bigger.
> Swap to dark mode.
> Add a fade-out at the end and a lower third at 0:03 with my name and title.
> The captions are too small and they overlap the lower third. Move them up and shrink them.
> Replace the background music with `assets/track.mp3`.
The agent already has the composition open and the skills loaded — small targeted edits produce better results than long re-specifications.
## Vocabulary that changes output
The skills map natural-language adjectives to specific framework settings. Using the right word gets you the right result without specifying technical details.
### Motion & easing
Describe how motion should *feel* and the agent picks the matching GSAP ease:
| Say this | Agent uses | Feels like |
| ----------- | ---------------- | ------------------------------ |
| smooth | `sine` / `power1`| Natural deceleration |
| snappy | `power4.out` | Quick and decisive |
| bouncy | `back.out` | Overshoots then settles |
| springy | `elastic.out` | Oscillates into place |
| dramatic | `expo.out` | Fast start, long glide |
| dreamy | `sine.inOut` | Slow, symmetrical |
**Timing shorthand:** fast (0.2s) = energy, medium (0.4s) = professional, slow (0.6s) = luxury, very slow (12s) = cinematic.
### Caption tones
Describe the *energy* of your captions and the agent picks matching typography, size, and animation:
| Tone | Typography | Animation | Size range |
| ------------ | ---------------------- | ------------ | ---------- |
| Hype | Heavy weight fonts | Scale-pop | 7296px |
| Corporate | Clean sans-serif | Fade + slide | 5672px |
| Tutorial | Monospace | Typewriter | 4864px |
| Storytelling | Serif | Slow fade | 4456px |
| Social | Rounded, playful | Bounce | 5680px |
```
"Hype-style captions with scale-pop"
"Calm, elegant subtitles with slow fades"
"Karaoke-style word highlighting"
```
Per-word styling also works:
```
"Make brand names larger with accent color"
"Add bounce to emotional keywords"
"Highlight numbers differently"
```
### Transitions
Every multi-scene composition benefits from transitions. Describe the energy level:
| Energy | CSS option | Shader option |
| ------- | ---------------- | ------------------- |
| Calm | Blur crossfade | Cross-warp morph |
| Medium | Push slide | Whip pan |
| High | Zoom through | Glitch, ridged burn |
Or describe by mood:
```
"Warm transitions for this wellness brand"
"Cold, clinical transitions for tech"
"Playful bouncy transitions"
"Dramatic zoom for the reveal"
```
### Audio-reactive animation
Map audio frequency bands to visual properties. The agent uses these defaults:
| Audio band | Maps to | Visual effect |
| ---------- | --------- | ------------------- |
| Bass | `scale` | Pulse on the beat |
| Treble | `glow` | Shimmer intensity |
| Amplitude | `opacity` | Breathing |
| Mids | `borderRadius` | Shape morphing |
```
"Make the text pulse with the beat"
"Add bass-driven scale to the logo"
"Create glow that responds to treble"
```
<Tip>
Keep audio-reactive effects subtle for text (36% intensity). Go bigger for backgrounds (1030%).
</Tip>
### Marker highlights
Hand-drawn emphasis effects for text:
| Mode | Effect | Best for |
| ----------- | ------------------ | ------------- |
| `highlight` | Marker sweep | Key phrases |
| `circle` | Hand-drawn ellipse | Single words |
| `burst` | Radiating lines | Hype moments |
| `scribble` | Chaotic scratch | Rough emphasis|
| `sketchout` | Cross-hatch lines | Crossing out |
```
"Add a marker highlight sweep on 'revolutionary'"
"Circle this keyword with hand-drawn effect"
"Add burst lines around 'AMAZING'"
```
### Text-to-speech voices
HyperFrames supports three TTS providers: **HeyGen** (Starfish voices, requires sign-in via `npx hyperframes auth`), **ElevenLabs** (requires API key), and **Kokoro** (free, runs locally, no API key needed). The agent asks which provider to use — or picks automatically in autonomous mode. Describe the content and the agent picks a voice, or request one directly:
| Content type | Kokoro voices |
| ------------- | -------------------------- |
| Product demo | `af_heart`, `af_nova` |
| Tutorial | `am_adam`, `bf_emma` |
| Marketing | `af_sky`, `am_michael` |
```
"Generate narration for this script"
"Create voiceover with a professional female voice"
"Add TTS with British male voice at 1.1x speed"
"Use HeyGen TTS for this narration"
```
### Rendering quality
| Quality | Use for |
| ---------- | ------------------------ |
| `draft` | Fast iteration |
| `standard` | Review and feedback |
| `high` | Final delivery |
```
"Quick draft render"
"Render at high quality"
"Export as transparent WebM"
```
## Rules to know
The skills enforce these automatically, but if you hand-edit compositions or debug issues, these are the rules that matter:
1. **Register all timelines** on `window.__timelines` — the renderer can't seek animations it doesn't know about.
2. **Video elements must be `muted`** — audio goes in separate `<audio>` elements so the renderer can mix it.
3. **No `Math.random()`** — random values produce different frames on each render, breaking determinism. Use a seeded PRNG (e.g. mulberry32) if you need pseudo-random values.
4. **Synchronous timeline construction** — no `async`/`await` or `fetch()` during GSAP timeline setup.
5. **Timed elements need `class="clip"`** — plus `data-start`, `data-duration`, and `data-track-index`.
6. **Add entrance animations to every scene** — elements appearing without animation feel broken on video.
7. **Add transitions between scenes** — jump cuts between scenes are almost always unintentional in composed video.
<Warning>
Rules 15 are technical requirements — breaking them produces incorrect renders. Rules 67 are best practices that the skills apply by default. You can override them when you have a reason to.
</Warning>
## Anti-patterns
Things that cause friction (or wrong output):
- **Don't ask for React / Vue components.** Hyperframes compositions are plain HTML with `data-*` attributes and a GSAP timeline. Asking for "a React component for the intro" forces the agent to translate later.
- **Don't ask for 4K or 60fps unless you need it.** Defaults (1920×1080, 30fps) render fast and look great. Higher specs slow rendering meaningfully.
- **Don't skip the slash command.** Without `/hyperframes`, the agent may guess at HTML video conventions instead of using the framework's actual rules (`class="clip"` on timed elements, `window.__timelines` registration, etc.).
- **Don't paste long error logs into the prompt without context.** Run `npx hyperframes check` first — lint catches structural issues, validate catches runtime errors (JS exceptions, missing assets, contrast problems).
- **Don't assume the agent knows your assets.** Mention file paths explicitly (`assets/intro.mp4`, `assets/logo.png`) — the agent will check what's there but a hint speeds it up.
## Recommended workflow
1. `npx hyperframes init my-video` — scaffold a project (skills install automatically)
2. Open the project in Claude Code (or Cursor / Codex)
3. Prompt with `/hyperframes` and one of the shapes above
4. `npx hyperframes preview` — watch in the browser as the agent edits
5. Iterate with small targeted prompts
6. `npx hyperframes render --output final.mp4` when you're happy
## Next steps
<CardGroup cols={2}>
<Card title="Quickstart" icon="rocket" href="/quickstart">
Build and render your first video
</Card>
<Card title="Common Mistakes" icon="circle-exclamation" href="/guides/common-mistakes">
Pitfalls the linter can't catch
</Card>
<Card title="GSAP Animation" icon="wand-magic-sparkles" href="/guides/gsap-animation">
Add fade, slide, scale, and custom animations
</Card>
<Card title="Catalog" icon="grid-2" href="/catalog/blocks/data-chart">
50+ ready-to-use blocks and components
</Card>
</CardGroup>
+2 -3
View File
@@ -217,14 +217,13 @@ For GSAP animation, register one paused timeline per composition:
const tl = gsap.timeline({ paused: true });
tl.from("#title", { opacity: 0, y: 40, duration: 0.6 });
tl.set({}, {}, 5); // keeps the timeline at least 5 seconds long
window.__timelines["main"] = tl;
</script>
```
<Warning>
If a video cuts off early, check that the GSAP timeline is at least as long as the intended edit. The final `tl.set({}, {}, 5)` pattern is the fix.
Render duration comes from `data-duration` on the composition and clips, not from GSAP timeline length. If a video cuts off early, check those timing attributes. Do not pad the timeline with an empty `tl.set()` sentinel.
</Warning>
## Render Presets
@@ -285,7 +284,7 @@ For editor-facing changes, keep `npx hyperframes preview` running, then have the
| Need to share editable project | `npx hyperframes publish` |
<CardGroup cols={2}>
<Card title="Prompt Guide" icon="wand-magic-sparkles" href="/guides/prompting">
<Card title="Prompt Guide" icon="wand-magic-sparkles" href="/prompting/overview">
How to direct AI agents to build better videos
</Card>
<Card title="Timeline Editing" icon="timeline" href="/guides/timeline-editing">
+107
View File
@@ -0,0 +1,107 @@
---
title: Anatomy of a one-shot prompt
description: "The six-part skeleton — route, spec, beats, copy, technique, negatives — that removes the decisions agents most often get wrong."
---
Your Level 1 prompts worked because the workflow filled the gaps for you — the palette, the pacing, the structure. This is Level 2: the skeleton is how you take that control yourself.
The [copy-paste examples](/prompting/examples) share one skeleton. Each part removes a decision agents most often get wrong on a first try:
```text
[route] /motion-graphics
[spec] 8-second 1920x1080 video.
[beats] Beat 1 (0-4s): ... Beat 2 (4-5s): ... Beat 3 (5-8s): ...
[copy] the exact on-screen text, quoted
[technique] Adapt the `code-typing` and `vfx-shatter` registry blocks.
[negatives] No narration, no image or media files.
```
- **Route** with a slash command — it loads the right workflow and the framework rules.
- **Spec** duration and resolution up front. Defaults are 1920x1080 at 30fps.
- **Beats** get timestamps. Include pacing instructions ("then hold on the blinking cursor") — agents skip breathing room unless told.
- **Copy** is quoted exactly, with `/` for line breaks. Unquoted copy gets paraphrased.
- **Technique**: name registry blocks exactly as they appear in the [catalog](/catalog/blocks/data-chart) — they're adapted starting points, not drop-ins (blocks ship with demo content the agent rewrites to your beat, so naming one pins the *technique*). Pin a technique wherever the default choice can fail (see [the specification dial](/prompting/specification-dial)).
- **Negatives** close the gaps: "no narration" is not "silent" — if you want no sound at all, say "no audio". Avoid ambiguous phrases like "no external assets" (CDN-loaded runtimes are normal; say "no image or media files" if that's what you mean).
Assembled:
> /motion-graphics Make an 8-second 1920x1080 video. Beat 1 (0-4s): dark macOS terminal types "npx skills add heygen-com/hyperframes" character by character, then hold on the blinking cursor. Beat 2 (4-5s): the terminal shatters into fragments. Beat 3 (5-8s): bold white kinetic text on black slams in word by word, snappy: "YOU JUST MADE THIS / WITH HYPERFRAMES." Adapt the `code-typing` and `vfx-shatter` registry blocks; hand-author the kinetic text. No narration, no image or media files.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/validate-anatomy-terminal.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
<Note>
The skeleton isn't just prompting style — the framework enforces a version of it on itself. Since the composition-structure mandate (`0aaac7aa3`, PR #2599), a `hyperframes feedback` report on a visual defect requires a `COMPOSITION_STRUCTURE:` block — element census, attribute presence, timeline shape — auto-filled from the composition's own HTML; the CLI soft-warns (never blocks) when it's missing. Same idea as the six parts above: name the shape explicitly, and both you and the framework reason about it faster.
</Note>
## The beat formula
The skeleton above structures the *ask*. Inside each beat, describe the *content* with the same five slots every time:
```text
[element] what's on screen a giant number · the tweet card · "SHOWREEL"
[motion] what it does counts up with an odometer roll · slides up · fades in per letter
[layout] where it sits top-center · filling the lower half · bottom-right
[style] how it looks dark navy, green accent · 8-bit pixel · thin geometric sans
[timing] when, inside the beat at 1s · over 3s, easing out as it lands · staggered 0.1s apart
```
One sentence per element, slots in any order: *"a giant number **(element)** counts up to $4.2M with an odometer roll **(motion)**, easing out as it lands **(timing)**, centered **(layout)** in green on dark navy **(style)**."* Elements you don't describe, the agent designs — which is fine when you trust its taste and a drift risk when you don't (see [the specification dial](/prompting/specification-dial)).
<Tip>
Beat-timestamped prompting — `Beat 2 (4-5s): ...` — is HyperFrames' native language. Diffusion video models bolt time segmentation on top of a single clip; here every beat maps directly to a timed clip in the composition, so per-beat descriptions translate losslessly. Use timestamps liberally.
</Tip>
## Common rewrites
The fixes that come up most, as before/after pairs — each exists because of how the engine actually behaves:
**Freezing the hold.** Compositions hold their final state, so a literal "hold" renders a frozen frame — the single biggest cheap-motion tell.
- ❌ `then everything holds motionless to the end`
- ✅ `then everything settles into a gentle ambient idle (breathing scale, slow drift)`
**A duration with no tail.** The subtler version of the same mistake: state a runtime longer than the beats you describe and the leftover seconds become a dead hold by default. Validating this guide's chart example turned up a 12-second ask carrying about five seconds of described content — the builder had to invent the remaining seven. Either shorten the ask to the content, or say what the tail does.
- ❌ `12-second video. [five seconds of beats]`
- ✅ `12-second video. [beats] … then hold ~5s on the finished chart with the leader pulsing once and a slow ambient drift`
**Simultaneity collisions.** Two things "at 4s" overlap for a few frames; the renderer does exactly what you wrote.
- ❌ `at 4s the counter fades out and READY stamps in`
- ✅ `the counter fades out fully by 4.2s; at 4.2s READY stamps in`
**Vague negatives.** "No external assets" is ambiguous — CDN-loaded runtimes are normal infrastructure.
- ❌ `no external assets`
- ✅ `no image or media files`
**Prose where copy belongs.** Unquoted text gets paraphrased; quoted text renders verbatim.
- ❌ `show a tagline about shipping faster`
- ✅ `tagline: "Ship faster."`
**Format-blind numbers.** An odometer needs fixed digit columns — "counts $0 → $4.2M" forces an awkward "$0.0M" start.
- ❌ `counts from $0 to $4.2M`
- ✅ `counts up to $4.2M`
## The framework's own vocabulary
The skeleton above is how you ask. This is what the agent writes, and it's worth recognizing on sight — when you read a composition, review a diff, or get a lint error, these four are the load-bearing pieces:
| In the file | What it means |
| --- | --- |
| `class="clip"` | Marks a timed section. The framework owns its visibility — never animate a clip's own opacity or `display` to hide it. |
| `data-start` / `data-duration` / `data-track-index` | When the clip begins, how long it runs, and which track it sits on. Two clips on the same track index must not overlap in time. |
| `window.__timelines["<id>"]` | Every GSAP-driven composition registers exactly one paused timeline here, built synchronously at load. The renderer seeks that timeline frame by frame — which is why nothing may depend on wall-clock time or unseeded randomness. Other adapters, including Lottie, CSS animations, and WAAPI, register through their own seekable runtime contracts. |
| `<video muted>` + a separate `<audio>` | Video elements render muted and their sound rides a sibling audio element, so the mixer can duck and balance it independently. |
You rarely write these by hand, but naming them in a prompt is legitimate and sometimes necessary — "put the captions on their own track index," "keep it one composition file, one timeline" — and it's the vocabulary every error message uses. The full contract lives in the `/hyperframes-core` skill; the [appendix](/prompting/rules-and-anti-patterns) carries the rules that most often bite.
<Note>
**Capstone thread** — the [Level 7 film](/prompting/capstone) makes this table its scenery. In the Mount region the timeline wire splits into three track lanes and clip chips seat onto them, tagging themselves with the very attributes above — the framework's own vocabulary, staged as a set. And the whole prompt is this chapter's skeleton at maximum scale: route, spec, beats (its nine regions), copy quoted exactly, technique pinned per region, and a hard negative list.
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> **Mount (714s).** Traveling along the wire: it splits into three parallel track lanes; clip chips slide in along the direction of travel and seat onto the lanes on their spoken cues (audio track chip, video track chip, our protagonist chip among them). Small mono labels (`data-start`, `data-track-index`) tag themselves onto chips as the camera passes. The lanes merge back to one wire as the camera accelerates out.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-mount.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause, rendered — the region cut from the finished film.*
*Next: [The specification dial](/prompting/specification-dial) — how much of the skeleton to fill in, and when density stops being optional.*
+133
View File
@@ -0,0 +1,133 @@
---
title: Capstone — every technique, one journey
description: "One prompt, one continuous camera move, every technique in this guide — the viewer rides the timeline itself from typed markup to rendered MP4."
---
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-timeline-default.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*"The Timeline" — 1920x1080, ~62 seconds, one prompt, one unbroken camera move, unedited.*
The prompt targeted roughly 60 seconds; the delivered render runs 1:02 because the final lockup holds for a two-second breath.
If you read the guide in order, you have already seen every piece of this film. Each region was cut out and embedded in the chapter that teaches its technique — the typed opening in [Code animations](/prompting/code-blocks), the chart and map in [Data and maps](/prompting/data-and-maps), the footage pipeline in [Media and audio](/prompting/media-and-audio), the glass and mural in [VFX](/prompting/vfx-and-liquid-glass) and [Generated artwork](/prompting/generated-artwork), the beat grid in [Music](/prompting/music-and-slideshows), the 3D coil in [Runtimes and 3D](/prompting/runtimes-and-3d), the continuous camera in [Motion](/prompting/motion), the seeded finale in [Rendering and output](/prompting/rendering-and-output). This page is where the pieces become one composition, and the prompt below is the glue: every paragraph of it is a chapter of this guide restated as a binding clause.
The concept is the product: the viewer travels along one literal timeline — the thing HyperFrames is built on — from the first typed character to the finished MP4. Everything in it is true: real catalog counts, real cloud regions, real product surfaces, footage staged as the material the tool processes. And the film has no scenes in the slideshow sense — it is one continuous camera journey through one wide world, which is the chapter's biggest lesson.
## The cinematography contract
Two earlier builds of this capstone passed every gate and still failed review — first as sparse frames floating in space, then as a well-dressed slideshow: discrete scenes, hard cuts, motion confined inside each frame. What fixed it wasn't more animation; it was a **cinematography contract** written into the prompt:
- **One continuous camera, dwell-and-sweep.** The whole film is a single wide world the camera dollies across in one unbroken move — but the rhythm matters as much as the continuity: the camera SWEEPS between regions, then eases to a genuine full stop for a couple of seconds while each region's main point lands (the count completing, the kicker stamping, the spoken keywords), then accelerates away. The dwell is what gives the sweep meaning — without the rest, emphasis dies mid-motion. During a dwell the world keeps resolving; the camera rests, the film never freezes. Scene changes happen by *arriving*: the next region is already visible at the frame edge before the camera reaches it, and the previous one exits by parallax, not by fade.
- **Persistent elements that tell the story.** Three things never leave frame: the **wire** (a single continuous line that is the timeline itself — it becomes the chart baseline, the map arc, the waveform, the 3D coil), the **ruler** (a frame-ruler chrome strip whose playhead tracks the film's real playback time), and the **protagonist** — the `<div class="clip">` typed in the opening, which rides the wire through every region and is the thing that finally renders at the end.
- **Anti-slideshow negatives.** No element cluster that fades in centered, sits, and fades out; no region that reads as a static slide the camera happens to pass; no motion that is just idling in place.
Like every rule in this film, it only exists because it was demanded in writing — the linter can verify a timeline, but only the prompt can demand cinematography.
## The full prompt, verbatim
This is the full prompt, nothing paraphrased or trimmed. Several of its clauses — the chip's hard bind to the wire's path geometry, the mural's pinned/iris-revealed/fade-out behavior, and the Depth region's 3D contract (the coil winding out of and back onto the wire, the chip passing through the loops and behind the form with true depth occlusion instead of floating over the geometry) — were added after frame review caught the builder drifting on exactly those points, and the film was patched to comply: the same catch-and-tighten loop [Iterating](/prompting/iterating) teaches, applied to the prompt itself so the next run can't regress.
> /general-video Make a ~60-second 1920x1080 promo film for HyperFrames — the open-source framework that turns hand-authored HTML and GSAP animation into deterministic, frame-accurate video. The film's concept IS the product: the viewer travels along one literal timeline — the thing HyperFrames is built on — from the first typed character to the finished MP4. Everything true, nothing invented: real catalog counts, real cloud regions, real product surfaces.
>
> **Design system: the provided `frame.md` is the single source of brand truth — read it first and obey it exactly.** It is the real HyperFrames brand (by HeyGen), dark-first: flat near-black ground (`--page-bg` `#0a0a0a`), greyscale text hierarchy (`#fafafa` / `#a1a1aa` / `#71717a`), and **mint `#3CE6AC` used sparingly as the single accent — mint means go / primary / active**. The mint→cyan gradient (`#3CE6AC → #00E3FF`) appears in exactly one place in the whole film: the final CTA lockup. Typography from the file: **ABC Solar Display 700** for the display lockups ("WRITE HTML." / "WRITE HTML. RENDER VIDEO."), **TT Norms Pro** for everything else, **TT Norms Pro Mono** for all machine-flavored text (code, commands, timecodes, counts, region labels) with `tabular-nums`. Sentence case everywhere except small uppercase letter-spaced eyebrows. Glass surfaces use the file's `--glass-*` tokens with real backdrop blur. Radii, borders, shadows, and easing curves come from the file's tokens (`--ease-out-quint` entrances, `--ease-spring` count-bumps). Font files are provided as woff2 assets — bundle them, don't fetch from the network.
>
> **Cinematography contract (the most important section — this is what the film lives or dies on):**
> - ONE continuous camera journey with a dwell-and-sweep rhythm. The whole film is a single wide world (one large scene space) that the camera dollies across — left to right, with occasional push-ins and one descent — in one unbroken move with NO hard cuts anywhere except inside the two sanctioned seam moments named below. The rhythm: the camera SWEEPS between regions, then eases to a genuine REST at each region's hero moment — a full stop of roughly 1.52.5 seconds while the main point lands (the hero count completing, the kicker stamping, the spoken caption line, the check completing) — then accelerates away. The dwell is what gives the sweep meaning: without the rest, emphasis dies mid-motion. During a dwell the WORLD keeps resolving (counters tick, labels stamp, secondary motion continues) — the camera rests, the film never freezes.
> - Scene changes happen by ARRIVING, not cutting. The next region is already visible at the edge of frame before the camera reaches it; the previous region exits by parallax, not by fade-out.
> - NO slideshow tells: no element cluster that fades in centered, sits, and fades out; no region that reads as a static slide while the camera happens to pass; no motion that is just shaking or idling in place. Motion = the camera's travel + elements entering along the direction of travel + elements the camera hands off between regions.
> - Three persistent elements thread the ENTIRE film and are never absent:
> 1. **The wire** — a 3px ink-colored horizontal line, the literal timeline. It is the film's spine: every region hangs off it, the camera follows it, and it visibly continues into the next region at all times. It may bend, split into tracks, become a waveform, arc across a map, spiral into 3D — but it is always the same continuous line.
> 2. **The ruler** — a thin frame-ruler chrome strip across the top: tick marks, a running timecode, and a playhead marker that advances in sync with the film's actual playback time (at t=30s the playhead is at 30s). The ruler is the product's own UI language and it never leaves.
> 3. **The clip card** — the `<div class="clip">` typed in the opening travels the whole journey: it slides onto the wire as a clip chip after being typed, rides ahead of the camera between regions (handing itself off — visible leaving one region and arriving in the next), and is the thing that finally renders at the end. It is the protagonist. **The chip sits ON the wire at every moment: bind its position to the wire's actual path geometry (sample the drawn path, seek-safe), never keyframe it independently — through every bend, arc, waveform, and dive, the chip visibly hugs the line. A chip floating off-path breaks the story.**
>
> **Message, stated once:** "Write HTML. Render video." — opening hook and closing lockup. Arc: Type → Mount → Measure → Everywhere → Material → Surface → Rhythm → Depth → Render. Audience: developers evaluating a programmable video tool. Mood: focused build-up that keeps accelerating, resolving confident and warm at the render.
>
> **Direction (every region obeys it):**
> - Brand restraint per `frame.md`: the world is greyscale-on-near-black; `ground` `#0a0a0a` and mint `ink` `#3CE6AC` are declared as the two composition variables. The wire, the playhead, active chips, and each region's single hero accent are mint; everything else is the greyscale text/border hierarchy. No other hue anywhere — except cyan `#00E3FF`, which exists only inside the one sanctioned mint→cyan gradient on the final CTA.
> - VO-paced reveals: each region's elements land on their spoken cues as the camera arrives; secondary elements keep resolving while the camera is present; the region is complete just as the camera accelerates away.
> - Permanent chrome: the ruler (persistent element 2) plus a faint ink grid at ~8% opacity over the ground, everywhere in the world.
> - Density: every region fills three roles — one focal element at display scale, at least two supporting elements on their own cues, and the chrome/wire. Asymmetric compositions; display type ~a tenth of frame width; three depth layers with parallax between them (foreground elements slide past faster than the wire plane, backdrop slower — the parallax is what sells the continuous travel).
> - Negative list: no purple-blue AI gradients, no bokeh, no real browser/OS chrome screenshots, no stock photography, no third hue, no unseeded randomness, no static holds mid-film (the only near-still moment is the final lockup breath).
> - SFX from the media pipeline's library on the visual hits: keystrokes at the opening, a soft whoosh at each region handoff, ticks on counters and beat cuts, one riser into the render moment, one stamp on the lockup. All under the VO.
>
> **The journey (regions along the wire, ~timings; the camera flows through these, they are NOT cuts):**
>
> **Type (07s).** Black-on-charcoal close-up: a cursor types real HyperFrames markup character by character — `<div class="clip" data-start="0" data-duration="4">` and a `gsap.timeline({ paused: true })` line. As the typed line completes, the text's baseline extends and becomes **the wire** — the underline literally grows into the timeline and the camera begins its dolly along it. The typed div folds into a compact clip chip (persistent element 3) that drops onto the wire. Kinetic display type states "WRITE HTML." as the travel begins.
>
> **Mount (714s).** Traveling along the wire: it splits into three parallel track lanes; clip chips slide in along the direction of travel and seat onto the lanes on their spoken cues (audio track chip, video track chip, our protagonist chip among them). Small mono labels (`data-start`, `data-track-index`) tag themselves onto chips as the camera passes. The lanes merge back to one wire as the camera accelerates out.
>
> **Measure (1421s).** The wire becomes a chart baseline (same line, same thickness — no cut): adapt the **`data-chart` registry block** — six labeled bars rise from the wire itself on spoken cues while a hero numeral counts up beside them in huge tabular mono: **50+ CATALOG BLOCKS** (the real registry count; supporting chips: 19 AGENT SKILLS · 7 RUNTIME ADAPTERS — also real). The bars recede by parallax as the camera moves on; the tallest bar's top edge hands off into the next region's horizon.
>
> **Everywhere (2128s).** The wire arcs and the ground falls away: adapt the **`world-map` registry block** — the dotted world map scrolls beneath while the wire becomes a great-circle route arcing across it. Real render infrastructure, truthfully labeled: pulses light up at AWS and Google Cloud regions (US-EAST-1 · EU-WEST-1 · AP-SOUTH-1) with a "CLOUD RENDER · LAMBDA + CLOUD RUN" kicker — HyperFrames' actual deploy targets. The map fades away as the wire descends into the next region — the pulses hold their size until the fade; nothing on the map ever shrinks.
>
> **Material (2839s).** The wire threads into a working canvas where footage is the material on the timeline: a real talking-head clip (generate a short clip of a person speaking one neutral line via the media pipeline's avatar video generation — it is material being processed, not a testimonial; **transcode it to HEVC `hvc1`** so the automatic proxy subsystem carries preview) sits as a clip on the wire. The order of operations IS the story: as the camera arrives and BEFORE the person speaks, the framework mattes the footage — **the background peels away via background removal**, sliding off along the travel direction and leaving the cutout standing alone on the brand ground. THEN they speak, and the main **keywords of their own line — derived from the clip's transcription — land word-synced as huge display text BEHIND the cutout**, each keyword slamming in on its spoken moment with the subject's silhouette occluding it (the two-layer text-behind-subject plate); style the keyword type by adapting a bold **catalog caption component** (`caption-kinetic-slam` or similar) at display scale. A designed lower-third chip renders behind the subject. The clip's own audio ducks the BGM briefly; the VO resumes as the camera pulls away and the worked footage recedes by parallax.
>
> **Surface (3945s).** SANCTIONED SEAM #1: the camera pushes through an **`sdf-iris` shader transition** — the iris opens ONTO the continuation of the same wire (the wire is visible through the iris throughout; this is a lens the journey passes through, not a cut). Inside: frosted-glass inspector panels (real translucency — blur over what's behind; the product's Studio design language) hover above the wire carrying live values (`ease: power3.out`, a color token, an fps readout), over a **generated-artwork backdrop** — a flat abstract mural evoking motion trails, generated **palette-neutral (grayscale) and duotoned live by CSS** from the two variables so it re-skins with everything else. **The mural is a full-frame, screen-fixed backdrop: it never moves on screen — the widening iris circle alone reveals it already in place at full frame, it holds dead still while the wire, glass, and chip travel in front of it, and it exits by fading out. It must never slide, and the iris lens itself must render only its glowing ring — fully transparent outside the circle, no visible container.** Drifting `data-*` tags ride between mural and glass, clear of all text.
>
> **Rhythm (4552s).** The wire becomes a waveform: **resolve the BGM first, run `hyperframes beats` on it, and drive this region on the detected grid** — the waveform pulses and compact media cards (a lyric line, a photo card, a chart flash) snap onto the wire on real analyzed beats while the camera keeps traveling; each snap gets a tick SFX. At least six beat-hits. SANCTIONED SEAM #2: the beat-hits may hard-cut card content ON the beat — the sanctioned exception, because the camera itself never stops moving through them.
>
> **Depth (5256s).** The wire spirals off the flat plane into real 3D — a **Three.js scene via the frame adapter** (never CSS fake-3D): the camera descends following the wire as it coils around a rim-lit faceted form (ink material on charcoal), mono axis readouts landing on cue, then rises back to the plane with the wire leading the way out. The coil winds up out of the wire and collapses back onto it — its ends never float cut off in mid-air — and the protagonist chip joins the 3D scene for the crossing: it rides the wire straight through the coil's loops and passes behind the form with true depth occlusion, never floating over the geometry as a flat overlay.
>
> **Render (5660s).** The wire's end plugs into a render chip; the protagonist clip card from the opening arrives (callback — same chip, having traveled the whole film) and snaps into it. A **seeded confetti burst** fires — mulberry32, **seed 42, each piece holding position for exactly two frames before stepping** (stop-motion feel) — and the VO lands the honest punchline: identical on every render, because determinism is the whole point. "WRITE HTML. RENDER VIDEO." locks up with the install line `npx skills add heygen-com/hyperframes`. The camera finally eases to rest — the film's only true stillness, one breath before the end.
>
> **Copy, quoted exactly:** opening "WRITE HTML." · stats "50+ CATALOG BLOCKS", "19 AGENT SKILLS", "7 RUNTIME ADAPTERS" · map kicker "CLOUD RENDER · LAMBDA + CLOUD RUN" · closing "WRITE HTML. RENDER VIDEO." + `npx skills add heygen-com/hyperframes`. The Material region's spoken line and typed code are yours to make real and legible — no lorem ipsum, no invented product claims anywhere.
>
> **Technique, pinned:** continuous world-camera via GSAP transforms (the dolly is the film); parallax layers in every region; `data-chart`, `world-map`, `sdf-iris`, and a caption component adapted from the registry; per-character typing at the open; matched-motion handoffs at every region boundary (the wire IS the match); full footage pipeline (generated clip → HEVC → auto-proxy → background removal → lower-third → word-synced captions); real beat grid from `hyperframes beats`; real Three.js via the frame adapter; seeded two-frame-hold confetti; SFX on hits; VO-paced reveals throughout.
>
> **Variables:** expose `ground` (default `#0a0a0a`) and `ink` (default `#3CE6AC`) as composition variables on the single root file, bound via CSS custom properties everywhere (including the duotoned mural), so one `--variables` call re-skins the entire journey. It will be rendered twice: the default brand palette, and a second full render with `{"ground":"#0d1420","ink":"#c8ff3d"}`.
>
> **Architecture constraint (technical):** single composition file — one `index.html`, one variable scope. The world is one wide scene space traversed by a camera transform; regions may still be timed `class="clip"` sections synchronized to the camera's arrival (so capture stays efficient), but their positions form one continuous world and the wire + ruler + protagonist chip layers persist across 100% of the film. No `data-composition-src` sub-files.
>
> **Voice & music:** confident, energetic female narration — Kokoro `af_sky`, passed explicitly via `--voice` — riding the journey's momentum (the VO never waits for the camera; the camera arrives on the VO). Driving, beat-forward electronic BGM from the catalog at the engine's default narration-bed volume; the same track feeds the Rhythm region's beat analysis.
>
> **Negatives:** no hard cuts outside the two sanctioned seams; no static regions; no shaking or idle-in-place motion passed off as energy; no hue beyond mint (cyan only inside the one CTA gradient) — and per the brand file, no purple-blue SaaS gradients ever; no stock footage or photography; no fictional stats, projects, or testimonials — every number and label must be true of HyperFrames today; no real OS/browser chrome; no unseeded randomness; none of the film is silent.
## Walking the prompt, chapter by chapter
Every region pins a different technique, and every technique has a chapter:
| Region | What it demonstrates | Chapter that teaches it |
| --- | --- | --- |
| Type (0:000:07) | Per-character code typing; the underline that becomes the wire | [Code animations](/prompting/code-blocks), [Prompt anatomy](/prompting/anatomy) |
| Mount (0:070:14) | The timeline's own vocabulary — tracks, clips, `data-*` labels | [Prompt anatomy](/prompting/anatomy) |
| Measure (0:140:21) | `data-chart` block; the wire as chart baseline (matched motion) | [Data and maps](/prompting/data-and-maps), [Transitions](/prompting/transitions) |
| Everywhere (0:210:28) | `world-map` block; real Lambda / Cloud Run regions | [Data and maps](/prompting/data-and-maps), [Rendering and output](/prompting/rendering-and-output) |
| Material (0:280:39) | Generated clip → HEVC proxy → background removed *before* the speech → the line's keywords slam in word-synced as display text BEHIND the cutout (silhouette occlusion) → lower-third behind the subject | [Media and audio](/prompting/media-and-audio), [Caption styles](/prompting/captions-catalog), [Overlays and lower thirds](/prompting/overlays-and-lower-thirds) |
| Surface (0:390:45) | `sdf-iris` shader lens, frosted glass, palette-neutral generated mural | [Transitions](/prompting/transitions), [VFX and liquid glass](/prompting/vfx-and-liquid-glass), [When to generate artwork](/prompting/generated-artwork) |
| Rhythm (0:450:52) | Cuts on a real analyzed beat grid (`hyperframes beats`) | [Music videos and slideshows](/prompting/music-and-slideshows) |
| Depth (0:520:56) | Real Three.js via the frame adapter, wire as 3D coil; the chip joins the scene to thread the loops and pass behind the form (true depth occlusion) | [Runtimes and 3D](/prompting/runtimes-and-3d) |
| Render (0:561:02) | Seeded (42) two-frame-hold confetti — determinism as the punchline | [Motion that reads premium](/prompting/motion) (rule 8) |
The camera itself is [Motion that reads premium](/prompting/motion)'s "camera as actor" rule taken to its logical end — the camera isn't *in* the film, it *is* the film. The persistent wire/ruler/protagonist are [Storyboards](/prompting/storyboards)' callback device stretched across the whole runtime instead of two frames. And the whole thing is written in [High-fidelity looks](/prompting/visual-specs)' density: exact hexes, exact timings, named eases, quoted copy.
One more chapter runs through every pixel: the film's look isn't described in the prompt at all — the prompt points at a **`frame.md` brand file** (the real HyperFrames design system: near-black ground, greyscale hierarchy, mint as the single go/active accent, ABC Solar Display lockups, TT Norms Pro Mono for machine text, one sanctioned mint→cyan gradient on the CTA) and says *obey it exactly*. That's [Design systems and brand](/prompting/design-systems)'s whole thesis — a source of brand truth beats any adjective — demonstrated at film scale.
### Why one file, still
HyperFrames scopes `data-composition-variables` **per composition file**, so the single-file constraint from the earlier capstones carries over — one `index.html`, one variable scope, which is what lets one `--variables` flag re-skin the whole journey. The new wrinkle: the world is one wide scene space traversed by a camera transform, while regions stay timed `class="clip"` sections synchronized to the camera's arrival — the persistent layers (wire, ruler, chip) are the only elements alive for all 62 seconds, so capture stays efficient without breaking the illusion of one continuous space. See [Variables and templating](/prompting/variables-and-templating).
## The re-skin: one flag, the whole journey
```bash
hyperframes render --variables '{"ground":"#0d1420","ink":"#c8ff3d"}' --strict-variables
```
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-timeline-reskin.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*The same composition, navy/acid-green — the wire, ruler, chart, map, glass, and even the generated mural all follow the override.*
The mural trick from the previous capstone carries over: the artwork is generated **palette-neutral** (grayscale) and duotoned live by CSS layers driven by the `ground`/`ink` variables, so even the raster re-skins. Keep color in CSS and shapes in the raster whenever a template frame needs to survive a re-skin.
## How everything ties together
Read the prompt again with the guide behind you and it stops looking like one enormous ask. Region by region it is material you already have: the typed opening is [Code animations](/prompting/code-blocks), the Mount paragraph is [Prompt anatomy](/prompting/anatomy)'s framework vocabulary staged as scenery, the chart and map paragraphs are [Data and maps](/prompting/data-and-maps), the Material paragraph is [Media and audio](/prompting/media-and-audio) + [Caption styles](/prompting/captions-catalog) + [Overlays](/prompting/overlays-and-lower-thirds) run in sequence, Surface is [Transitions](/prompting/transitions) + [VFX](/prompting/vfx-and-liquid-glass) + [Generated artwork](/prompting/generated-artwork), Rhythm is [Music](/prompting/music-and-slideshows), Depth is [Runtimes and 3D](/prompting/runtimes-and-3d), and the finale is [Motion](/prompting/motion)'s rule 8. None of those clauses is new — they are the chapters, restated as requirements.
What IS new — the part you could not have written after any single chapter — is the connective tissue, and that is exactly what the rest of the prompt spends its words on:
- **The cinematography contract** fuses nine demos into one space: one camera, arrivals instead of cuts, dwell-and-sweep pacing. Remove it and you get a playlist of chapter examples; with it, the same nine pieces are a film.
- **The persistent elements do the handoffs.** The wire is the trick that makes each region *become* the next — the same line is the typed underline, the chart baseline, the map arc, the waveform, the 3D coil. The chip is a callback threaded through all nine techniques; the ruler makes the film's own medium visible.
- **One brand file governs every region** ([Design systems](/prompting/design-systems)) — so nine techniques drawn from a dozen chapters land looking like one designer made them.
- **One variable scope re-skins all of it** ([Variables and templating](/prompting/variables-and-templating)) — the single-file constraint exists so the chart, the map, the glass, the mural, and the confetti all answer to the same two variables in one `--variables` flag.
- **One soundtrack paces all of it** ([Media and audio](/prompting/media-and-audio)) — the VO cues every region's reveals ([pace reveals to the narration](/prompting/media-and-audio#pace-reveals-to-the-narration)), the same BGM that beds the narration feeds the Rhythm region's beat analysis, and one SFX plan rides the visual hits — keystrokes at the open, a whoosh per handoff, ticks on counters and beats, one riser into the render ([sound effects](/prompting/media-and-audio#sound-effects)).
That's the method this guide has been building toward: learn each technique in isolation, then write the prompt whose job is not to invent the pieces but to bind them — shared space, shared spine, shared brand, shared variables, shared clock. A chart, a map, typed markup, processed footage, 3D, glass, a shader seam, a beat grid, seeded confetti: every one traceable to a chapter you've already read. The film is what the glue makes of them — and the glue, like everything else here, is just words in the prompt.
*Next: [Rules and anti-patterns](/prompting/rules-and-anti-patterns) — keep this cheat sheet open while you build.*
@@ -0,0 +1,95 @@
---
title: Captions and talking-head footage
description: "Two ways to dress an existing talking-head clip — readable captions or designed graphic overlays — both leaving the footage itself untouched."
---
The first two rides built a video from nothing and the third built one from a diff. This one starts from footage you already have — a talking-head clip — and adds a layer on top without touching the shot itself.
## Your first win
One prompt to [`/embedded-captions`](/prompting/overview), pointed at an existing clip, is enough for a finished captioned video — no technique required yet.
Captions route by **identity**, not by mode. You pick one look from the catalog; the engine behind it is a lookup detail you never have to name. The default is a clean verbatim rail — `anchor` — with the occasional peak word composited behind the subject.
> /embedded-captions Add captions to ./interview.mp4. Use the `anchor` identity — clean verbatim rail carrying the spoken words, readable lower-third. Promote the single hardest-hitting word to an embed behind the speaker; highlight one key word in each rail line. Keep the source aspect ratio. Footage stays untouched.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/captions-anchor-rail.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above on generated avatar footage, unedited — one earned embed behind the speaker, everything else on the rail.*
The rail carries most of the text; an **embed** is the scarce, earned peak — one big word matted behind the subject at the climax, never every line. Embedding the whole transcript is the most common mistake this skill guards against.
## Two things you can add to a talking head
Both workflows take an existing talking-head / interview / podcast clip and add a layer on top. Neither edits the footage — no trims, no recolor, no reframe, no reorder. The clip plays untouched underneath; you're choosing what rides on top of it.
| You want | Route | What it adds |
| --- | --- | --- |
| The spoken words as readable text | `/embedded-captions` | Captions / subtitles — a rail, with earned climax embeds |
| Designed on-screen graphics synced to the talk | `/talking-head-recut` | Overlay cards — titles, lower-thirds, data callouts, quotes, PiP |
If the words themselves need to read, you want captions. If you want a produced look — kinetic titles, a stat callout, a pull-quote card, the speaker shrunk into a corner while a chart fills the frame — you want overlay cards. When it's genuinely both, caption first, then package; they're siblings, not substitutes.
`/embedded-captions` runs locally end to end — it transcribes and mattes the subject itself, no API key — and needs a **single-subject clip**. Multi-speaker clips or hard cuts get split per shot or refused, because the matte is one person.
## Base prompt — overlay cards
> /talking-head-recut Package ./founder-clip.mp4 with designed graphic overlay cards synced to the transcript. 9:16 portrait, warm-paper style. Open with a fullscreen kicker + title hook, drop a lower-third when she names the company, a data callout card counting up the "200+ teams / $1.2M ARR" stat, and a pull-quote card for the strongest line. Speaker stays full-bleed under the cards; shrink her into a corner PiP while the data card holds. The clip plays untouched underneath.
You describe the *cards* — their content, timing, and how the speaker shares the canvas with them (full-bleed, split, PiP, or glass overlay). The skill designs and writes each card; there's no fixed archetype list, so the overlays follow what the transcript actually says.
## The knobs that matter
What you can already steer from the prompt, before you've learned any technique.
**Identity and tone (captions).** One identity picks the entire look — surface, palette, motion, climax behavior. Route by content: explainer / interview / must-read words → a rail-carrying identity, with `anchor` the conservative default where every word has to read; poetic / social / cinematic → a column-flow identity by register (`editorial`, `cream`, `loud`, `neon`); "炸 / 特效 / VFX" → a themed identity (`ordnance`, `terminal`, `stomp`). Unsure → `anchor`: the words read and the scene stays safe. Don't ask for "Standard vs Cinematic vs Theme" — those are engine names; name the identity.
**Verbatim rail vs climax embed.** The rail is the default and carries most of the text. An embed is a promotion — one peak word matted behind the subject, scarce and spaced (roughly one per beat, never two co-visible, at most one apex). Tell the skill *which* lines earn the embed; leave the rest on the rail.
**Style, layout, and canvas (recut).** Pick a style group (warm-paper / clinical / experimental), a layout (split / stack / pip / overlay), and a canvas ratio; the video frame follows from layout × style. The recommended ratio matches the source, but you choose — 16:9 for desktop / YouTube, 9:16 for Reels / Shorts, 4:5 for feed.
**Keyword highlighting.** On the caption rail, a punch word can carry an inline `emphasis` — an accent-color or active-word pop — without leaving the rail. Ask for "highlight the key word in each line" and it stays readable.
## Variants
<AccordionGroup>
<Accordion title="Cinematic caption embed (mood over verbatim)">
> /embedded-captions Cinematic captions on ./poem.mp4 — no rail, hero typography composited behind the speaker, words accumulating as a column. Use the `editorial` identity (lowercase-italic hero). One apex word per thought, air between them. 4:5. Never grade the footage.
Column-flow identities drop the rail and make everything embed-style — reach for them on poetic / social / "cinematic" asks where mood beats strict readability, never on an explainer where the words must read.
</Accordion>
<Accordion title="Bright-scene captions">
> /embedded-captions Add verbatim captions to ./outdoor-vlog.mp4. It's a bright daylight scene, so use the `ink` identity — near-black type printed onto the surface — not a light-on-bright look that washes out. Keep it a readable rail. 16:9.
Screen-blend cream looks wash out over bright backdrops (luminance > ~180); `ink` is built for bright surfaces. Match the identity to the scene rather than asking the engine to recolor a look.
</Accordion>
<Accordion title="VFX-grade themed captions">
> /embedded-captions Bring the energy on ./hype-clip.mp4 — I want the captions to hit hard. Use the `ordnance` identity: a stamped verbatim rail with a detonation apex. Rail carries the verbatim; the payoff line is the setpiece. 9:16.
Themed identities (`ordnance`, `terminal`, `stomp`, `neonsign`, `stardust`, …) are the answer to "make it explode / 特效 / like AE did it". Theme mode is the one place a register-gated reaction beat may touch the frame — applied after the matte composite so subject, text, and plate move as one — but the a-roll is still never graded.
</Accordion>
<Accordion title="Landscape data recut">
> /talking-head-recut Recut ./analyst-interview.mp4 as a 16:9 explainer with a clinical style. Split layout: speaker on the right, data cards on the left. Cards for each claim — a count-up for the headline number, a swiss-grid comparison for the two options, a terminal-style callout for the technical bit. Auto-pace the card count for a 5-minute clip. Footage untouched.
Layout (split / stack / pip / overlay) sets how speaker and cards share the canvas; card count auto-infers from duration and information density, with a floor of five so even a short clip has rhythm.
</Accordion>
</AccordionGroup>
## Failure modes
**Asking for footage edits.** Both skills add a layer and leave the a-roll exactly as shot. Trimming, speeding up, recoloring, reframing, or reordering is NLE editing and out of scope — captions and cards are the only additions.
- ❌ `add captions and trim the dead air at the start, and warm up the color to match my brand`
- ✅ `add captions; leave the footage untouched` — do the trim / grade in an editor first, then bring the finished clip here.
**Embedding every word.** On a talking head the rail is the verbatim default; matting every caption behind the subject buries the words and spends the climax on nothing.
- ❌ `composite every caption behind the speaker for a cinematic look` (on an explainer)
- ✅ `verbatim rail; promote only the two payoff lines to an embed`
**Multi-subject clips.** The caption matte is one person; two speakers or hard cuts flicker or get refused.
- ❌ `caption this two-person podcast in one pass`
- ✅ `split the clip per shot / per speaker first, then caption each` — or use a single-subject cut.
<Tip>
For the beat-timestamped skeleton these prompts share, see [Prompt anatomy](/prompting/anatomy); for adjectives that map to motion and emphasis settings, [Vocabulary](/prompting/vocabulary). To build a video from scratch instead of dressing existing footage, start at the router in `/hyperframes`.
</Tip>
*Next: [Music videos and slideshows](/prompting/music-and-slideshows) — swap footage for a soundtrack, or slides, and let the beat or the deck set the pace.*
+106
View File
@@ -0,0 +1,106 @@
---
title: Caption styles
description: "Map caption tone to named caption components, and prompt per-word emphasis for composed videos."
---
Your faceless explainer from Level 1 already asked for "embedded captions, keywords highlighted in the accent color" and got a sensible default. This chapter is the catalog behind that ask — the named components you can pin instead, by tone, so the highlight color and the animation are a decision, not a default.
## What caption styles do and when they trigger
Caption components are drop-in snippets that render animated on-screen text — one visual identity per component, animating per word or per line. Prompts trigger this layer when you ask for captions, subtitles, kinetic text, lyric-style words, or word-by-word titles inside a composition you're building. Describe the *energy* of the captions and the agent picks matching typography, size, and animation; name a component to lock the look.
<Note>
These components are for **composed videos** — captions you author into a HyperFrames composition. To add captions to an existing **talking-head MP4**, use the [`/embedded-captions`](/prompting/captions-and-talking-heads) workflow instead: it carries its own catalog of caption identities built around subject matting and occlusion (the caption sits *behind* the speaker), which the composition snippets below don't do.
</Note>
## Tone → caption component
| Tone | Components |
| ---- | ---------- |
| **Hype / high-energy social** | [`caption-kinetic-slam`](/catalog/components/caption-kinetic-slam), [`caption-highlight`](/catalog/components/caption-highlight), [`caption-particle-burst`](/catalog/components/caption-particle-burst), [`caption-emoji-pop`](/catalog/components/caption-emoji-pop) |
| **Clean / corporate** | [`caption-clip-wipe`](/catalog/components/caption-clip-wipe), [`caption-weight-shift`](/catalog/components/caption-weight-shift) |
| **Elegant / editorial** | [`caption-editorial-emphasis`](/catalog/components/caption-editorial-emphasis), [`caption-gradient-fill`](/catalog/components/caption-gradient-fill), [`caption-weight-shift`](/catalog/components/caption-weight-shift) |
| **Neon / nightlife / music** | [`caption-neon-glow`](/catalog/components/caption-neon-glow), [`caption-neon-accent`](/catalog/components/caption-neon-accent) |
| **Tech / cyber / glitch** | [`caption-glitch-rgb`](/catalog/components/caption-glitch-rgb), [`caption-matrix-decode`](/catalog/components/caption-matrix-decode) |
| **Karaoke / lyric / follow-along** | [`caption-pill-karaoke`](/catalog/components/caption-pill-karaoke), [`caption-highlight`](/catalog/components/caption-highlight) |
| **Textured / cinematic display type** | [`caption-texture`](/catalog/components/caption-texture), [`texture-mask-text`](/catalog/components/texture-mask-text) |
| **Depth / 3D layering** | [`caption-parallax-layers`](/catalog/components/caption-parallax-layers) |
## Text-effect components
Three [Text Effects](/catalog/components/morph-text) components do one focused job rather than caption a whole track:
| Component | Use when |
| --------- | -------- |
| [`caption-blend-difference`](/catalog/components/caption-blend-difference) | Text sits over busy or shifting footage and must stay legible — it auto-inverts per pixel against whatever is behind it. |
| [`morph-text`](/catalog/components/morph-text) | You want one spot to cycle through a short word list with a gooey morph ("fast / simple / yours"). |
| [`texture-mask-text`](/catalog/components/texture-mask-text) | A large display word filled with a physical texture (brick, rock, wood, metal, lava). |
## Example prompts
> /faceless-explainer 30-second vertical explainer. Add [`caption-highlight`](/catalog/components/caption-highlight) captions, TikTok-style — the visible line stays up, one word highlighted at a time.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/validate-captions-catalog.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
<Note>
Caption components ship as demos — a fixed word list, landscape sizing, an 8-second timeline. The agent re-authors the words and timings to your narration and re-sizes for your format; that's expected, not a workaround. If you want one full-screen word at a time (no visible line), that's [`caption-kinetic-slam`](/catalog/components/caption-kinetic-slam), not `caption-highlight`.
</Note>
> Hype captions with [`caption-kinetic-slam`](/catalog/components/caption-kinetic-slam): one full-screen word per beat, alternating slam-in direction.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/caption-kinetic-slam.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above with an authored 24-word line, unedited.*
> Neon music-video captions using [`caption-neon-glow`](/catalog/components/caption-neon-glow). Make brand names larger with an accent color and highlight the numbers differently.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/caption-neon-glow.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited — the brand renders 1.4x in magenta, numbers in amber, distinct from the default cyan.*
> Fill the hero word "STONE" with [`texture-mask-text`](/catalog/components/texture-mask-text) using the rock texture.
## Knobs
- **Tone** picks typography, size, and animation — Hype (heavy, 7296px, scale-pop) through Storytelling (serif, 4456px, slow fade). See the caption-tone table in [vocabulary](/prompting/vocabulary).
- **Per-word emphasis.** "Make brand names larger with accent color," "highlight numbers differently," "add bounce to emotional keywords" all work — several components key off this: [`caption-editorial-emphasis`](/catalog/components/caption-editorial-emphasis) drives a dramatic size contrast on emphasis words, [`caption-particle-burst`](/catalog/components/caption-particle-burst) fires on keywords, and the neon components carry keyword accent colors.
- **Texture variable.** [`caption-texture`](/catalog/components/caption-texture) ships lava, marble, metal, wood, concrete, and rock — name the one you want.
- **Word list.** [`morph-text`](/catalog/components/morph-text) cycles an editable list; quote the words in order.
- **Format.** Full-screen single-word styles ([`caption-kinetic-slam`](/catalog/components/caption-kinetic-slam)) and TikTok-style highlights ([`caption-highlight`](/catalog/components/caption-highlight)) are built for vertical / social framing — say "vertical" or "9:16" so sizing and safe areas match.
## Failure modes
**Don't stack a heavy effect on every word.** Caption components already animate per word; layering another emphasis on top of that competes and turns illegible. Emphasize only the keywords.
- ❌ `make every word explode with particles`
- ✅ `caption-particle-burst, firing only on the keywords`
**Don't mix caption styles in one section.** One identity per composition (or per section) reads as designed; two competing styles read as a mistake.
- ❌ `use caption-neon-glow and caption-matrix-decode together`
- ✅ pick one; switch styles only across a clear section break
**Don't reach for these on talking-head footage.** These are composition snippets, not the matting/occlusion pipeline — dropped onto an untouched MP4, a caption sits in front of the speaker, never behind. (The capstone thread below shows `caption-kinetic-slam` reading *behind* a subject, which is not a contradiction: that composition mattes the footage itself first, so the cutout is a separate layer the type can pass under. The limitation is about the snippet alone, not the technique.)
- ❌ `/hyperframes add caption-highlight to my interview.mp4`
- ✅ `/embedded-captions` (see [captions and talking heads](/prompting/captions-and-talking-heads))
**Don't match a hype style to calm content.** A high-energy caption on a corporate explainer fights the tone; let the tone table pick the identity.
- ❌ `glitchy RGB captions` (on a wellness brand piece)
- ✅ `clean captions with caption-clip-wipe`
**Don't invent caption names.** Only the components in the [Captions](/catalog/components/caption-highlight) and [Text Effects](/catalog/components/morph-text) groups exist.
- ❌ `add typewriter-bounce captions`
- ✅ describe the tone ("tutorial, monospace, typewriter") or name a real component
<Note>
**Capstone thread** — in the [Level 7 film](/prompting/capstone)'s Material region, word-synced keywords from the clip's own transcription slam in as display type behind the matted-out speaker — captions as scenography, on real word timings (cut from the film, below).
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> THEN they speak, and the main **keywords of their own line — derived from the clip's transcription — land word-synced as huge display text BEHIND the cutout**, each keyword slamming in on its spoken moment with the subject's silhouette occluding it (the two-layer text-behind-subject plate); style the keyword type by adapting a bold **catalog caption component** (`caption-kinetic-slam` or similar) at display scale.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-material.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause, rendered — the region cut from the finished film.*
*Next: [When to generate artwork](/prompting/generated-artwork) — where hand-drawn HTML/CSS/SVG wins, and where a generated image beats it.*
+86
View File
@@ -0,0 +1,86 @@
---
title: Code changes and PRs
description: "What to say to turn a GitHub pull request into a code-change explainer — changelog, feature reveal, fix, or refactor walkthrough."
---
Two rides in, both built from nothing yet on the page. This one starts from something that already exists and is already true — a merged PR — and turns the diff itself into the story.
## Your first win
One prompt to [`/pr-to-video`](/prompting/overview), pointed at a PR link, is enough for a finished code-change explainer — no technique required yet.
Verified, from the [examples](/prompting/examples) page — a 30-second feature reveal:
> /pr-to-video Make a 30-second 1920x1080 feature-reveal video from [PR URL]. Lead with what users get, not the diff; show the key code change with the `code-diff` block for one beat only; end on version number + repo URL. No narration, kinetic captions instead.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-pr-video.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
## What this makes
A code-change explainer built from a GitHub pull request. The [`/pr-to-video`](/prompting/overview) workflow reads the PR through `gh` — the diff, commits, files, and contributors — reshapes it into a story, and builds it frame by frame, rendering code beats on a purpose-built diff surface.
The input is a **code change**, not a website or a product page. A PR link (`https://github.com/owner/repo/pull/N`), an `owner/repo#N` ref, or "this PR" in a checked-out repo all work. A product to sell → [`/product-launch-video`](/prompting/product-launch); a topic with no PR → [`/faceless-explainer`](/prompting/explainers). Unsure → start at `/hyperframes`.
## The knobs that matter
What you can already steer from the prompt, before you've learned any technique.
| Knob | What to say | Why it matters |
| --- | --- | --- |
| **Impact vs diff** | "lead with what users get, not the diff" | The video explains the *change*, it doesn't read the diff aloud; opening on impact answers "why should I care?" before the code |
| **How many hunks** | "one `code-diff` beat only" / "at most two hunks" | Code beats feature 2-4 real hunks total, each a small legible snippet — a whole file is unreadable at video scale |
| **Which code block** | `code-diff` for a delta · `code-morph` for a refactor · `code-typing` for new code | The block matches the story: a diff shows added/removed lines, a morph shows one shape becoming another |
| **Angle** | "changelog" / "feature-reveal" / "fix-explainer" / "refactor-walkthrough" | Sets the story shape; the workflow reshapes the PR into it rather than walking files in diff order |
| **Audience** | "for developers" (default) / "mixed technical" / "non-technical stakeholders" | Shifts how much the narration assumes — a non-technical cut leans harder on impact and lighter on code |
| **End card** | "end on version number + repo URL" | The conventional close for a code explainer; state what goes on it so it's a real CTA, not an afterthought |
| **Narration vs captions** | "calm male narration" or "no narration, kinetic captions instead" | Both are supported; captions-only keeps it silent-friendly for social, narration carries a longer walkthrough |
| **Length** | "~30s" for one headline, up to ~3 min for a large PR | The workflow reads the change size and recommends a tier — a huge PR is a ceiling on story, not a floor to fill |
<Tip>
The style is fixed to the workflow's warm-editorial preset with a navy code surface built for diffs — it's what makes the code beats legible. You don't choose a theme here; you choose the angle, the hunks, and the narration.
</Tip>
## Variants
<AccordionGroup>
<Accordion title="Changelog roundup">
> /pr-to-video Make a ~40-second 1920x1080 changelog video from [PR URL]. Changelog angle: open with the release line, then one beat per notable change — a short label and a one-line "what it does" each. Show at most two `code-diff` hunks across the whole video. End on version + repo URL. Calm male TTS narration, no captions.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/variant-pr-changelog.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from this prompt with [PR URL] = heygen-com/hyperframes#2092, unedited.*
A changelog trades depth for breadth — many small changes, each a beat, rather than one change explored deeply. Keep code beats sparse so the pace stays fast. A PR doesn't carry its shipping version — the agent finds it from the release tag or the package manifest on main.
</Accordion>
<Accordion title="Fix explainer">
> /pr-to-video Make a ~40-second 1920x1080 fix-explainer from [PR URL], for developers. Fix angle: state the bug's symptom first, then the root cause, then the one-line fix on the `code-diff` block. End on version + repo URL. No narration, kinetic captions.
A fix reads as symptom → cause → fix. Lead with what users saw break, not the stack trace — the diff is the payoff, not the opening.
</Accordion>
<Accordion title="Refactor walkthrough">
> /pr-to-video Make a ~70-second 1920x1080 refactor-walkthrough from [PR URL], for developers. Refactor angle: why the old shape hurt, then the new shape, showing the before/after with the `code-morph` block for the key file. End on version + repo URL. Calm male TTS narration.
A refactor changes shape without changing behavior, so the story is *why* the new structure is better. `code-morph` animates one form transforming into another — the right block when the point is the transition, not a line-by-line delta.
</Accordion>
</AccordionGroup>
## Common failure modes
**Forcing a theme over the preset.** The general rule is on [ride 1](/prompting/product-launch#common-failure-modes); the specific reason here is that the navy code surface is tuned for diff legibility, so a foreign theme costs you the one thing the workflow is good at.
- ❌ `/pr-to-video ... dark theme, neon accents`
- ✅ let the preset carry the look; spend your specificity on the angle and the code beats
**Asking for the whole diff.** A PR video explains the change; it doesn't recite every file. A full diff is unreadable at video scale.
- ❌ `walk through the entire diff, file by file`
- ✅ `feature the 2-3 key hunks, each a small legible snippet`
**Opening on the code.** The diff is the payoff, not the hook — lead with what the change means.
- ❌ `start with the diff, then explain what it does`
- ✅ `lead with what users get, then show the key hunk`
**Hard-timing a narrated cut.** As in [ride 1](/prompting/product-launch#common-failure-modes) — the spoken length sets the runtime, so state a range.
- ❌ `a 40-second narrated walkthrough`
- ✅ `a ~40-second narrated walkthrough`
*Next: [Captions and talking-head footage](/prompting/captions-and-talking-heads) — same one-prompt move, now dressing footage you already shot.*
+157
View File
@@ -0,0 +1,157 @@
---
title: Code animations
description: "Prompt code walkthroughs — typing, diffing, highlighting, scrolling — and pick a terminal or editor theme by name."
---
Your PR video from Level 1 named `code-diff` for a single beat and moved on. This chapter is the rest of that catalog: typing, diffing, highlighting, scrolling, and picking a terminal or editor theme by name — for the moments a walkthrough needs to slow down and let the code itself carry the scene.
Code is the one subject where the framework does the hard part for you. The [Code Animations](/catalog/blocks/code-typing) blocks handle syntax highlighting, caret tracking, diff coloring, and camera moves deterministically — you describe the *walkthrough*, name the block, and paste your snippet. This page is the vocabulary for doing that well; for turning a real pull request into a code-change video, see [Code and PRs](/prompting/code-and-prs).
Everything here follows the [one-shot skeleton](/prompting/anatomy): route, spec, beats, copy, technique, negatives. The "technique" slot is where you name the block, and the "copy" slot is where your code goes — quoted exactly, because unquoted code gets paraphrased into something that won't compile.
### Pick the motion by what the viewer should learn
Each Code Animations block answers a different "what is the viewer supposed to notice." Map the intent to the block:
| You want to show… | Name this block | Length |
| ------------------------------------------ | ------------------------------------------------------- | ------ |
| Code being written, character by character | [`code-typing`](/catalog/blocks/code-typing) | 5s |
| An edit — before → after, red/green | [`code-diff`](/catalog/blocks/code-diff) | 6s |
| One line as *the* line, everything else dim | [`code-highlight`](/catalog/blocks/code-highlight) | 5s |
| Walking a long file to a spot deep inside | [`code-scroll`](/catalog/blocks/code-scroll) | 6s |
| One snippet transforming into another | [`code-morph`](/catalog/blocks/code-morph) | 7s |
| Snippets flying in and stacking up | [`code-snippet-flight`](/catalog/blocks/code-snippet-flight) | 6s |
| Code on a rotating 3D slab (title-card feel) | [`code-3d-extrude`](/catalog/blocks/code-3d-extrude) | 8s |
| Code resolving out of a shader dissolve | [`code-shader-dissolve`](/catalog/blocks/code-shader-dissolve) | 7s |
| Code assembling from a particle swarm | [`code-particle-assemble`](/catalog/blocks/code-particle-assemble) | 8s |
The first four are the workhorses of a code *walkthrough* — they keep the code readable and the viewer oriented. Everything below them trades legibility for motion: they look great as an opener or a hero moment, but they trade legibility for motion, so don't ask them to carry an explanation.
<Tip>
`code-morph` re-drives Shiki Magic Move as a paused GSAP timeline, and `code-diff` collapses removed lines and expands added lines. Both read "an edit happened" far more clearly than retyping the whole snippet with `code-typing` — reach for them when the story is *a change*, not *authoring from scratch*.
</Tip>
### Prompting a typing reveal
`code-typing` reveals code character by character with a caret that tracks the frontier — no CSS animation, so it seeks cleanly. Give it the exact code and a pace; the agent re-bakes the block's syntax tokens to your snippet.
> /motion-graphics 6-second 1920x1080 video. A dark editor types this snippet, character by character, caret tracking the frontier, then holds on the blinking cursor for the final second:
> ```
> export async function render(comp: Composition) {
> await comp.seek(0);
> return comp.capture();
> }
> ```
> Use the `code-typing` registry block. No narration, no image or media files.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/validate-code-typing.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
**Quote the code as a literal block.** Prose descriptions of code get paraphrased.
- ❌ `type out a function that seeks to zero and captures`
- ✅ paste the actual snippet in a fenced block — it renders verbatim
**Give the caret somewhere to rest.** Compositions hold their final state, so if you don't ask for a hold the last frame is a frozen full snippet — the [dead-motion tell](/prompting/motion).
- ❌ `types the code and ends`
- ✅ `types the code, then holds on the blinking cursor for the final second`
### Prompting a diff or a highlight
For "here's what changed," hand `code-diff` the before and after and let it color the delta. For "look at *this* line," give `code-highlight` the full context and name the target line.
> /motion-graphics 6-second 1920x1080 video. Show this edit to `api.ts` as a colored diff — the removed line collapses in red, the added line expands in green:
> removed: `const res = await fetch(url)`
> added: `const res = await fetch(url, { signal })`
> Use the `code-diff` registry block. No audio.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/validate-code-diff.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
> /motion-graphics 5-second 1920x1080 video. Show a 12-line config file; a highlight band sweeps to line 7 (`timeout: 30_000`) while the surrounding lines dim. Hold with line 7 lit and the cursor blinking. Use the `code-highlight` registry block. No audio.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/validate-code-highlight.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited — the agent authors plausible surrounding config lines; paste all 12 if the exact file matters.*
**Name the target line unambiguously.** The block dims context around one line — tell it which.
- ❌ `highlight the important line`
- ✅ `highlight line 7 (timeout: 30_000)`
### Prompting a scroll-through
`code-scroll` moves the camera down a long file to bring a target line to center and spotlights it — the block for walking real modules, not toy snippets.
> /motion-graphics 6-second 1920x1080 video. Scroll a ~60-line source file so line 44 (`return dedupeFrames(frames)`) arrives at center and gets spotlighted; ease the scroll and let it settle without snapping. Use the `code-scroll` registry block. No audio.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/code-scroll.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
**Ask the scroll to ease and settle, not snap.** A linear scroll that stops dead reads mechanical.
- ❌ `scroll straight to the line`
- ✅ `ease the scroll and let it settle` — pair with the [motion grammar](/prompting/motion)
### Choosing a theme by name
The [Code Snippets](/catalog/blocks/code-snippet-monokai) blocks are pre-styled shells with per-character typing already built in. There are two families, and you select one by asking for it in plain language — the exact block name is the theme name.
**macOS Terminal.app profiles** — a real terminal window chrome. Say "apple terminal, ocean profile" → [`code-snippet-apple-terminal-ocean`](/catalog/blocks/code-snippet-apple-terminal-ocean). The full set of profiles:
| Profile | Block | Profile | Block |
| ------------ | ----------------------------------------- | -------------- | ------------------------------------------- |
| Basic | `code-snippet-apple-terminal-basic` | Novel | `code-snippet-apple-terminal-novel` |
| Clear Dark | `code-snippet-apple-terminal-clear-dark` | Ocean | `code-snippet-apple-terminal-ocean` |
| Clear Light | `code-snippet-apple-terminal-clear-light` | Pro | `code-snippet-apple-terminal-pro` |
| Grass | `code-snippet-apple-terminal-grass` | Red Sands | `code-snippet-apple-terminal-red-sands` |
| Homebrew | `code-snippet-apple-terminal-homebrew` | Silver Aerogel | `code-snippet-apple-terminal-silver-aerogel`|
| Man Page | `code-snippet-apple-terminal-man-page` | Solid Colors | `code-snippet-apple-terminal-solid-colors` |
**VS Code workbench themes** — full editor chrome (activity bar, sidebar, tabs, terminal, status bar). Say "monokai" or "visual studio dark":
| Say this | Block | Say this | Block |
| ---------------------- | ------------------------------------- | ------------------- | ---------------------------------- |
| Monokai | `code-snippet-monokai` | Solarized Light | `code-snippet-solarized-light` |
| Dark Modern | `code-snippet-dark-modern` | Light Modern | `code-snippet-light-modern` |
| Dark Plus | `code-snippet-dark-plus` | Light Plus | `code-snippet-light-plus` |
| Dark 2026 | `code-snippet-dark-2026` | Light 2026 | `code-snippet-light-2026` |
| High Contrast | `code-snippet-high-contrast` | High Contrast Light | `code-snippet-high-contrast-light` |
| Visual Studio Dark | `code-snippet-visual-studio-dark` | Visual Studio Light | `code-snippet-visual-studio-light` |
> /motion-graphics 5-second 1920x1080 video. A macOS Terminal window in the Ocean profile types `npx skills add heygen-com/hyperframes` character by character, then holds on the typed, unexecuted command with the cursor blinking — no output, no second prompt. Use the `code-snippet-apple-terminal-ocean` registry block. No narration.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/terminal-ocean.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
**Match the theme to the surface you're claiming to show.** A terminal command in a VS Code editor chrome reads wrong; a source file in Terminal.app reads wrong.
- ❌ `monokai theme typing a shell command`
- ✅ `apple terminal homebrew profile typing a shell command`
<Tip>
Ambiguity resolves to the closest named block. "Dark theme" is under-specified — the agent picks one of a dozen dark variants and you may not get the one you pictured. Say the theme name. This is the [specification dial](/prompting/specification-dial) applied to code: name the block when the default choice can miss.
</Tip>
### Pairing with a pull request
When the code you're animating comes from a real PR, don't hand-write the beats — the [`/pr-to-video`](/prompting/code-and-prs) workflow reads the diff and composes `code-diff`, `code-highlight`, and `code-scroll` around the actual changed hunks. Use the blocks on this page directly when you're illustrating a concept; route through the PR workflow when you're narrating a specific change set.
### Where to go next
- [Anatomy of a one-shot prompt](/prompting/anatomy) — the skeleton every prompt above uses.
- [Copy-paste examples](/prompting/examples) — full prompts you can adapt.
- [Code and PRs](/prompting/code-and-prs) — turning a GitHub PR into a code-change video.
- [Motion that reads premium](/prompting/motion) — the hold-and-settle rules the code blocks still need from you.
<Note>
**Capstone thread** — the [Level 7 film](/prompting/capstone) opens with this chapter's technique: real HyperFrames markup typed character by character, and the typed line's baseline literally grows into the timeline wire the rest of the film travels (cut from the film, below).
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> **Type (07s).** Black-on-charcoal close-up: a cursor types real HyperFrames markup character by character — `<div class="clip" data-start="0" data-duration="4">` and a `gsap.timeline({ paused: true })` line. As the typed line completes, the text's baseline extends and becomes **the wire** — the underline literally grows into the timeline and the camera begins its dolly along it. The typed div folds into a compact clip chip (persistent element 3) that drops onto the wire. Kinetic display type states "WRITE HTML." as the travel begins.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-type.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause, rendered — the region cut from the finished film.*
*Next: [Data and maps](/prompting/data-and-maps) — the same named-block, quoted-copy pattern, for charts, stats, and maps instead of code.*
+119
View File
@@ -0,0 +1,119 @@
---
title: Data and maps
description: "Prompt animated charts, count-up stats, and maps — highlight regions, draw flows, size bubbles — or hand-draw a chart for full control."
---
Level 3's six-scene SaaS explainer ended on a `cinematic-zoom` into a pricing reveal — a transition doing the work a real number could. This chapter is how to put an actual animated chart or count-up stat in that reveal instead of just a camera move.
Numbers and geography are the two subjects where "what data" and "how it moves" are separate decisions. The [Data](/catalog/blocks/data-chart) blocks give you a polished chart or map you feed values into; the count-up [showcase](/catalog/blocks/apple-money-count) blocks handle the odometer-and-flourish moment. Everything here plugs into the [one-shot skeleton](/prompting/anatomy) — the data goes in the "copy" slot, the block name in "technique."
### Charts: name the block, or hand-draw
There are two ways to get a chart, and the choice is about control:
- **Name [`data-chart`](/catalog/blocks/data-chart)** to get the built animated bar + line chart — staggered reveal, value labels, NYT-style typography — and feed it your numbers. Fast, consistent, no design decisions.
- **Say "hand-draw everything — no chart library"** to make the agent build the chart from inline SVG and GSAP instead. You give up the polish of the block for total control over shape, motion, and layout — a bar race that overtakes mid-animation, an arc that draws to match a counter, a layout no library ships.
Feed data inline or as a file. Small series go straight in the prompt; a CSV gets referenced and parsed at build time (keep it deterministic — no [render-time fetches](/concepts/determinism)).
> /motion-graphics 12-second 1920x1080 video. Title "Most-used languages" with the subtitle "share of respondents, 2026 survey". Turn this into an animated bar chart with a staggered reveal and value labels counting up on each bar, each label suffixed `%`:
> ```
> Python 41, TypeScript 33, Rust 19, Go 14, Java 9
> ```
> Use the `data-chart` registry block. No audio.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/chart-languages.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited — labels count up while the next bar is already growing.*
> /motion-graphics 10-second 1920x1080 video, dark slate background. Title "Top languages 2026" top-left. Five horizontal bars (Python, TypeScript, Rust, Go, Java) grow from zero with staggered starts, overtaking each other twice mid-animation; each bar has a right-edge value label counting up to its final %. End state holds 2s, alive — the leader pulses once, timed to finish exactly on the last frame, never a frozen tail. Hand-author the marks (no chart library) and make them *look* hand-drawn: wobbly ink strokes, not clean rectangles. Say which two pairs overtake and when, or the reveal will land on a different number of crossings each run. No audio.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/barrace-handdrawn.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited — hand-authored ink strokes, no chart library; ranks swap twice.*
**State the block *or* opt out of it — don't leave it implicit.** "Animate this data" without a decision drifts between a generic block and an improvised layout.
- ❌ `animate this CSV as a chart`
- ✅ `turn this CSV into an animated bar chart — use the data-chart registry block` **or** `…hand-draw everything, no chart library`
**Format numbers for the animation you asked for.** An odometer count-up needs fixed digit columns; a `$0 → $4.2M` range forces an awkward `$0.0M` start.
- ❌ `counts from $0 to $4.2M`
- ✅ `counts up to $4.2M`
### Count-up stats
For a single hero number, [`apple-money-count`](/catalog/blocks/apple-money-count) is the Apple-style finance counter — it rolls from $0, flashes green, and bursts money icons with sound. Name it when you want that exact flourish; hand-draw when you want a bare number in your own type.
> /motion-graphics 6-second 1920x1080 video, dark navy background. Beat 1 (0-1s): label "ARR" fades up small, top-center. Beat 2 (1-4s): a giant number counts up to $4.2M with an odometer roll, easing out as it lands. Beat 3 (4-6s): "+312% YoY" stamps in below in green, then settles into a gentle ambient idle. Use the `apple-money-count` registry block as base. No narration.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/money-count.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
### Maps: match the ask to the map
Each map block answers a different geographic question. Say what the map is *for* and name the matching block:
| You want to… | Name this block | Length |
| ----------------------------------------------- | -------------------------------------------------- | ------ |
| Shade US states by a value (choropleth) | [`us-map`](/catalog/blocks/us-map) | 12s |
| Size US cities by a value (proportional bubbles)| [`us-map-bubble`](/catalog/blocks/us-map-bubble) | 12s |
| Draw connections between US cities (origin→dest)| [`us-map-flow`](/catalog/blocks/us-map-flow) | 12s |
| Show US states as an equal-weight hex grid | [`us-map-hex`](/catalog/blocks/us-map-hex) | 10s |
| Shade Spain by autonomous community | [`spain-map`](/catalog/blocks/spain-map) | 12s |
| Shade the world country by country | [`world-map`](/catalog/blocks/world-map) | 14s |
The US map encodings combine — asking to "shade states *and* draw flows between two cities" gets you one composition: the agent merges the blocks at source onto a shared projection so arcs land exactly on the choropleth's states.
> /motion-graphics 12-second 1920x1080 video. A US choropleth shades states by adoption rate with staggered reveals and a gradient legend, then connection arcs draw between San Francisco, Austin, and New York. Use the `us-map` and `us-map-flow` registry blocks. No audio.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/validate-us-map-flows.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
<Note>
The map blocks fetch their geo data from a CDN at runtime, which violates the deterministic-render rules — the agent bakes the projected geometry into the composition instead. You never need to ask for this; it's part of building the map correctly.
</Note>
**Pick the encoding, don't just say "map."** Choropleth (color), bubble (size), hex (equal weight), and flow (arcs) tell different stories from the same data.
- ❌ `put California's number on a US map`
- ✅ `size each city as a proportional bubble` → `us-map-bubble`, or `shade each state by value` → `us-map`
**Name the region's block.** The choropleths are region-specific with baked-in projections (`spain-map` is D3 conic conformal, `world-map` is Natural Earth) — there's no generic "any country" map.
- ❌ `a map of Spain's regions` (leaves the projection and geography to chance)
- ✅ `use the spain-map registry block`
### Routes and flights
For a point-to-point journey — a route drawing across a map with a landing beat — [`nyc-paris-flight`](/catalog/blocks/nyc-paris-flight) is the Apple-style flight animation: a plane flies New York → Paris with a marker circle, landing pop, and sound effects. Use it as the base and re-point the endpoints in your prompt.
> /motion-graphics 6-second 1920x1080 video. A realistic map with a plane flying between two cities, a marker circle at the origin, and a landing pop at the destination. Use the `nyc-paris-flight` registry block as base — the route runs New York (JFK) → Paris (CDG), labelled at both ends. Keep the block's own title card and route ticks. End on the settled map, not on the block's white-out hand-off frame. No audio.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/flight-route.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
**A route is a flow with a vehicle, not a static arc.** If you want the drawn arc without the plane and sound, that's `us-map-flow`; if you want the journey performance, that's `nyc-paris-flight`.
- ❌ `draw a line from NYC to Paris` (ambiguous between arc-only and full flight)
- ✅ `a plane flies the route with a landing pop` → `nyc-paris-flight`
### Where to go next
- [Anatomy of a one-shot prompt](/prompting/anatomy) — the skeleton, and why odometers need fixed digit columns.
- [Copy-paste examples](/prompting/examples) — bar-chart race and stat-tile prompts to adapt.
- [Generated artwork](/prompting/generated-artwork) — where hand-drawn HTML/CSS/SVG shines, and where it doesn't.
- [The specification dial](/prompting/specification-dial) — deciding when to name a block versus hand-draw.
<Note>
**Capstone thread** — the [Level 7 film](/prompting/capstone)'s Measure and Everywhere regions are this chapter end to end: `data-chart` bars rise from the timeline wire while the hero count lands, then the same wire becomes a great-circle arc over the `world-map` block's real cloud render regions (cut from the film, below).
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> **Measure (1421s).** The wire becomes a chart baseline (same line, same thickness — no cut): adapt the **`data-chart` registry block** — six labeled bars rise from the wire itself on spoken cues while a hero numeral counts up beside them in huge tabular mono: **50+ CATALOG BLOCKS** (the real registry count) […]
>
> **Everywhere (2128s).** The wire arcs and the ground falls away: adapt the **`world-map` registry block** — the dotted world map scrolls beneath while the wire becomes a great-circle route arcing across it. Real render infrastructure, truthfully labeled: pulses light up at AWS and Google Cloud regions (US-EAST-1 · EU-WEST-1 · AP-SOUTH-1) with a "CLOUD RENDER · LAMBDA + CLOUD RUN" kicker […]
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-data-map.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause, rendered — the region cut from the finished film.*
*Next: [Overlays and lower thirds](/prompting/overlays-and-lower-thirds) — naming timed blocks that sit above your footage or scene, instead of ones that render the scene itself.*
+103
View File
@@ -0,0 +1,103 @@
---
title: Design systems and brand
description: "Point the agent at a source of brand truth — a design spec, a site, or a Figma file — instead of asking for 'on-brand', and let it compose the frame."
---
Everything so far was one scene at a time. This level is about videos as systems — design that persists across scenes, edits that don't regress, output that ships. It starts with the most systemic thing a video has: its brand.
"Make it on-brand" is the single vaguest thing you can ask. The agent has no way to know what your brand *is*, so it invents one. The fix is always the same: give it a **source of brand truth** — a design spec, a live site, or a Figma file — and name it in the prompt. Everything on this page is a way to do that.
## Point at a spec, don't describe a vibe
HyperFrames projects can carry a design spec — `frame.md` — whose frontmatter tokens are the machine-readable brand: exact hex values, font families, weight relationships, and the brand's Do's and Don'ts. When one exists, name it:
> Use the palette and type from `frame.md`. Build a 15-second feature announcement.
- ❌ `make it feel on-brand and premium`
- ✅ `pull colors and fonts from frame.md; premium means generous spacing and one restrained accent`
The engine rationale: `on-brand` is a mood the agent guesses at. A spec's frontmatter is normative — the agent quotes the hex and font family verbatim instead of approximating, and reads the prose sections for intent. If your brand lives somewhere else (a PDF brand guide, a screenshot, pasted hex codes), attach it — attachments and pasted tokens are read more reliably than a described impression.
## Brand is truth for color and type — not for layout
A design spec tells the agent what the brand *looks like*; it does **not** dictate how to compose a video frame. Say what's sacred and let the agent stage the rest:
> Colors and fonts are locked to the brand — keep the exact hexes and the display/body pairing. Layout, spacing, and motion are yours to compose for video.
The engine rationale: web-scale brand values don't survive video. A `1px` border with a `0.06`-opacity shadow is invisible after H.264 compression; a web body size vanishes on a 1080p frame. The brand color, background choice (if the brand is a light canvas, keep it light), fonts, and weight relationships are strict — but type sizes, decorative opacity, and border weight get scaled up for the medium. Over-specifying layout from a web design system fights this; pin the palette and typography, delegate the frame.
## Use the site's own palette and fonts
When there's no spec but there is a brand out there, point at it and let the agent extract:
> Match this site's look — pull its palette and fonts — and make a 20-second launch clip: `https://…`
For a well-known brand, naming it is often enough for the agent to research the palette and typography. One caveat worth stating: a single-page-app homepage often returns a near-empty shell, so if the palette comes back thin, point the agent at a blog, press, or docs page instead. This is the same brand-truth move — the *site* is the source instead of a file.
## Bringing in a Figma frame, brand, or logo
If the brand lives in Figma, ask for it directly — the agent imports it rather than eyeballing a screenshot:
> Bring in the brand tokens from this Figma file, then build the intro: `https://figma.com/…`
> Import this Figma frame as the opening scene and this logo as an SVG: `<links>`
The [Figma import](/guides/figma) path freezes each import as a local asset with recorded provenance (so renders stay deterministic) and imports brand variables as composition brand tokens. Two things worth knowing when you phrase the ask:
- **Import tokens before components.** Say "brand tokens first, then the components" — that's what lets imported component colors link to your brand variables instead of baking in duplicate hexes.
- **Storyboard frames are states, not slides.** If you point at a strip of scene frames, ask the agent to *reconstruct the motion between them* — a frame showing an element at four positions is one element animating, not four stills to flip through.
## Keeping a multi-video series consistent
For a series — a launch set, a weekly clip, a per-region cut — consistency comes from a **shared source of truth**, not from re-describing the brand each time:
> All four videos share `frame.md` for palette and type. Only the headline and the stat change per video.
The parts that stay constant come from the one authoring spec (or one set of imported Figma tokens), and the parts that vary become [variables](/prompting/variables-and-templating). When `frame.md` changes, rerun the workflow steps that generate or assemble the affected compositions so the authored HTML picks up the new brand value. For existing compositions that must re-skin at render time, declare the shared brand tokens as composition variables and bind them through CSS custom properties. This is where design systems and templating meet: the brand is shared, the content is parameterized.
## Supplying brand assets by path
Logos, fonts, textures, and product shots are inputs — hand the agent the path, don't ask it to draw them:
> Logo at `assets/logo.svg`, brand font files in `assets/fonts/`, product shot at `assets/hero.png`. Use them; don't invent placeholders.
Prefer an SVG logo (scalable, animatable) over a raster one. State the paths explicitly so the agent wires the real assets instead of generating stand-ins — and so the render is deterministic, with every asset present locally before it starts.
## Supply inputs a workflow accepts — don't fight its preset
The creation workflows (`/product-launch-video`, `/faceless-explainer`, and the rest) each come with a designed look. The productive move is to feed that look your brand inputs, not to override its composition after the fact:
- ❌ `run /product-launch-video, then restyle every scene to my colors afterward`
- ✅ `run /product-launch-video with my palette, fonts, and logo as inputs up front`
The engine rationale: a workflow's preset is a coherent, tested system — colors, spacing, motion, and component treatments that hang together. Supplying brand inputs at the start lets it apply your palette and type *within* that system. Restyling scene-by-scene afterward pulls threads out of a design that was balanced as a whole, and you spend more effort fighting the preset than you'd have spent handing it a spec.
## Related
<CardGroup cols={2}>
<Card title="Figma Import" icon="figma" href="/guides/figma">
Import brand tokens, assets, components, and motion from a Figma file.
</Card>
<Card title="Variables and templating" icon="sliders" href="/prompting/variables-and-templating">
Turn brand tokens into variables that re-skin a whole series from one value.
</Card>
<Card title="The specification dial" icon="gauge" href="/prompting/specification-dial">
How pinning exact hexes and type direction removes drift.
</Card>
<Card title="Claude Design" icon="message" href="/guides/claude-design">
Attach a brand guide or screenshot to seed a first draft from your look.
</Card>
</CardGroup>
<Note>
**Capstone thread** — the entire [Level 7 film](/prompting/capstone) obeys a supplied `frame.md` — the real HyperFrames brand. The closing lockup is where the system shows most: ABC Solar Display for the display type and the mint→cyan CTA gradient in its single sanctioned appearance (cut from the film, below).
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> **Design system: the provided `frame.md` is the single source of brand truth — read it first and obey it exactly.** It is the real HyperFrames brand (by HeyGen), dark-first: flat near-black ground […] **mint `#3CE6AC` used sparingly as the single accent — mint means go / primary / active**. The mint→cyan gradient (`#3CE6AC → #00E3FF`) appears in exactly one place in the whole film: the final CTA lockup.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-render.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause paying off, rendered — the closing lockup, where the brand file's display face and its one sanctioned gradient finally appear together.*
*Next: [Variables and templating](/prompting/variables-and-templating) — turn the brand tokens that repeat across a series into named slots you fill per render.*
+132
View File
@@ -0,0 +1,132 @@
---
title: Editing existing videos
description: "Direct the agent like an editor — trim, move, retime, swap, restyle — with the NLE verb you already know mapped to the prompt that lands it in one pass."
---
A [storyboard](/prompting/storyboards)'s frames still get edited, and so does everything else you build — a templated composition, a one-shot scene, all of it. Most HyperFrames time isn't the first render — it's the twenty edits after it. A composition is plain HTML with `data-*` timing attributes and a GSAP timeline, so every edit you'd make in a non-linear editor maps to a specific, inspectable change in the source. You don't re-specify the video; you name the edit the way you'd say it to a human editor, and the agent makes the smallest change that does it.
This page maps the editor verbs to the prompts that land them. The examples name elements from a typical composition — swap the noun ("the lower third", "scene 2") for whatever yours is called. Two habits from [Iterating](/prompting/iterating) apply to every one of them, so keep them in mind: **change one thing per render**, and **state targets as absolute values** ("scene 2 = 2 seconds", not "a bit shorter") so the agent lands it in a single pass instead of oscillating.
## The verb → edit map
Every timeline verb resolves to a `data-*` attribute or an inline style. This is what each one touches under the hood — useful to know because it's why absolute targets work and why some edits are cheap:
| You say | Editor verb | What the agent edits |
| --- | --- | --- |
| "start scene 2 later / earlier" | Move | `data-start` |
| "put the captions on top of the video" | Restack | `data-track-index` + inline `z-index` |
| "end the logo sooner" | Trim (right) | `data-duration` |
| "skip the first second of the clip" | Trim (front, media only) | `data-media-start` / `data-playback-start` |
| "make scene 2 two seconds long" | Retime | `data-duration` (and the GSAP timeline length) |
| "the audio bed is too loud" | Level | `data-volume` |
<Note>
The mental model the Studio timeline uses: **move** changes when a clip *starts*, **right trim** changes when it *ends*, and **front trim** only exists for media clips — a `<video>` or `<audio>` can skip into its own content, but a GSAP-driven `<div>` can't start halfway through its animation. See [Timeline editing](/guides/timeline-editing) for the full clip-type breakdown. One more surface the map can't show: retiming a scene that spans the whole composition also needs the root's `data-duration` bumped — the root governs total length, so a longer child never renders past it.
</Note>
## Trim, move, and restack
These are the pure-timing edits — no visual change, just when and where a layer lives on the timeline.
> Trim the intro so it ends at 0:03 instead of 0:05.
> Move the lower third to start at 0:06.
> The captions are rendering behind the video — put them on a higher track so they sit on top.
Bound moves by the composition's length — a 4-second clip moved to 0:08 in a 10-second video barely appears before the end. And absolute targets matter most here: "make the intro shorter" invites a guess; "the intro should end at 3.0s" is a single `data-duration` write with nothing to overshoot.
- ❌ `tighten up the opening`
- ✅ `intro clip duration = 3s; leave its animation and position alone` — one attribute, and the freeze clause stops a rebuild from drifting on axes you'd already settled
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/editing-before.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Before — the untouched composition.*
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/editing-after.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*After — four verbatim edits from this page (move, right-trim, retime, restyle-with-freeze); each touched exactly the attribute the verb map promises, gates green after every edit.*
## Split a scene
The Studio timeline exposes move and trim as drag gestures but does **not** yet offer split, slip, slide, ripple, or roll. You can still split by directing the agent, because it edits the HTML directly — a split is just one clip becoming two with adjusted `data-start` / `data-duration`:
> Split scene 2 at 0:04 so I can drop a transition between the two halves.
Name the exact cut point. The agent turns one clip into two adjacent clips; you then treat each half as its own layer.
## Retime a scene
Retiming starts with the scene's `data-duration`. If the motion should keep its original speed, the timeline can finish early and hold its final state while the scene continues. If the motion itself should slow down or spread across the longer scene, ask the agent to retime the affected tweens as well.
> Make scene 2 run 4 seconds instead of 2.5 — keep the animation, just give it more room.
- ❌ `stretch scene 2` — ambiguous whether you mean slower motion or a longer hold
- ✅ `scene 2 duration = 4s, same motion, add the extra time as a hold at the end`
<Warning>
Render duration comes from `data-duration`, not GSAP timeline length. Do not pad a timeline with an empty `tl.set({}, {}, <seconds>)` sentinel. If a scene still ends early, check the scene and composition timing attributes in the [Video editor cheatsheet](/guides/video-editor-cheatsheet#timing-cheatsheet).
</Warning>
## Make it snappier (retiming *feel*, not just duration)
"Snappier," "punchier," "more relaxed" are pacing words, and they map to concrete easing and timing choices — [Vocabulary](/prompting/vocabulary) has the full table. The agent reads "snappy" as a decisive ease (`power4.out`) and tighter durations, "dreamy" as slow symmetrical motion, and so on.
> Make scene 2 snappier — quicker entrances, harder cuts.
> The reveal feels robotic; give it a bouncy overshoot.
Change one scene's feel per render so you can attribute what helped. If a scene keeps missing, strip it to the minimal version (subject + its motion only), confirm it reads, then re-layer.
## Adjust a keyframe
Individual animation properties are editable — the value, the ease, the timing of any tween. You can direct these by prompt, or edit them yourself in the Studio Design Panel; either way they resolve to the same GSAP code.
> The title slides in from too far — change its entrance to travel 40px, not 200.
> Give the add-to-cart item an arc instead of a straight diagonal, like it's being tossed into the cart.
State the property target absolutely (`Move X = 40`, `arc curviness ≈ 1.5`). [Keyframes & arc motion](/guides/keyframes) covers what's editable, arc-motion paths, and gesture recording.
<Tip>
For an element-specific edit, the Design Panel's clipboard icon copies structured context — the element's id, position, size, and current animation — ready to paste into your prompt. It gives the agent exact spatial context instead of a vague "the title."
</Tip>
## Swap an asset
Replacing a video, image, logo, or audio track is a source-swap. The one rule: **name the path.** The agent will search for "my logo," but a path skips the search and removes the ambiguity of which file you mean.
> Replace the background music with `assets/track.mp3`.
> Swap the hero image for `assets/product-v2.png` and keep its animation.
- ❌ `use the new logo`
- ✅ `swap the logo for assets/logo-2025.svg`
## Change copy
On-screen text is edited verbatim when you quote it. Unquoted text gets paraphrased — the agent treats a description as an instruction to write copy, not to place it exactly.
> Change the headline to "Ship faster." — exact text, keep the styling.
- ❌ `update the title to say something about speed`
- ✅ `title copy: "Ship faster."`
## Restyle one element
Visual tweaks — color, size, weight, position of a single element — are where the "freeze the rest" clause earns its keep. Without it, a restyle prompt can trigger a rebuild that drifts on layout or motion you'd already approved.
> Make the CTA button 20% larger and switch it to the accent color — don't touch anything else.
- ❌ `make the CTA pop more`
- ✅ `CTA background = accent color, font-size = 1.2× current; framing and motion are right, leave them`
## Related
<CardGroup cols={2}>
<Card title="Iterating" href="/prompting/iterating">One variable per edit, absolute targets, freeze what works</Card>
<Card title="Vocabulary" href="/prompting/vocabulary">Pacing and easing words that retime the *feel* of a scene</Card>
<Card title="Timeline editing" href="/guides/timeline-editing">Which edits the Studio timeline persists, and how</Card>
<Card title="Video editor cheatsheet" href="/guides/video-editor-cheatsheet">The `data-*` attributes as timeline controls</Card>
</CardGroup>
*Next: [Iterating](/prompting/iterating) — the discipline behind every edit on this page: one variable per render, absolute targets, freeze what works.*
+116
View File
@@ -0,0 +1,116 @@
---
title: Verified example prompts
description: "Copy-paste prompts, every one run end-to-end to a finished video that passes check."
---
This is the level's gallery, not a new lesson — read these with the vocabulary you just picked up: the [six-part skeleton](/prompting/anatomy), the [specification dial](/prompting/specification-dial), and the [word list](/prompting/vocabulary) or [full visual spec](/prompting/visual-specs) it maps to. Spot the skeleton parts in each prompt below as you read it.
Every prompt below has been run end-to-end and one-shots a finished video that passes `check`. Swap the copy, colors, and inputs for your own.
<Note>
**These prompts carry the [Level 3 motion grammar](/prompting/motion), stated once here rather than repeated in all of them.** Every one asks for the density contract (one focal element at display scale, supporting elements on their own cues, permanent chrome), three depth layers with parallax under one continuous non-settling camera, entrances staggered at offsets shorter than the animations they offset, overshoot on transforms only, and an ambient idle instead of a frozen final frame. What each prompt *does* state individually is its **spectacle beat** — the single exaggerated moment, placed where the piece earns it. That clause is load-bearing: an unnamed burst gets dropped, which is exactly what happened to the count-up's confetti before it was written down.
</Note>
### With registry blocks and workflows
<AccordionGroup>
<Accordion title="Stat count-up">
> /motion-graphics 6-second 1920x1080 video, dark navy background. Beat 1 (0-1s): label "ARR" fades up small, top-center. Beat 2 (1-4s): a giant number counts up to $4.2M with an odometer roll, easing out as it lands. Beat 3 (4-6s): "+312% YoY" stamps in below in green, then everything settles into a gentle ambient idle (subtle breathing scale, slow particle drift). Use the `apple-money-count` registry block as base. No narration. **Spectacle beat:** On the land at 4s, a burst of ~60 paper money notes erupts from behind the numeral and flutters down, seeded so every render is identical, settled by 5.5s — the one exaggeration.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-stat-countup.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="Animated social post">
> /motion-graphics 7-second 1080x1350 vertical video. A real tweet card (handle @hyperframes, text "we render video from HTML now. no timeline UI. just code.") slides up over a soft animated gradient, likes counter ticks 0→1.2K, then the card tilts in 3D and a highlight sweeps the second sentence. Hold on the card at the end. Use the `x-post` and `vfx-liquid-background` registry blocks. No narration, no image or media files. **Spectacle beat:** When the like count lands, the heart pops to 1.6× with a radial burst of ~24 seeded particles and settles — one moment, nothing else exaggerated.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-tweet.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="Map route">
> /motion-graphics 8-second 1920x1080 video. Dark world map, a glowing arc animates from San Francisco to Tokyo over 3s, destination pin drops with a pulse, then camera zooms into Tokyo and the label "LATENCY: 89ms" types on. Use the `nyc-paris-flight` registry block as the base pattern, restyle to teal on charcoal. No narration. **Spectacle beat:** The destination pin's landing fires a triple concentric shockwave that expands past the label and fades, with a brief chromatic split on the pin.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-map-route.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="Logo sting with shader transition">
> /motion-graphics 5-second 1920x1080 logo sting. Beat 1 (0-2s): the word "ACME" assembles from scattered particles. Beat 2 (2-3s): full-frame `swirl-vortex` shader transition. Beat 3 (3-5s): logo lockup + tagline "Ship faster." settles on white, holds. Use `code-particle-assemble` for the assembly. **Spectacle beat:** The particle assembly IS the spectacle — ~1200 seeded particles converging with visible motion trails, and a single bloom flash on the frame the wordmark completes.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-logo-sting.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="Product launch from a URL">
> /product-launch-video Make a 45-second 1920x1080 launch video for https://linear.app. Energetic but minimal, use the site's own palette and screenshots. Structure: hook stating the problem, 3 feature beats with UI captures and one-line captions, end card with logo + "Try it free". Female TTS voice, confident tone, subtle electronic BGM under -18dB. **Spectacle beat:** One exaggerated moment: the end card's logo lands with a bloom flash and a fast light-sweep across the wordmark. Feature beats stay restrained.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-product-launch.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="Explainer from pasted text">
> /faceless-explainer Turn this into a ~60-second 1080x1920 vertical explainer: [paste your text]. One idea per scene, big typography, diagrams over stock footage, brand color #FF5533 on off-black. Male TTS voice, calm. Embedded captions, keywords highlighted in the brand color. **Spectacle beat:** One exaggerated moment: the final CTA's key phrase slams in at 1.5× with a chromatic split that resolves in 0.2s. Every other scene stays typographically calm.
With a verbatim script, final duration follows the narration — ask for "~60 seconds", not exactly 60.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-explainer.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="GitHub PR reveal">
> /pr-to-video Make a 30-second 1920x1080 feature-reveal video from [PR URL]. Lead with what users get, not the diff; show the key code change with the `code-diff` block for one beat only; end on version number + repo URL. No narration, kinetic captions instead. **Spectacle beat:** The added line in the diff ignites — a green light-sweep travels its length and the line blooms as the camera settles on it.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-pr-video.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="Beat-synced slideshow">
> /music-to-video 20-second 1080x1080 video. Resolve a dark, driving electronic track and cut to its analyzed beat grid — one image per bar, punch-in on downbeats, whip-pan transitions on phrase changes. Generate the eight images rather than using stock: brutalist concrete details as high-contrast monochrome abstracts, one consistent visual language across all eight, each carrying a single cyan light thread. Chrome: a `PLATE 0N/08` counter. End card "CAST IN PLACE" in condensed caps over a hard-edged opaque scrim, with the sub-line "EIGHT SURFACES · ONE HUNDRED BPM". No TTS. **Spectacle beat:** On the loudest downbeat, one image punches to 1.25× with an RGB channel split that snaps back on the next beat. The other cuts stay clean.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-music-slideshow.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
</AccordionGroup>
### Freeform — no blocks, hand-built HTML/CSS/SVG/GSAP
<AccordionGroup>
<Accordion title="Kinetic quote">
> 12-second 1920x1080 video, off-white background. The quote "Simplicity is the ultimate sophistication" builds word by word in massive black serif type, each word snapping in with a slight overshoot; "sophistication" lands last in italic with a hand-drawn underline drawing on. Attribution "— Leonardo da Vinci" fades in small, bottom-right, at 9s. Settle into a barely-visible ambient idle to the end. No audio. **Spectacle beat:** The final word lands 1.4× oversized with an ink-bleed bloom before settling to its true size — the sentence's payoff.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-kinetic-quote.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="Countdown title card">
> 6-second 1920x1080 video. Numbers 3, 2, 1 each fill the frame for one second — each number wipes in with a diagonal mask and its background alternates black/white with inverted text. At 3s the frame slams to "LAUNCH DAY" in condensed caps with a screen-shake, holds with a subtle grain flicker. No audio. **Spectacle beat:** The frame snap at 3s is the moment — a hard white flash frame and a 1.5° rotation kick settling in 0.25s.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-countdown.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="Bar chart race">
> 10-second 1920x1080 video, dark slate background. Title "Top languages 2026" top-left. Five horizontal bars (Python, TypeScript, Rust, Go, Java) grow from zero with staggered starts, overtaking each other twice mid-animation; each bar has a right-edge value label counting up to its final %. End state holds 2s with the leader pulsing once. Hand-draw everything — no chart library. No audio. **Spectacle beat:** The leader's single end pulse is the moment — a +16px overshoot and a bright cap flare, returning to rest exactly on the last frame.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-bar-race.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="Before / after split">
> 8-second 1920x1080 video. Vertical split: left half labeled "BEFORE" shows a cluttered mock UI (grey, 12 overlapping windows drawn in CSS), right half "AFTER" shows one clean card. Both halves settle within the first second. A vertical divider line sweeps left to right at 4s, wiping the clutter into the clean state across the full frame. End on "One tool." centered. No audio. **Spectacle beat:** The divider's wipe is the moment — a bright scan-line travels the split with a bloom as it crosses, and the AFTER half resolves behind it.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-before-after.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="Loader → reveal">
> 7-second 1920x1080 video, black background. A thin white progress ring draws from 0° to 360° over 4s while a percentage counter (0→100) ticks in the center in mono type, matching the arc exactly. Ring and counter fade out fully by 4.2s; at 4.2s the ring bursts outward into short radial dashes and "READY." stamps into the center, then holds. No audio. **Spectacle beat:** The ring's completion at 100% detonates — it flashes white, expands past frame, and the reveal rides that expansion out.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-loader-ready.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="3D cards (Three.js)">
> 9-second 1920x1080 video, light warm cream background. Build the scene in Three.js via the adapter: three rounded card meshes labeled "Design", "Build", "Ship" lie flat on the ground plane, camera at a fixed 3/4 isometric angle, soft directional light + ambient so the cards cast soft shadows. One at a time each card lifts and straightens upright to face the viewer center-frame while the other two slide apart and dim; then it returns. Finish with all three standing upright in a row by 8.5s, hold. All easing power3.inOut. No audio. **Spectacle beat:** As the cards rise into their stack, a single specular sweep rakes across all three faces in sequence, catching each edge.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-3d-cards.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="SVG line-draw logo reveal">
> 6-second 1920x1080 video, deep green background. A minimal mountain-range logo draws on as an SVG stroke over 2.5s, then the stroke fills with cream, the wordmark "NORTHTRAIL" letterspaces in beneath it, and a thin rule expands from center. Hold the last 1.5s. No audio. **Spectacle beat:** The stroke's completion is the moment — the drawn path flares once along its whole length, then the fill floods from that flare.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-svg-logo.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="Word-swap headline">
> 8-second 1920x1080 video, white background. Static sentence "Make it ___." in huge black type stays centered while the blank cycles through "faster", "simpler", "yours" — each swap flips vertically like a split-flap board, 1.5s apart, with a slight blur on motion. Final word "yours." lands in orange and the period pops. No audio. **Spectacle beat:** The last swap lands hardest — that word arrives 1.3× behind a motion-blur streak that resolves as it settles.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-word-swap.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="Stat tile dashboard">
> 10-second 1920x1080 video, near-black background. Four stat tiles slide up in a 2x2 grid, staggered: "99.99% uptime", "42ms p50", "18M req/day", "0 incidents". Each tile's number counts or decrements to its value with its own easing; sparklines draw underneath in teal. At 8s the grid scales back and "Built to hold." fades in above. No audio. **Spectacle beat:** When the fourth tile lands, all four numerals flare in unison for three frames — one synchronized accent, then back to restrained idles.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-data-ticker.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
<Accordion title="Vertical social hook">
> 9-second 1080x1920 vertical video, charcoal background. Social-style hook: "nobody talks about this" types on center in bold white, then each following phrase replaces it on a hard cut every 1.5s — "it's not your code", "it's your prompts", "here's the fix" — with yellow highlight bars behind key words, alternating tilt. Last phrase holds with an arrow-down bounce. No audio. **Spectacle beat:** The final phrase is the payoff — it slams in at 1.35× with a three-frame shake and a chromatic split that resolves fast.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-vertical-hook.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
</Accordion>
</AccordionGroup>
*Next: [Motion that reads premium](/prompting/motion) — Level 3: the grammar rules behind why these moves read as professional instead of generic.*
+88
View File
@@ -0,0 +1,88 @@
---
title: Explainers
description: "What to say to turn an article, notes, or a topic into a faceless explainer — where every visual is invented, not captured."
---
The last ride pointed a workflow at a website. This one drops the site entirely — arbitrary text is the whole input, and every visual downstream is invented rather than captured.
## Your first win
One prompt to [`/faceless-explainer`](/prompting/overview), with your text pasted in, is enough for a finished explainer — no site, no footage, no design decisions required yet.
Verified, from the [examples](/prompting/examples) page — a ~60-second vertical explainer from pasted text:
> /faceless-explainer Turn this into a ~60-second 1080x1920 vertical explainer: [paste your text]. One idea per scene, big typography, diagrams over stock footage, brand color #FF5533 on off-black. Male TTS voice, calm. Embedded captions, keywords highlighted in the brand color.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-explainer.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
Note the `~` — with a supplied script the runtime follows the spoken words, so ask for *about* a minute, not exactly one. See the [anatomy](/prompting/anatomy) for the rest of the skeleton.
## What this makes
A faceless explainer: arbitrary text — an article, notes, a topic, a brief — becomes a narrated video where every visual is invented per scene (typography, abstract graphics, diagrams, data-viz). The [`/faceless-explainer`](/prompting/overview) workflow picks a design system, reshapes your text into a teaching story, generates its own TTS narration, and builds it frame by frame.
**Faceless means there's nothing to capture.** No site, no footage, no asset inventory — the visuals are designed downstream. If you have a product to sell use [`/product-launch-video`](/prompting/product-launch); if you have a real site to show use [`/product-launch-video`](/prompting/product-launch) with a tour brief; a GitHub PR goes to [`/pr-to-video`](/prompting/code-and-prs). Unsure → start at `/hyperframes`.
## The knobs that matter
What you can already steer from the prompt, before you've learned any technique.
| Knob | What to say | Why it matters |
| --- | --- | --- |
| **Verbatim vs summarized** | "use my wording verbatim" or "restructure it freely" | The workflow asks once. Verbatim keeps your voice but locks the word count; summarized lets it cut and reorder for pace |
| **Duration** | "~60 seconds", never "60 seconds" | With a script the narration sets the real length; a hard number forces the agent to trim or pad the words |
| **Scene density** | "one idea per scene" | A faceless scene has one invented focal to animate; two ideas in a scene leave nothing to build the motion around, and it reads as a text dump |
| **Angle** | "concept" / "how-to" / "listicle" / "narrative" | The angle decides the story shape — the workflow reshapes your text into it rather than reading paragraphs in order |
| **Caption style** | "embedded captions, keywords highlighted in the accent color" | Captions are burned in; naming the highlight color ties them to the palette instead of a default pill |
| **Palette** | "brand color #FF5533 on off-black" | With no site to borrow from, the preset supplies a full palette; a named accent + ground personalizes it |
| **Voice** | "male TTS voice, calm" / "warm female voice" | Gender and tone are prompt words; the provider is a workflow decision |
<Tip>
The single biggest quality lever here is scene density. "One idea per scene" turns a dense paragraph into a paced sequence — the workflow reorders and compresses your text to hit it, which is exactly what makes an explainer teach instead of recite.
</Tip>
## Variants
<AccordionGroup>
<Accordion title="30-second landscape topic explainer (16:9)">
> /faceless-explainer Make a ~30-second 1920x1080 explainer on how HTTPS keeps a request private, for a non-technical audience — the takeaway: your data is sealed before it leaves the browser. Concept angle: one idea per scene, big geometric type, a simple lock-and-key diagram as the centerpiece (swap the metaphor with the topic). Near-black ink on off-white with a deep-blue accent. Female TTS voice, warm and clear. Embedded captions, key terms highlighted in the accent color.
Shorter runtime, landscape for YouTube / embed. Fewer scenes means the topic has to compress — naming the takeaway tells the workflow what to keep.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/variant-explainer-landscape.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from this prompt with the topic swapped to HTTP caching (cache diagram as the metaphor), unedited — 26s, because the narration sets the length.*
</Accordion>
<Accordion title="Listicle">
> /faceless-explainer Make a ~45-second 1080x1920 listicle: "5 habits of fast-shipping teams". Listicle angle — one habit per scene, each with a big number and a one-line label, escalating energy toward #1. Off-black with a lime accent. Male TTS voice, upbeat. Embedded captions, the habit label highlighted each scene.
The listicle angle gives each item its own scene with a consistent number-and-label shape, so the structure reads as a countdown rather than a wall of points.
</Accordion>
<Accordion title="How-to with diagrams">
> /faceless-explainer Make a ~60-second 1920x1080 how-to on setting up a CI pipeline, for developers. How-to angle: one step per scene, each built around a simple node-and-arrow diagram that draws on as the narration explains it. Charcoal with a teal accent. Calm male TTS voice. Embedded captions, the step name highlighted.
A how-to leans on diagrams as the load-bearing visual. Describe the diagram *shape* per step ("node-and-arrow", "a pipeline that fills left to right") and let the workflow invent the specifics.
</Accordion>
</AccordionGroup>
## Common failure modes
**"60 seconds" instead of "~60 seconds".** Same rule as [ride 1](/prompting/product-launch#common-failure-modes), and it bites harder here because the script is the whole video — a supplied script's spoken duration isn't knowable until the TTS renders.
- ❌ `a 60-second explainer from this text: ...`
- ✅ `a ~60-second explainer from this text: ...`
**Cramming ideas into a scene.** Every faceless visual is invented around a single focal; overload the scene and there's no clear thing to animate.
- ❌ `explain all five caching layers in one scene`
- ✅ `one idea per scene — one caching layer at a time`
**Asking it to capture or pull real imagery.** There is no capture step; a faceless explainer invents its visuals.
- ❌ `pull screenshots from the site and explain the feature`
- ✅ that's a site or product video — use [`/product-launch-video`](/prompting/product-launch)
**Leaving the look unspecified when you care.** With no brand to read, the preset picks the palette; if you have colors, name them.
- ❌ `make it look on-brand`
- ✅ `brand color #FF5533 on off-black`
*Next: [Code changes and PRs](/prompting/code-and-prs) — point a workflow at a merged GitHub PR instead of a blank page.*
+39
View File
@@ -0,0 +1,39 @@
---
title: When to generate artwork
description: "Code-drawn wins for UI, type, geometry, and 3D; illustration-led hero art comes from an image model, animated as layers."
---
Your Level 1 music slideshow cut real photos to the beat — there was real art to work with. This chapter is for the opposite case: a hero character, a painterly scene, sculptural objects that don't exist as a photo or a footage capture yet.
Code-drawn HTML/CSS/SVG excels where the medium is native: UI mockups, typography, geometric shapes, charts, pixel art, and 3D via Three.js. For **illustration-led** hero art — characters, painterly scenes, sculptural objects — hand-drawn SVG tops out at clipart quality no matter how well you prompt. The fix is a hybrid:
> Generate the hero artwork as an image (via the media-use skill's image generation), then animate it with code. Anything that must move independently — eyelids, light beams, drawing lines — stays a code layer on top of the static art.
Three rules that make this work:
1. **Generate on a solid contrasting background** (magenta works) so the subject keys out cleanly. A light subject on a light background gets silently eaten.
2. **Key flat vector-style art with a chroma key** (`ffmpeg colorkey`), not AI background removal — `remove-background`'s matting model is tuned for photographic and human subjects and reads flat art unreliably, sometimes keeping the background instead of the subject. Reserve `remove-background` for photographs.
3. **Put a restraint clause in the generation prompt** ("minimal, lots of negative space") — image models fill the frame by default, and a busy generated texture reads as cheap as bad vector art.
## The difference, side by side
The same brief — four people arm in arm, warm gradient sweaters, flat-illustration style — built both ways. Same animation beats, same craft layer (lighting, atmosphere, grade); the only variable is where the figures came from.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/artwork-hand-drawn.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Hand-drawn SVG figures — clean, but the proportions and gesture read as clipart.*
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/artwork-generated.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Generated illustration animated as layers — real anatomy and editorial shading; the animated line still passes behind the figures because the artwork was keyed to transparency.*
<Note>
**Capstone thread** — the [Level 7 film](/prompting/capstone)'s Surface region rides on generated artwork: a palette-neutral mural duotoned live by CSS from the composition's two variables, so even the raster re-skins on a `--variables` render (cut from the film, below).
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> […] over a **generated-artwork backdrop** — a flat abstract mural evoking motion trails, generated **palette-neutral (grayscale) and duotoned live by CSS** from the two variables so it re-skins with everything else. **The mural is a full-frame, screen-fixed backdrop: it never moves on screen — the widening iris circle alone reveals it already in place at full frame […] and it exits by fading out.**
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-surface.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause, rendered — the region cut from the finished film.*
*Next: [VFX and liquid glass](/prompting/vfx-and-liquid-glass) — device mockups, frosted glass, and cinematic effects for when the layer on top needs its own spectacle.*
+56
View File
@@ -0,0 +1,56 @@
---
title: Iterating
description: "Talk to the agent like a video editor — small targeted edits beat re-specification."
---
[Editing existing videos](/prompting/editing-existing-videos) already leaned on two habits — one variable per render, absolute targets over relative nudges — for every verb on that page. This is where they come from, stated plainly, so you can apply them anywhere: HyperFrames is a conversation. After the first render, talk to the agent the way you'd talk to a video editor — don't re-prompt from scratch:
> Make the title 2x bigger.
> Swap to dark mode.
> Add a fade-out at the end and a lower third at 0:03 with my name and title.
> The captions are too small and they overlap the lower third. Move them up and shrink them.
> Replace the background music with `assets/track.mp3`.
The agent already has the composition open and the skills loaded — small targeted edits produce better results than long re-specifications.
## The gates can't tell you it's good
Before the loop: know what you are iterating *against*. `lint` and `check` are necessary and not sufficient — they verify structure, not quality. `lint` reads the HTML, `check` runs the composition in a headless browser and reports runtime errors, layout collisions, motion problems, and contrast failures. Every one of those is a fact about whether the composition is *well-formed*. None of them is an opinion about whether the video is *good*.
So the failure mode to expect is: **passes every automated gate, fails human review.** Both earlier builds of this guide's [capstone](/prompting/capstone) did exactly that — one came back clean and read as sparse elements floating in empty frames, the next came back clean and read as a well-dressed slideshow. No linter has an opinion about either.
Which is why the loop below always ends at the render, not at the gate. Watch the output, name what's wrong in specific terms, and change one thing.
## Editing is for nudging, not gambling
Three moves keep iteration convergent instead of circular:
1. **One variable per edit.** Change one thing, render, judge, repeat. A request that moves three things at once ("bigger title, warmer colors, faster cuts") makes it impossible to attribute what helped and what hurt.
2. **Layer one element type at a time.** Building up a complex scene? Start with the minimal version (subject + its motion only), confirm it reads, then add exactly one layer per pass — camera move, then background motion, then style treatment. Every addition stays attributable.
3. **Strip, then re-layer.** When a scene keeps misfiring, don't pile on corrections — strip it back to the simplest version that works (freeze the camera, simplify the motion, clear the background), confirm, then reintroduce complexity one layer at a time until you find the ingredient that breaks it.
## Calibrate with absolute targets
When you're dialing in a look, relative corrections oscillate — "make the dots finer" overshoots, "a bit bigger" overshoots back. State targets as absolute values and the agent lands them in one pass:
- ❌ `make the dots 2x finer`
- ✅ `dot radius = 25% of row spacing, with clear gaps between dots`
- ❌ `the glow is too strong`
- ✅ `glow at ~35% peak opacity, always subtler than the orb itself`
And freeze what already works: "the framing and motion are right — don't touch them; only change the dot size." Without the freeze clause, a rebuild can drift on axes you'd already settled.
<Note>
**Capstone thread** — several clauses in the [Level 7 film](/prompting/capstone)'s prompt exist only because frame review caught the builder drifting — the chip's hard bind to the wire, the pinned mural, the Depth region's occlusion contract. Each catch was folded back into the prompt so the next run can't regress: this chapter's loop, applied to the prompt itself.
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> **The chip sits ON the wire at every moment: bind its position to the wire's actual path geometry (sample the drawn path, seek-safe), never keyframe it independently — through every bend, arc, waveform, and dive, the chip visibly hugs the line. A chip floating off-path breaks the story.**
*Next: [Recreating something you saw](/prompting/recreating-references) — the same absolute-target discipline, applied to matching a specific reference frame by frame.*
+142
View File
@@ -0,0 +1,142 @@
---
title: Media and audio
description: "Ask for the voiceover, music, sound, captions, cutouts, and assets a composition needs — with the precise, unambiguous phrasing the media pipeline acts on."
---
By now your video moves and reads right; this level gives it a voice. HyperFrames owns media *playback*; a companion media pipeline resolves everything else — voice, music, sound effects, images, icons, logos, captions, and background removal. You reach all of it by describing what the composition needs, and the agent resolves each need to a frozen local file. The craft here is precision: vague media asks ("add some music," "no sound") are the ones that come back wrong, because the pipeline does exactly what the words say.
## Voiceover (TTS)
Text-to-speech runs locally through Kokoro — no API key needed — with a HeyGen TTS upsell behind it. Describe the content and the agent picks a fitting voice, or name the voice, tone, and speed directly:
> Generate narration for this script with a professional female voice.
> Add TTS voiceover, British male voice, at 1.1× speed.
The [Vocabulary](/prompting/vocabulary#text-to-speech-voices) table maps content types to Kokoro voices (for example `af_heart` / `af_nova` for a product demo, `am_adam` / `bf_emma` for a tutorial, `af_sky` / `am_michael` for marketing). Name one directly if you already know it; otherwise describe the read and let the agent choose.
- ❌ `add a voice`
- ✅ `warm, unhurried female narration of the quoted script` — tone and pace are what actually change the delivery
## Background music
Music resolves from a large catalog by mood, and it should almost always sit *under* the narration, not compete with it. Give the mood **and** a loudness target — the pipeline can duck and normalize to a level, so an explicit target lands a mix instead of a guess:
> Add subtle electronic BGM, kept under 18 dB so it stays beneath the voiceover.
> Upbeat tech-launch music bed at a low level, ducking under narration.
- ❌ `add background music` — you'll get a full-volume track fighting the VO
- ✅ `subtle background music, ducked ~12 dB under the voice` — a mix instruction the pipeline can execute
<Tip>
A stated loudness target ("under 18 dB," "ducked under the voice") is the difference between music that supports the piece and music that buries it. When there's narration, always say the bed goes under it.
</Tip>
## Sound effects
SFX resolve from a small bundled library plus the catalog. Cue them to specific moments — a transition, a stamp-in, an impact — rather than sprinkling them:
> Add a whoosh on each of the three scene transitions.
> Put a soft click on the button press at 0:04.
## Pace reveals to the narration
Once a video has a voice, the voice is the clock — the next instruction that matters is telling the agent that on-screen elements land **on their spoken cues** — the stat appears as the narrator says it, not at some independent time the builder eyeballed. Without this, narration and visuals drift into two parallel tracks that happen to share a file:
> VO-paced reveals: each scene's elements land on their spoken cues; secondary elements keep resolving while the narrator is mid-thought; the scene is complete just as the narration moves on.
The capstone film applies exactly this rule to every region — its Direction block reads:
> VO-paced reveals: each region's elements land on their spoken cues as the camera arrives; secondary elements keep resolving while the camera is present; the region is complete just as the camera accelerates away.
Two practical notes: the agent gets word timings for free (the narration is transcribed with per-word timestamps, the same machinery behind [captions](#captions-and-transcription)), so "on its spoken cue" is a real, executable instruction — and the inverse rule matters just as much: the narration never waits for the visuals. Pace the camera and reveals to the voice, not the voice to the animation.
## Captions and transcription
Captions come from word-level timestamps. When you generate a voiceover, the timing comes with it; for existing footage, transcription produces the timing (Parakeet by default, with a whisper.cpp fallback). Scaffolding a project from a source video can generate captions from its audio directly.
> Transcribe the narration and add karaoke-style captions synced to it.
> Generate captions from `assets/interview.mp4` and style them hype, scale-pop.
Caption *look* is its own vocabulary (tone, size, per-word emphasis) — see [Captions catalog](/prompting/captions-catalog) for the styles. This page is about producing the timed text; that page is about styling it.
## Background removal (transparent cutouts)
The `remove-background` command mattes a subject out of a video or image locally and hands you a transparent WebM you can drop into any scene as a `<video>`:
> Remove the background from `assets/presenter.mp4` and float the subject over the scene.
One caveat is load-bearing: the built-in model is **purpose-built for people** — head-and-shoulders or full-body, reasonably stable framing, a background that contrasts with the subject. It returns a mostly-empty mask on **non-human subjects** (products, animals, objects). If you need to cut out a product, say so — the agent should route to a different tool rather than run the person model and get nothing.
- ❌ `remove the background from this product shot` with the built-in command — the human-matting model can't see it
- ✅ `matte the presenter out of assets/talk.mp4` (person) — or, for a product, flag that it's a non-human subject so a different matter is used
The [Remove background guide](/guides/remove-background) covers the person-only caveat, the two-layer plate for text-behind-subject, and alternatives for objects and hair-fine mattes.
## Video-in-video and picture-in-picture
Layering footage — a talking head over a scene, a subject in front of a headline, PiP inset — is a compositing prompt. Two grounded rules keep it frame-accurate, and the agent applies them for you, but naming the layout you want helps:
> Put the transparent presenter cutout in the bottom-right, over the chart scene.
> Layer the headline *behind* the presenter so their silhouette occludes the text.
<Note>
Two mechanics the workflow skills handle automatically (from the [Remove background guide](/guides/remove-background#compositing-patterns-and-pitfalls)): a cutout that reveals into view is wrapped in a non-timed `<div>` and the *wrapper* is animated (the framework forces `opacity: 1` on timed clips, so animating the video directly does nothing); and both the base video and the cutout mount at `data-start="0"` so their decoders stay in sync at the cut. You rarely need to say this — but it's why "late-mounting" a PiP clip can land a frame off.
</Note>
## Bring any footage
You don't need to pre-convert supplied footage before naming it in a prompt. If a clip's codec doesn't play back cleanly in a browser — HEVC (H.265) is the common case, straight off an iPhone or a screen recorder — the framework probes the asset and builds a bounded H.264 proxy automatically, cached under `.transcode-cache/`. `preview`, `play`, Studio, and published player pages use the proxy for playback; a render always decodes the original file, so nothing about final quality or color is touched. `hyperframes lint` also flags the asset at info level (`hevc_preview_codec`) so you know a proxy is in play, and it's optional — `--no-proxy` per command, or `media.autoProxy: false` in `hyperframes.json` project-wide. The same mechanism covers alpha-channel sources too (ProRes 4444, alpha WebM proxy to VP9+Opus WebM instead of being refused), so a transparent cutout in a hostile codec isn't a blocker either.
None of this changes how you phrase the ask: name the footage by path like any other supplied asset, and describe the composition you want built from it.
> Build a short picture-in-picture piece from `source-hevc.mp4` — inset it bottom-right over a full-bleed background scene, with a soft rounded border.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/proxy-footage.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited — the source clip is a plain H.265/HEVC file; render decoded it directly via FFmpeg, while preview would have used the automatic H.264 proxy.*
See the [Rendering guide](/guides/rendering#input-video-codecs) for the mechanics — proxy generation, caching, and which codecs it covers.
## The supplied-assets rule
For any asset you already have, an explicit path is the instruction that removes the most ambiguity. The agent will search when you describe an asset, but a path removes every ambiguity about *which* file — and for your own brand assets, it's the only way to guarantee the right one:
- ❌ `use my logo`
- ✅ `use assets/logo.svg`
This matters even when resolution would otherwise work: brand and entity assets should point at *your* file, not a resolved lookalike. (Third-party logos are a separate case — the pipeline pulls official marks from a logo cascade and never hand-redraws them, so "add the LinkedIn logo" is fine; "add my company's logo" needs a path.)
## Say what "no sound" actually means
The most common audio mistake is a negative that means less than you think. "No narration" removes the voiceover — it does **not** silence music or sound effects. If you want genuine silence, say so:
- ❌ `no narration` when you mean a completely silent video — music and SFX can still be added
- ✅ `no audio at all` — the unambiguous way to ask for silence
This mirrors the negatives discipline in [Anatomy](/prompting/anatomy): close the gap explicitly, because the engine acts on the literal words.
## Related
<CardGroup cols={2}>
<Card title="Vocabulary" href="/prompting/vocabulary">Voice names, caption tones, and audio-reactive mappings</Card>
<Card title="Captions catalog" href="/prompting/captions-catalog">Styling the timed text this page produces</Card>
<Card title="Remove background guide" href="/guides/remove-background">The matting command, its person-only caveat, and alternatives</Card>
<Card title="Video components" href="/guides/video-components">Installable overlays, captions, and effects</Card>
</CardGroup>
<Note>
**Capstone thread** — the [Level 7 film](/prompting/capstone)'s Material region runs this chapter's entire pipeline on one clip: generated footage → HEVC auto-proxy → background removal mid-scene → word-synced captions from the clip's own transcription, with the clip's audio ducking the BGM (cut from the film, below).
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> […] a real talking-head clip (generate a short clip of a person speaking one neutral line via the media pipeline's avatar video generation […] **transcode it to HEVC `hvc1`** so the automatic proxy subsystem carries preview) sits as a clip on the wire. The order of operations IS the story: as the camera arrives and BEFORE the person speaks, the framework mattes the footage — **the background peels away via background removal** […] THEN they speak, and the main **keywords of their own line — derived from the clip's transcription — land word-synced** […] The clip's own audio ducks the BGM briefly; the VO resumes as the camera pulls away.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-material.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause, rendered — the region cut from the finished film.*
*Next: [Design systems and brand](/prompting/design-systems) — pointing the agent at a source of brand truth instead of describing a vibe.*
+83
View File
@@ -0,0 +1,83 @@
---
title: Motion graphics
description: "Short, design-led pieces where motion is the message — kinetic type, a stat hit, a logo sting — and the knobs that decide MP4 versus transparent overlay."
---
Five rides in, five different inputs — a site, plain text, a PR, footage, a track. This last one drops narration and asset input altogether: a short, design-led motion graphic where motion alone carries the message.
## Your first win
One prompt to [`/motion-graphics`](/prompting/overview), with the beats spelled out, is enough for a finished motion graphic — no technique required yet.
The canonical shape: routed, spec'd, beat-timestamped, copy quoted, technique pinned, gaps closed.
> /motion-graphics Make an 8-second 1920x1080 video. Beat 1 (0-4s): dark macOS terminal types "npx skills add heygen-com/hyperframes" character by character, then hold on the blinking cursor. Beat 2 (4-5s): the terminal shatters into fragments. Beat 3 (5-8s): bold white kinetic text on black slams in word by word, snappy: "YOU JUST MADE THIS / WITH HYPERFRAMES." Adapt the `code-typing` and `vfx-shatter` registry blocks; hand-author the kinetic text. No narration, no image or media files.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/validate-anatomy-terminal.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
## What a motion graphic is
A motion graphic is a short, design-led piece where **motion is the message** — kinetic typography, a stat count-up, a chart hit, a logo sting, a lower-third or social overlay, an animated map, tweet, or headline. It's usually under 10 seconds (up to ~30s), has no narration and no live-action subject, and renders to an MP4 or a transparent overlay.
Route with `/motion-graphics`. The workflow is autonomous by design — at most one clarifying question, then straight through to render. Reach for a different workflow when the piece grows past what "motion is the message" covers:
| If the piece is… | Route instead |
| --- | --- |
| Longer, multi-scene, or narrated | `/general-video` |
| A narrated video of a website | `/product-launch-video` |
| A topic explainer with a voice-over | `/faceless-explainer` |
| A product promo / launch | `/product-launch-video` |
| Captions on existing footage | `/embedded-captions` |
## The knobs that matter
What you can already steer from the prompt, before you've learned any technique.
**Duration.** Keep it short — motion graphics live under 10 seconds, up to ~30. A 2-second idea stretched to 8 feels slow no matter how it's animated; if the piece genuinely needs multiple scenes or a longer runtime, it's a `/general-video`, not a motion graphic.
**MP4 vs transparent overlay.** The default output is an MP4. Ask for a transparent overlay — a lower-third, a callout, a bug meant to composite over other footage — and the render targets `webm` or `mov` with alpha. Transparency only makes sense when part of the frame is *meant* to be empty. A full-frame design (its own background, edge-to-edge composition) has nothing to be transparent, so asking for a transparent WebM there produces either an opaque file or a broken-looking one. Say "transparent overlay, alpha channel" only for pieces designed to sit on top of something else.
**Registry blocks vs freeform.** Naming a block (`apple-money-count`, `x-post`, `data-chart`, `code-typing`, `us-map` / `world-map`) makes the agent compose reuse-first: install the block, customize in place, hand-author only the gaps. Omit the block and it hand-builds from your description — fine for one-off looks, more drift on the details you didn't pin. Name blocks exactly as they appear in the [catalog](/catalog/blocks/data-chart).
**Easing and motion feel.** The words you use for *how* motion feels — "snappy", "bouncy", "settles with overshoot" — map to specific eases. Spend them; they're cheap precision. See [Vocabulary](/prompting/vocabulary) for the adjective-to-ease table and [Premium motion](/prompting/motion) for the grammar that keeps a piece from reading cheap (nothing fully stops, action overlaps, the camera acts).
## Variants
Each reuses a registry block, so the agent composes rather than hand-building from scratch.
<AccordionGroup>
<Accordion title="Stat count-up">
> /motion-graphics 6-second 1920x1080 video, dark navy background. Beat 1 (0-1s): label "ARR" fades up small, top-center. Beat 2 (1-4s): a giant number counts up to $4.2M with an odometer roll, easing out as it lands. Beat 3 (4-6s): "+312% YoY" stamps in below in green, then everything settles into a gentle ambient idle (subtle breathing scale, slow particle drift). Use the `apple-money-count` registry block as base. No narration.
</Accordion>
<Accordion title="Logo sting">
> /motion-graphics 5-second 1920x1080 logo sting. Beat 1 (0-2s): the word "ACME" assembles from scattered particles. Beat 2 (2-3s): full-frame `swirl-vortex` shader transition. Beat 3 (3-5s): logo lockup + tagline "Ship faster." settles on white, then holds with a subtle ambient idle (slow breathing scale). Use `code-particle-assemble` for the assembly.
</Accordion>
<Accordion title="Animated tweet">
> /motion-graphics 7-second 1080x1350 vertical video. A real tweet card (handle @hyperframes, text "we render video from HTML now. no timeline UI. just code.") slides up over a soft animated gradient, likes counter ticks 0→1.2K, then the card tilts in 3D and a highlight sweeps the second sentence. Settle into a gentle ambient idle on the card at the end. Use the `x-post` and `vfx-liquid-background` registry blocks. No narration, no image or media files.
</Accordion>
<Accordion title="Map route">
> /motion-graphics 8-second 1920x1080 video. Dark world map, a glowing arc animates from San Francisco to Tokyo over 3s, destination pin drops with a pulse, then camera zooms into Tokyo and the label "LATENCY: 89ms" types on. Use the `nyc-paris-flight` registry block as the base pattern, restyle to teal on charcoal. No narration.
</Accordion>
</AccordionGroup>
## Failure modes
**Transparent output on a full-frame design.** Alpha is for overlay elements, not for pieces that fill the frame. A design with its own background has no transparent region to export.
- ❌ `an 8s full-screen stat count-up on dark navy — export as a transparent WebM`
- ✅ `an 8s stat count-up on dark navy, MP4` — or, for a bug to composite over footage: `just the count-up chip, no background, transparent overlay (webm)`
**Narration on a motion-is-the-message piece.** Motion graphics are unnarrated by definition — the visual carries it. A voice-over means a different workflow.
- ❌ `/motion-graphics a 10s logo sting with a voice-over reading the tagline`
- ✅ `/motion-graphics a 10s logo sting, no narration` — for a spoken track, use `/faceless-explainer` or `/general-video`.
**Stretching a short idea long.** Runtime is a knob, and past ~30s a single motion beat runs out of things to do.
- ❌ `a 45-second kinetic-type piece of one headline`
- ✅ `an 8-second kinetic-type piece of one headline` — or promote it to a multi-scene `/general-video`.
<Tip>
For the six-part skeleton and the per-beat content formula these prompts share, see [Prompt anatomy](/prompting/anatomy); for the full set of run-verified examples, [Verified examples](/prompting/examples). Unsure whether your ask is a motion graphic at all? Start at the router in `/hyperframes`.
</Tip>
*Next: [Prompt anatomy](/prompting/anatomy) — you've been riding workflows through six one-prompt inputs; Level 2 opens the prompt itself.*
+242
View File
@@ -0,0 +1,242 @@
---
title: Motion that reads premium
description: "Eight motion-grammar rules from frame-by-frame study of professional work — nothing stops, the camera acts, action overlaps, imperfection stays reproducible."
---
By now you can specify a frame precisely — exact copy, exact colors, exact layout, exact timing. This level makes it move. But "make it move" is the wrong instruction, and it is worth understanding why before you write a single motion clause.
## Motion is a claim, not a coat of paint
**Every movement says something about the thing that moves.** A number that breathes says *this figure is live, it is still counting*. A card that flies in from the right says *it came from somewhere, there is more offscreen*. Overshoot says *this has mass and it arrived eagerly*. A slow, heavy entrance says *this matters*. A jitter says *this is unstable*. You do not get to opt out of this: motion is read as meaning whether or not you intended meaning, so the only question is whether what it says is true.
That reframes the useful question. Not *"does this feel too static?"* but **"what is this movement saying, and do I mean it?"** A stat card that breathes because the number is live is telling the truth. The same card breathing because the frame looked empty is telling the viewer a live-data story about a static figure — and viewers feel the mismatch even when they can't name it.
The strongest motion goes further and works as a metaphor for the content itself. A deployment pipeline should *flow*. A build should *assemble*. A countdown should *tick*, discretely, the way time does. A route should *draw* in the direction of travel. When the movement and the meaning are the same gesture, you stop needing to explain the graphic.
**The failure this prevents is the common one: adding amplitude when a piece feels dead.** More motion is not more meaning. Four decorative movements are worse than one motivated movement — they compete for the eye, none of them says anything, and the result reads as busy rather than alive. If a shot feels lifeless, the fix is usually to find the one thing that genuinely *is* changing and let that carry the frame, not to set everything else wobbling.
A practical test: name what each movement means, in words, in a sentence that ends. *"The counter breathes because it's still receiving data."* *"The cards stagger because they arrive over the wire, not all at once."* If you can't finish the sentence, cut the movement.
Four jobs cover almost everything a movement can honestly be doing:
| The job | What it looks like |
| --- | --- |
| **Direct attention** | Motion pulls the eye to the thing that matters this second — a count landing, a line igniting |
| **Carry continuity** | Motion ties one moment to the next so the piece reads as one space, not a sequence of slides |
| **Show change** | Motion *is* the information — a value rising, a diff resolving, a route drawing |
| **Express character** | Motion carries the brand's temperament — snappy and confident, or calm and editorial |
If a movement is doing none of those, it's decoration, and decoration is what makes a video read as cheap no matter how smooth the easing is. (Product-UI motion frameworks name a fifth job, *feedback* — confirming a user's action. Video has no user input, so it doesn't apply here.)
## The grammar
Once a movement has something to say, this is how to make it read well. These rules govern *execution*; none of them is a reason to add a movement that has no job:
1. **Nothing ever fully stops.** Every "hold" carries ambient idle motion — a 1-2% breathing scale, slow drift, a shimmer. Never write "holds motionless"; write "settles into a gentle ambient idle." A frozen final second is the single biggest cheap-motion tell.
2. **The camera is an actor.** Give each scene one continuous camera move — a 4-8% push-in, a slow orbit, parallax between layered planes — easing gently and never *decaying* to a dead stop at the end of a scene (compute the ease over a window slightly longer than the render). But the reason isn't that stillness looks cheap: a move is what carries the **spatial relationship** between two shots. A cut or a cross-fade throws that away, so two views of one system read as two unrelated pictures. Move to get somewhere and the audience infers one continuous space for free. Beware the corollary — translating the scene and translating the camera are the *same transform*, so "the content slid past a fixed frame" is not a static alternative, it is the same shot. A deliberate mid-film rest is a different thing, and it is sanctioned: see [avoiding the slideshow](#avoiding-the-slideshow).
3. **Overlapping action.** No two elements share a start or end time. Entrances stagger at irregular offsets; the next element begins while the last is still settling. The mechanism is a ratio: **each offset should be clearly shorter than the animation it offsets** — a small offset-to-duration ratio reads fluid, a large one reads deliberate, and an offset longer than the animation stops being a stagger and becomes a queue. Delay the *supporting* elements, never the focal one; a focal element that waits reads as lag. And stagger what is **happening**, not what is merely **present**: things that arrive in sequence should animate in sequence, but a set of things that already existed before the shot started should appear together — staggering those just makes the scene take longer to assemble.
4. **Compound properties only when they tell one story.** Every animation changes at least one property — position, scale, opacity, rotation. Combining them works when they say the same thing (a confirmation sliding up *and* fading in both read as "arriving") and confuses when they don't (a sheet that rises *and* rotates is two unrelated claims about one object). Pick the properties that serve the movement's job and drop the rest. Direction follows from the same logic: entrances ease **out** (fast in, gentle settle — the object arrives), exits ease **in** (gentle start, decisive departure), on-screen moves ease **in-out**, and impacts — a stamp, a slam — ease **in**, because an impact is an acceleration.
5. **Overshoot and follow-through.** Overshoot is a claim about mass: a thing with weight passes its target and settles back, and the parts it drags — a shadow, a trailing panel, the housing it lands in — resolve a beat *after* it does, not with it. That one-frame lag is what sells the weight; perfectly synchronized "follow-through" is just a bigger single object. Apply it to **transforms only**. A number is not a physical object, so a counter must never fly past its value and fall back — that renders a figure that was never true, and on a real metric it's a lie with a frame count.
6. **Depth planes.** Move each plane at a rate that matches its distance — far background at a fraction of the camera's rate, content at full rate, foreground at several times. The cue that does the most work isn't blur, it's **occlusion**: one large, heavily-blurred foreground element that actually crosses in front of your content proves there is space between them in a way no amount of blur on an element that never overlaps can. Prefer one such element over two — a second stops reading as the room and starts reading as decoration.
7. **Match pacing to genre.** Showreel-style cuts run 1.5-4 seconds per idea; a stretched 8-second version of a 2-second idea feels slow no matter how it's animated.
8. **Handmade imperfection stays reproducible.** A stop-motion or hand-animated feel wants irregular timing, not machine-smooth eases — but true unseeded randomness breaks the render: HyperFrames must produce the identical frame every time it seeks to the same point, and `Math.random()` can't promise that. Seed a PRNG once at composition start instead, and step discrete elements between held positions (a "two-frame hold": land, hold two frames, jump to the next offset) rather than tweening continuously between them. The irregularity is real — it just comes from a seed, not from chance.
## Each rule, shown
One A/B per rule, and the prompt that produces each half. Both halves come from the same base composition — same content, palette, layout and timing — so the two prompts below are the *only* difference between them. That's the point: you can see what the words buy.
Each subject is chosen so the rule is *unavoidable* given the content: a live feed that freezes reads as a broken connection, a pipeline wider than the frame forces the camera to move, a split-flap board cannot stop dead because it has mass. The values are pushed far enough to be legible in a six-second loop, but every movement in the applied half can finish the sentence "this moves because…". If you find yourself copying a number here without being able to finish that sentence, you're copying the exaggeration and not the rule.
### Rule 1 — Nothing ever fully stops
❌ **Without**
> 6-second 1920x1080 live telemetry panel: an `ARR · LIVE` eyebrow with a status dot, `PROD · US-EAST-1` opposite, `$4.200M` at display scale with a delta chip, a sparkline of the reading history beneath, and a footer row reading `LAST SYNC` and `READINGS`. The panel rises in and lands by 1.9s, then holds motionless to the end — the dot stops, the figure freezes, the sparkline stops extending, the footer stops updating.
✅ **With**
> 6-second 1920x1080 live telemetry panel: an `ARR · LIVE` eyebrow with a status dot, `PROD · US-EAST-1` opposite, `$4.200M` at display scale with a delta chip, a sparkline of the reading history beneath, and a footer row reading `LAST SYNC` and `READINGS`. The panel rises in and lands by 1.9s, then stays alive because the feed is — a new reading arrives every 300ms and each one moves exactly what it should: the last digits change, the sparkline extends one segment with its leading dot, `LAST SYNC` restamps, `READINGS` increments. The status dot keeps a 1s heartbeat because the connection is up. Nothing else moves. Fix the sparkline's y-scale up front so an arriving reading never rescales the axis.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/motion-rule1-nothing-stops.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Left `WITHOUT`, right `WITH` — from 2.0s the left's 120 remaining frames share a single hash; all 180 of the right's are distinct. Every movement on the right answers "because": the dot beats because the connection is up, the digits change because a reading arrived, the sparkline extends because that reading is now history. That's the test — not "is there enough motion" but "can each thing that moves say why".*
*The clearest artifact is the footer. The frozen half reads `LAST SYNC 09:41:21.75 · READINGS 22` while the live one reads `09:41:23.95 · 30`. A still frame of the left half looks like a perfectly good stat card; the moment it sits next to a running one you can read exactly how long the feed has been dead. That is what a frozen hold communicates to a viewer, whether or not you meant it.*
### Rule 2 — The camera is an actor
❌ **Without**
> …an isometric deploy pipeline of five stages, each with its own accent colour — `SOURCE` mint, `BUILD` cyan, `TEST` violet, `DEPLOY` amber, `EDGE` rose — bolted along a rail with support posts and numbered floor bays. The block at each stage and its card wear the same colour. **The camera never moves.** Stack all five stages on the same centred spot and cross-fade between them: the current card fades out as the next fades in already coloured, and the block beneath it changes to that colour. No wire and no work in transit. Nothing in the frame changes position at any point.
✅ **With**
> …an isometric deploy pipeline of five stages, each with its own accent colour — `SOURCE` mint, `BUILD` cyan, `TEST` violet, `DEPLOY` amber, `EDGE` rose — bolted along a rail with support posts and numbered floor bays. The block at each stage and its card wear the same colour. Lay the stages out where they really are, 640px apart across 2.5 frame widths, and **move the camera** across the whole run in one continuous linear pass, `ease: none`. The floor and rail travel with the camera 1:1 because they are the same space; distant structures track at a fifth. A packet of work runs the wire and ignites each stage in its colour as it reaches it, so a stage is revealed by arriving at it rather than by appearing.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/motion-rule2-camera-actor-v2.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Left `WITHOUT`, right `WITH` — same five stages, same colours, same six seconds. On the left the block never leaves the centre of frame: five stages, one place. On the right you travel to each one, and neighbours stay visible on the way, so the five read as five points along a single run.*
*The rule is usually stated as "give every scene a camera move," which undersells it. The real claim is about what a **cut throws away**. A move carries the spatial relationship between two shots; a cross-fade carries none, so the colour simply becomes a different colour where it stood. Watch the amber `DEPLOY` block still sitting behind frame-left as the camera settles on rose `EDGE` — that adjacency is information, and it does not survive a dissolve.*
*There is a trap in this one worth knowing: **translating the scene and translating the camera are the same transform.** Earlier versions of this demo had the locked half slide the content past a fixed frame, then step it, then freeze the backdrop behind it — and every one looked identical to the pan, because it was. Measured, 3% of pixels differed. Only refusing to move at all produces the 3047% here. If your "static" comparison still moves the content, you have built the same shot twice.*
### Rule 3 — Overlapping action
❌ **Without**
> …`Rolling out to every region` over a dotted world map with twelve edge regions marked and a live `n / 12` tally. A progressive rollout takes each region live: the region's core ignites, its dot field turns mint, and the tally counts. All twelve ignite at the same moment, 0.5s each.
✅ **With**
> …`Rolling out to every region` over a dotted world map with twelve edge regions marked and a live `n / 12` tally. A progressive rollout spreads outward from `us-east-1`, reaching each region in order of its distance from the origin, 0.09s apart: a link draws from the nearest already-live region and lands exactly as that region's core ignites, its dot field turns mint, a ping carries onward and an ACK dash returns up the link. Each region's 0.5s ignite is still running when the next three begin.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/motion-rule3-overlapping-action.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Left `WITHOUT`, right `WITH` — twelve distinct ignition frames on the right, 0.088s apart, against a single frame on the left. At peak, all twelve regions are animating at once.*
*Stagger here isn't a styling preference, it's the difference between a true statement and a false one. A rollout propagates; twelve regions cannot go live on the same frame, and the simultaneous version says something about the system that isn't true. Watch the tally: the left jumps `0 → 12` in one frame and the map gives you no way to tell where the rollout started, because there is no order to read. Note also what is **not** staggered — the twelve region markers all appear on the same frame in both halves, because the regions already existed. Stagger what is happening, not what is merely present.*
### Rule 4 — Compound properties only when they tell one story
❌ **Without**
> …an itemized coffee-shop receipt; a payment-confirmation sheet rises 470px over 1.2s to cover its lower half while simultaneously rotating in from 18°, scaling up from 0.8, and un-blurring from 14px. The receipt dims behind it. The sequence plays twice.
✅ **With**
> …an itemized coffee-shop receipt; a payment-confirmation sheet rises 470px over 1.2s to cover its lower half while fading up from 0. Rotation, scale and blur are never touched. The receipt dims behind it. The sequence plays twice.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/motion-rule4-compound-properties.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Left `WITHOUT`, right `WITH` — identical travel (470px), identical window (0.25s → 1.45s), identical ease. The only difference is how many properties ride along. Both halves are pixel-identical at rest, so everything you see mid-flight is the cost of the extra three. On the left, "Payment confirmed" is an unreadable tilted smear for most of its arrival; on the right it is legible the whole way up. Two properties agreeing on one claim — this is arriving — beat four properties each making a different one.*
### Rule 5 — Overshoot and follow-through
❌ **Without**
> …a bolted airport departure board, `HF 402 · SAN FRANCISCO`, its split-flap gate cells riffling to **42**. The flap plates drop 34px and rotate from 16° straight to their final position and stop. Linear throughout, no overshoot, and the cast shadow moves locked to its plate. The sequence plays twice.
✅ **With**
> …a bolted airport departure board, `HF 402 · SAN FRANCISCO`, its split-flap gate cells riffling to **42**. The flap plates drop 34px and rotate from 16° on `back.out(5)`, swinging past their rest position and settling back; the housing recoils ~7px when they land, and each plate's cast shadow reaches its own peak one frame later than the plate that casts it. Transforms only — the gate number never overshoots **42**. The sequence plays twice.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/motion-rule5-overshoot.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Left `WITHOUT`, right `WITH` — the right's plates swing 1.51× past their travel before settling; every transform on the left has peak == final. The shadow's lag is measured at 0.033s, exactly one frame at 30fps. Note the one thing that does **not** overshoot on either side: the number itself tops out at 42 and stays there. Overshoot is a claim about mass, and a value has none — a counter that flies to 47 and falls back is reporting a number that was never true.*
### Rule 6 — Depth planes
❌ **Without**
> …a render-farm corridor: CSS-3D floor and ceiling converging on a single vanishing point, a bank of server racks with blinking status LEDs, a distant rack row near the horizon, and one rack column close to the lens. `CLUSTER 04 · WEST / Every frame, in parallel.` on a camera-locked HUD panel. The camera trucks forward for the full 6s. Every plane drifts at the same rate and nothing is blurred.
✅ **With**
> …a render-farm corridor: CSS-3D floor and ceiling converging on a single vanishing point, a bank of server racks with blinking status LEDs, a distant rack row near the horizon, and one rack column close to the lens. `CLUSTER 04 · WEST / Every frame, in parallel.` on a camera-locked HUD panel. The camera trucks forward for the full 6s and each plane drifts at its own distance: the horizon row at a fifth of the room's rate, the room and its racks at full rate, and the near column sweeping past at six times — `blur(22px)`, close enough to cross in front of the headline. Haze the far plane slightly.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/motion-rule6-depth-planes.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Left `WITHOUT`, right `WITH` — measured travel ratios on the right are exactly 0.20× / 1.00× / 6.00×; on the left all five planes displace an identical 240px with `filter: none`. The strongest cue isn't the blur, it's the occlusion: the near column crosses in front of the headline for about a third of the shot, which is only possible if it's in the room. One foreground element does this — a second would just be clutter with a blur on it.*
### Rule 7 — Match pacing to genre
❌ **Without**
> …a three-beat product showreel, each beat a titled panel with its own live evidence: `SEEK ANY FRAME` over a scrubbing timeline reading out frame 147, `RENDER ON LAMBDA` over six worker lanes filling their own frame ranges, `SHIP THE MP4` over a player with an encode bar. 2 seconds per beat, dissolving between them, filling the full 6s runtime. A rail under each half tracks time consumed and time spare.
✅ **With**
> …a three-beat product showreel, each beat a titled panel with its own live evidence: `SEEK ANY FRAME` over a scrubbing timeline reading out frame 147, `RENDER ON LAMBDA` over six worker lanes filling their own frame ranges, `SHIP THE MP4` over a player with an encode bar. Showreel pace — 1.1 seconds per beat, cutting hard rather than dissolving, all three delivered by 3.3s, then hold the last beat on a live idle. A rail under each half tracks time consumed and time spare.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/motion-rule7-pacing.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Left `WITHOUT`, right `WITH` — same three ideas, same evidence, same 6 seconds. At 3.6s the left is still working through beat 2 with `SPARE 0.00`; the right has already delivered all three and banked `+0.33s`. The left is not badly animated, it is correctly animated at the wrong tempo — which is why this failure survives review so often. Pace is a genre decision you make before you touch an ease.*
### Rule 8 — Handmade imperfection stays reproducible
❌ **Without**
> …a hanging paper mobile: five cut-paper shapes on fine visible wires from a wooden dowel, each with real paper grain, irregular hand-cut edges and its own layered drop shadow. They sway through a ±12° arc with smooth continuous tweens. A readout shows the current frame and how many distinct states the six seconds contain.
✅ **With**
> …a hanging paper mobile: five cut-paper shapes on fine visible wires from a wooden dowel, each with real paper grain, irregular hand-cut edges and its own layered drop shadow. They sway through the same ±12° arc, but driven by a mulberry32 PRNG seeded once at composition start and stepped on a two-frame hold quantized to the integer frame index — so the cadence is stop-motion, not a tween, and every render is byte-identical. A readout shows the current frame and how many distinct states the six seconds contain.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/motion-rule8-seeded-handmade.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Left `WITHOUT`, right `WITH` — the right holds each pose for exactly two frames: 90 states across 180 frames, no irregular holds, while the left has 180 distinct ones. Rendered twice, the whole thing came back byte-identical both times. This is the rule that is easy to get wrong in the wrong direction — reaching for `Math.random()` to buy the same irregularity produces a composition that renders differently every time and can never be re-rendered or resumed.*
## The grammar, measured
The same composition built twice — identical content, identical layout; the only variable is the motion treatment. The first build commits the anti-patterns above: simultaneous identical entrances, no camera, a frozen final 2.4 seconds. The second applies the eight rules.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/motion-before.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Every anti-pattern: tiles enter together, nothing overlaps, the last seconds are a freeze-frame.*
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/motion-after.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*The eight rules: staggered overshoot entrances, a continuous 5% push, ambient idle, a blurred foreground plane, sparkline draw-ins as secondary motion.*
The difference is measurable, not just visible: in the final second the frozen build has bit-identical consecutive frames, while the motion-grammar build changes every single frame — corroborated by the encoder (211KB vs 2.5MB for the same content). A frozen final second is the cheap-motion tell you can test for.
## Avoiding the slideshow
The eight rules govern motion *inside* a scene. Apply all eight, cut between scenes, and you can still end up with a slideshow: a sequence of well-animated cards that happen to play in order. It's the most common note on multi-scene work, and the rebuild passes on the [Level 7 film](/prompting/capstone) after its slideshow diagnosis are what taught us the two properties — neither fix was more animation.
**What a slideshow actually is:** a run of scenes that are *independent* (nothing connects one to the next) and *uniform in energy* (nothing tells you which moment matters). So any fix has to supply two properties:
1. **Something crosses the boundary.** Consecutive scenes need a shared element, space, or motion the eye can follow through the transition — otherwise the viewer re-orients at every seam and reads slides.
2. **The energy varies.** The piece needs quiet against which a hero moment can register. Uniform motion is as flat as no motion; emphasis is a *contrast* effect.
Ask for those two properties, not for a specific device. Below is the device we used, then several others that satisfy the same two properties differently — pick whichever fits your piece.
### One method: a continuous world-camera
The capstone treats the whole film as a single wide space the camera travels across, with three elements that never leave frame and a dwell-and-sweep rhythm on the camera itself.
For property 1, "no hard cuts" was not enough on its own — the first rebuild honored it and still played as slides, because each region faded up centered, sat, and faded out while the camera politely waited. What worked was naming the things that survive the boundary: **persistent elements** (a wire that becomes the chart baseline, then the map arc, then the waveform, then the 3D coil; a frame-ruler; a protagonist chip), **arrivals instead of cuts** (the next region is already at the edge of frame before the camera reaches it; the previous exits by parallax), and **a negative that names the tell** (agents default to the slideshow because it is safe — forbid it explicitly).
For property 2, the second rebuild fixed continuity and broke emphasis: the camera never stopped, so every hero moment landed mid-sweep and got thrown away. The fix was a **dwell** — a genuine 1.52.5s full stop while the point lands. Two subtleties keep this consistent with the rules above. Against rule 1: during a dwell the **camera** rests, the **film** does not — counters keep ticking, labels keep stamping. Say that, or you trade a slideshow for a freeze-frame. Against rule 2: a dwell is a mid-film rest the piece resumes from, not the decayed settle that rule guards against — what rule 2 forbids is a camera that runs out of energy and dies on the last frame.
<Note>
**Capstone thread** — this is the method the [Level 7 film](/prompting/capstone) uses, and the reason it reads as a film instead of nine demos in a row.
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys both properties — prompt language you can lift for your own video:
> ONE continuous camera journey with a dwell-and-sweep rhythm. The whole film is a single wide world (one large scene space) that the camera dollies across […] in one unbroken move with NO hard cuts anywhere except inside the two sanctioned seam moments named below. The rhythm: the camera SWEEPS between regions, then eases to a genuine REST at each region's hero moment — a full stop of roughly 1.52.5 seconds while the main point lands […] then accelerates away. The dwell is what gives the sweep meaning: without the rest, emphasis dies mid-motion. During a dwell the WORLD keeps resolving (counters tick, labels stamp, secondary motion continues) — the camera rests, the film never freezes.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-camera.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause, rendered: accelerating out of one region, arriving in the next, stopping to let the beat land — no cut anywhere in the span.*
This method is expensive. One continuous space is more to author than a cut, and it only pays when the piece has a through-line worth following. For a 6-second stat card, cut.
### Other methods worth trying
Each of these supplies both properties by different means. They also compose — a beat grid pairs happily with a hero prop.
| Method | How it crosses the boundary | Where the emphasis comes from |
| --- | --- | --- |
| **Match cuts** ([Transitions](/prompting/transitions)) | Cut freely, but on a shared shape, color, or motion vector, so the eye carries a form through the seam | Break the rhyme deliberately at the moment that matters |
| **A hero prop** ([Storyboards](/prompting/storyboards)) | Cut the environment, keep one object continuously on screen (the product, a chart, a character) | Push in on the prop and still everything else around it |
| **A persistent frame** | Scenes change *inside* a frame that never does — a HUD, a ruler, a browser shell, a border | Let the chrome fall away for one beat; its absence is the accent |
| **Audio as the spine** ([Media and audio](/prompting/media-and-audio)) | One unbroken sentence or musical phrase crosses the cut; the ear ties what the eye didn't | A rest in the audio — a held note, a beat of silence before the number lands |
| **Beat-locked cutting** | Cuts land on an analyzed grid, so the rhythm itself is the connective tissue ([Music](/prompting/music-and-slideshows)) | Hold through a downbeat, or drop a hit, so one card gets twice the space |
| **Accumulation** ([Storyboards](/prompting/storyboards)) | Nothing ever exits — each scene adds to one canvas and the frame reflows to the new material | Everything but the newest element quiets to a low idle |
Whichever you choose, put the two properties in the prompt in your own words, and name the tell you are forbidding. The device is negotiable; the properties are not.
## Handmade, still deterministic
Rule 1's ambient idle is *continuous* — a slow, smooth breathing scale. Rule 8 is a different tool for a different job: when the brief wants something that reads as physically hand-animated (paper cutouts, stop-motion, a wobbly hand-drawn line), continuous easing is the wrong texture — it reads as digital no matter how organic the curve. What you want is irregular, but reproducible:
> A paper-cutout garden scene: flowers, leaves, and a bee drifting on visible wires, like stop-motion. Seed a random offset per element at the start (same seed every render) so each leaf sways to a slightly different rhythm, but hold each position for exactly two frames before stepping to the next — discrete jumps, not a smooth tween. It should read as intentionally handmade, not glitchy.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/motion-handmade.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited — paper cutouts on visible wires, every position held for exactly two frames, and byte-identical across two separate renders.*
Name the seed and the hold length explicitly — "seeded" and "two-frame hold" are the two words that keep an agent from reaching for `Math.random()` and quietly breaking every re-render.
<Warning>
**Ask for the hold in frames, not seconds.** Validating this prompt surfaced a real trap: quantizing the hold in the *time* domain (`Math.floor(t / 0.0667)`) produces irregular holds — a three-frame group here, a one-frame group there — because the renderer's seek times don't land on exact 1/30-second doubles, so the arithmetic drifts across step boundaries. Quantize on the integer frame index instead (`Math.floor(Math.round(t * fps) / 2)`), which tolerates any seek error under half a frame. The visible symptom is a stop-motion cadence that stutters unevenly instead of ticking, and it is worth naming in the prompt: "hold each position for exactly two frames, quantized on the frame index." Note also that a hold specified in frames is coupled to the composition's fps — re-rendering at a different frame rate changes the hold's duration.
</Warning>
Ask for it, and seeking to frame 214 twice gives you the identical frame twice, exactly like every other HyperFrames render — the imperfection is designed in, not left to chance.
Unseeded randomness is the obvious way to break cold-render correctness, but a handful of GSAP lint rules catch subtler seek-order mistakes — relative tweens colliding with another writer, `repeatRefresh` combined with a relative value, function-valued tween vars, DOM measurement inside timeline callbacks — see the [seek-order safety rows](/prompting/rules-and-anti-patterns#seek-order-safety) in the appendix if the linter ever flags one.
*Next: [Transitions](/prompting/transitions) — the same grammar applied to the cut between scenes, not just the motion inside one.*
+94
View File
@@ -0,0 +1,94 @@
---
title: Music videos and slideshows
description: "Two music- and slide-driven outputs that look alike in a brief but ship differently — a beat-synced MP4 versus a navigable deck — and how to route to the right one."
---
You've dressed footage and turned a PR into a story — now the driving input changes again: a track's own beat, or a deck of slides, sets the pace instead of a script.
## Your first win
One prompt to [`/music-to-video`](/prompting/overview), pointed at a track and some photos, is enough for a finished beat-synced video — no technique required yet.
The verified starting point: photos cut to a track, exported to a square MP4.
> /music-to-video 20-second 1080x1080 video from ./track.mp3 (pick the best 20 seconds of the track) and the 8 photos in ./shots/. Cut on the beat grid, one photo per bar, punch-in on downbeats, `whip-pan` transitions on phrase changes. End on the last photo with "SUMMER '26" in condensed caps. No TTS.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-music-slideshow.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
Every timing decision here is delegated to the track's own analysis — you describe the *treatment* ("one photo per bar", "punch-in on downbeats"), and the beat grid supplies the *times*.
## Two outputs that a brief blurs together
"Make a slideshow from these photos and this track" and "make a slideshow deck for my pitch" both say *slideshow*, but they produce different things and route to different workflows. Name the output you want up front.
| You want | Route | Output |
| --- | --- | --- |
| Photos / clips cut to a music track, exported as a video | `/music-to-video` | A beat-synced **MP4** with audio |
| A presentation you click through — slides, reveals, speaker notes | `/slideshow` | A **navigable deck**, not an MP4 |
`/music-to-video` turns a **music track** — an audio file, a video to pull audio from, or a track generated from a mood brief — into a beat-synced video. The music drives all pacing; any photos or clips you supply are cut onto the same beat grid, and a complete video needs zero assets (typography carries it otherwise). There is no narration and no website capture.
`/slideshow` authors a HyperFrames deck — discrete slides with fragment reveals, hotspot branching, and a built-in presenter mode with speaker notes. Its output is the **running deck**, served with `hyperframes present`. Do not point `render` at a deck: it resolves only the first scene and emits a silently truncated MP4. If the user didn't explicitly ask for a slideshow, the skill confirms the deck route before authoring — that's a routing decision, not a style preference. One authoring detail worth knowing: fragment reveal times are absolute positions on the deck's master timeline, not per-slide offsets.
## The knobs that matter
What you can already steer from the prompt, before you've learned any technique.
**The beat grid.** `/music-to-video` analyzes the track once into energy phases, onsets, rolls, silences, hard stops, and phrases, then cuts at real musical changes. You steer *how* it cuts, not *when*: "one photo per bar" sets cut density, "punch-in on downbeats" adds the accent, "transitions on phrase changes" reserves the visible moves for structural boundaries. On genuinely rhythmic music the grid is trustworthy and cuts snap to the beat; on calm music the grid is a metronome the analyzer imposed, so the skill paces by phrase and energy instead of hard-cutting — say "let it flow, no hard cuts" if the track is ambient.
**Track section — describe, don't timestamp.** Ask for "the best 20 seconds" or "the verse into the hook" and let the analyzer choose boundaries that land on musical anchors. Hard timestamps ("use 0:320:52") cut mid-phrase and fight the grid.
**Asset supply.** Zero assets is valid — typography and templates carry a complete video. Any photos or clips you hand it are woven in *on the same beat grid* (beat-cut or Ken Burns), so more assets means more to cut between, not a different pacing model. Point at a directory ("the 8 photos in ./shots/") and name the end card.
**Deck structure (slideshow).** Fragments (reveal hold-points), hotspots + branch sequences (off-line detail slides), and presenter notes are the deck's structural knobs. Ask for them by name — "reveal the bullets as fragments", "branch to a detail slide from a hotspot", "add speaker notes" — and the island wiring follows.
## Variants
<AccordionGroup>
<Accordion title="Lyric video">
> /music-to-video 30-second 1080x1920 lyric video from ./song.mp3 (pick the strongest 30-second section — a verse into the hook). Transcribe the vocals for word timing. Lines rise in one at a time on the beat, big condensed type on a dark grain background; the hook lands with each word punching in on its downbeat. Keyword in each line highlighted in acid green. No photos — typography only. No TTS.
Word-level timing comes from transcribing the track (or from lyrics you paste, placed on the beat grid). No supplied assets needed — type is the whole video.
</Accordion>
<Accordion title="Kinetic promo from a mood brief (no track)">
> /music-to-video 15-second 1080x1080 kinetic promo. No track supplied — generate one: driving synthwave, high energy. Cut hard on the beat: full-frame word cards ("FASTER", "SHARPER", "SHIP IT") slam in on downbeats, alternating black/white with inverted type, a glitch flash on each phrase change. End on the wordmark "NOVA" holding with a subtle ambient idle. No TTS.
With no audio supplied, the track is generated from the mood you describe; the beat grid it produces still drives every cut. Fast, high-energy briefs suit this workflow best.
</Accordion>
<Accordion title="Presentation deck (slideshow)">
> /slideshow Build a 5-slide pitch deck, 1920x1080. One idea per slide, each headline a complete-sentence claim (not a label), punchline first. Slide 2 reveals three pain points one at a time as fragments. Slide 3 shows bottom-up market math (accounts × ACV), not a bare "$40B TAM". Add presenter notes to every slide, and a hotspot on slide 3 that branches to a "sizing methodology" detail slide. I'll present it with `hyperframes present`.
This produces a clickable deck, not a video. Fragments are reveal hold-points inside a slide; the hotspot branches off the main line and returns on Back. Headlines follow the deck's hard rules — complete-sentence claims, one idea + one visual per slide, font no smaller than a 30pt equivalent.
</Accordion>
</AccordionGroup>
## Failure modes
**Hard track timestamps.** The whole point of `/music-to-video` is that the track's structure sets the cuts. A literal time window ignores the analyzed beat grid and lands cuts mid-phrase.
- ❌ `use the section from 0:32 to 0:52`
- ✅ `pick the best 20 seconds of the track`
**Expecting an MP4 from `/slideshow`.** A deck is authored as several top-level scenes with no master-root composition, so `render` resolves only the first one and truncates. The supported outputs are the live `present` deck and per-slide snapshots.
- ❌ `/slideshow ... then render it to deck.mp4`
- ✅ `/slideshow ... I'll present it with hyperframes present` — or, if you actually need a rendered video, use `/music-to-video` (beat-synced) or `/general-video`.
**Wrong workflow for the output.** Photos set to music that you'll export and post is `/music-to-video`; a thing you click through live is `/slideshow`. Picking by the word "slideshow" alone builds the wrong deliverable.
<Tip>
Both prompts here are unnarrated. `/music-to-video` has no TTS by design; if you want a spoken voice-over instead of a music bed, that's a different workflow (see the router in `/hyperframes`). For the six-part skeleton these prompts share, see [Prompt anatomy](/prompting/anatomy); for adjectives that map to eases and transitions, [Vocabulary](/prompting/vocabulary).
</Tip>
<Note>
**Capstone thread** — the [Level 7 film](/prompting/capstone)'s Rhythm region cuts media cards onto a real analyzed beat grid from `hyperframes beats` — the film's only sanctioned hard cuts, every one on a detected beat while the camera keeps traveling (cut from the film, below).
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> **Rhythm (4552s).** The wire becomes a waveform: **resolve the BGM first, run `hyperframes beats` on it, and drive this region on the detected grid** — the waveform pulses and compact media cards (a lyric line, a photo card, a chart flash) snap onto the wire on real analyzed beats while the camera keeps traveling; each snap gets a tick SFX. At least six beat-hits. SANCTIONED SEAM #2: the beat-hits may hard-cut card content ON the beat — the sanctioned exception, because the camera itself never stops moving through them.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-rhythm.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause, rendered — the region cut from the finished film.*
*Next: [Motion graphics](/prompting/motion-graphics) — the shortest one yet, a single motion graphic where motion alone is the message.*
@@ -0,0 +1,118 @@
---
title: Overlays and lower thirds
description: "Prompt named lower-third and social-post overlay blocks with timing, copy, and brand tone."
---
Level 1's founder-clip.mp4 got named lower-thirds and card overlays through `/talking-head-recut`, which designs bespoke cards synced to that specific transcript. This chapter is the reverse case: ready-made lower-third and social-overlay blocks you drop into a composition you're building from scratch, no existing footage required — the same vocabulary, pointed at a scene instead of a shot.
## What overlays do and when they trigger
Overlays are timed blocks that sit on top of your footage or scene — a lower third that names a speaker, a broadcast ticker, or a replica social-media card. Because each is a timed clip, prompts trigger this layer when you ask to *add* something *at* a moment: "add a lower third at 0:03 with the name and title," "show an animated tweet during the intro," "put a Spotify now-playing card in the corner." Give the timestamp, the copy, and the tone; the agent places the block on a track above the footage.
Two groups:
- **[Lower thirds](/catalog/blocks/lt-clean-bar)** — name/title identifiers for speakers, interviews, podcasts, and news.
- **[Social overlays](/catalog/blocks/x-post)** — animated replicas of platform UI (posts, cards, notifications, follow prompts).
## Brand tone → lower third
Lower thirds split into **cards** (a filled shape behind the text) and **cardless** (text with a rule or sweep, designed to overlay live footage without boxing it in).
| Tone | Blocks |
| ---- | ------ |
| **Minimal / clean / corporate** | [`lt-clean-bar`](/catalog/blocks/lt-clean-bar), [`lt-soft-pill`](/catalog/blocks/lt-soft-pill) |
| **High-energy / podcast / bold** | [`lt-bold-block`](/catalog/blocks/lt-bold-block), [`lt-color-block`](/catalog/blocks/lt-color-block) |
| **Cardless over footage** (interview, talking head) | [`lt-accent-underline`](/catalog/blocks/lt-accent-underline), [`lt-kicker-name`](/catalog/blocks/lt-kicker-name), [`lt-mask-reveal`](/catalog/blocks/lt-mask-reveal), [`lt-side-rule`](/catalog/blocks/lt-side-rule) |
| **Card over bright footage** | [`lt-dark-card`](/catalog/blocks/lt-dark-card) |
| **Broadcast / news** | [`lower-third-bild`](/catalog/blocks/lower-third-bild), [`news-ticker`](/catalog/blocks/news-ticker) |
| **Two-part wipe (name + role)** | [`lt-stack-bars`](/catalog/blocks/lt-stack-bars) |
<Tip>
Over live footage, prefer a **cardless** lower third — they're text-shadowed for legibility without a box that fights the shot. Use a **card** ([`lt-dark-card`](/catalog/blocks/lt-dark-card) charcoal for bright scenes) when the background is too busy for cardless text to read.
</Tip>
## Use → social overlay
Each social overlay is a self-contained animated card with editable placeholder content.
| You want | Block |
| -------- | ----- |
| An animated tweet / X post with engagement metrics | [`x-post`](/catalog/blocks/x-post) |
| A Reddit post card with upvotes and comments | [`reddit-post`](/catalog/blocks/reddit-post) |
| A Spotify now-playing card with album art and progress | [`spotify-card`](/catalog/blocks/spotify-card) |
| A macOS notification banner | [`macos-notification`](/catalog/blocks/macos-notification) |
| An Instagram follow prompt | [`instagram-follow`](/catalog/blocks/instagram-follow) |
| A TikTok follow prompt | [`tiktok-follow`](/catalog/blocks/tiktok-follow) |
| A YouTube subscribe lower third | [`yt-lower-third`](/catalog/blocks/yt-lower-third) |
## Example prompts
Quote the exact copy — unquoted names and titles get paraphrased (see [anatomy](/prompting/anatomy)).
> Add a lower third at 0:03 for 5 seconds, on a track above the footage, with [`lt-clean-bar`](/catalog/blocks/lt-clean-bar). Name: "Dana Ríos". Title: "Head of Design".
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/validate-lower-third.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above over stand-in footage, unedited.*
<Note>
Give a lower third at least the block's own timeline length (most run ~5 seconds including their designed exit) — a shorter window hard-cuts the block before its settle-out animation plays.
</Note>
> Podcast clip. Bring in [`lt-bold-block`](/catalog/blocks/lt-bold-block) when the guest starts talking, holding 5 seconds — name "MARCUS LEE", tag "GUEST" — brand accent #FF5A1F.
> During the intro, show an [`x-post`](/catalog/blocks/x-post) card with the quote "we shipped it in a weekend" and 12.4K likes, then slide it out before the demo.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/overlay-xpost.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above over stand-in scenes, unedited — the card's built-in like-tap ticks 12.4K → 12.5K.*
> /motion-graphics Transparent overlay only — a [`spotify-card`](/catalog/blocks/spotify-card) now-playing widget animating in, bottom-left. Export as transparent WebM so I can drop it over footage in my editor.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/overlay-spotify-preview.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*MP4 preview of the transparent WebM over a checkerboard — the delivered file carries real VP9 alpha (verified via ALPHA_MODE + alphaextract).*
## Knobs
- **Timing.** "at 0:03," "for 4 seconds," "slide it out before the demo" set the block's start and duration — an overlay is a timed clip, so it needs both.
- **Copy.** Quote every editable field: name, title, handle, headline, metrics, ticker text. The blocks ship with placeholder content you replace.
- **Track placement.** Overlays go on a track *above* the footage so they composite on top; say "over the footage" if you're layering onto an existing clip.
- **Brand accent.** Give a hex or brand color — most lower thirds carry an accent bar, tab, or block that takes it.
- **Card vs cardless.** State it when it matters, or let the tone table decide.
- **Transparent output.** For use in an external NLE, render the overlay on its own as a transparent WebM — see [rendering and output](/prompting/rendering-and-output).
## Failure modes
**Don't leave the copy unquoted.** Unquoted names and titles get paraphrased; quoted text renders verbatim.
- ❌ `add a lower third with the speaker's name and role`
- ✅ `lt-clean-bar — name: "Dana Ríos", title: "Head of Design"`
**Don't omit the timestamp.** An overlay is a timed clip; without a start (and ideally a duration) the agent has to guess when it appears and how long it holds.
- ❌ `put a lower third somewhere in the intro`
- ✅ `lower third at 0:03, holding 4 seconds`
**Don't let the overlay render behind the footage.** It has to sit on a track above the clip, or the video covers it.
- ❌ `add the tweet card to the video` (ambiguous layering)
- ✅ `x-post card on a track above the footage, top-right`
**Don't over-spec real account data.** These are stylized replicas with editable placeholders — provide the copy you want shown, not a live URL to scrape.
- ❌ `pull my actual Spotify page`
- ✅ `spotify-card: track "Midnight City", artist "M83"`
**Don't invent overlay names.** Only the blocks in the [Social Overlays](/catalog/blocks/x-post) and [Lower Thirds](/catalog/blocks/lt-clean-bar) groups exist.
- ❌ `add a linkedin-post overlay`
- ✅ pick a real block, or describe the card and let the agent build a custom one in a freeform composition
<Note>
**Capstone thread** — the [Level 7 film](/prompting/capstone)'s Material region docks a designed lower-third chip that renders *behind* the matted-out subject (cut from the film, below).
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> […] as the camera arrives and BEFORE the person speaks, the framework mattes the footage — **the background peels away via background removal**, sliding off along the travel direction and leaving the cutout standing alone on the brand ground. […] A designed lower-third chip renders behind the subject.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-material.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause, rendered — the region cut from the finished film.*
*Next: [Caption styles](/prompting/captions-catalog) — the same timed-block pattern, tuned to on-screen text instead of name cards and social replicas.*
+156
View File
@@ -0,0 +1,156 @@
---
title: Prompt Guide
description: "How to prompt AI agents to author HyperFrames videos — setup, the two prompt shapes, and the map of this guide."
---
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-timeline-default.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*By the end of this guide, you can build this with a prompt.*
HyperFrames is built for AI agents — compositions are plain HTML, the CLI is non-interactive, and the framework ships [skills](https://github.com/vercel-labs/skills) that teach agents the patterns docs alone don't cover. This guide shows how to prompt agents effectively once skills are installed — the vocabulary that changes output, the iteration patterns that save time, and the rules that prevent breakage.
<Note>
**Before you prompt**, have three things in place: the skills installed (below), a scaffolded project (`npx hyperframes init my-video`), and the live preview running (`npx hyperframes preview`) so you can judge each render the moment it lands. Prompting without the preview open turns every iteration into a blind guess.
</Note>
## The level ladder
The guide is one arc, novice to advanced. Each level is what you can do once you've read it — read them in order, or jump straight to whichever gap matches where you are:
| Level | What you can do after it |
| --- | --- |
| **1 — [Your first video](/prompting/product-launch)** | Get a finished video from one prompt — the workflow fills the gaps (palette, pacing, structure) for you. |
| **2 — [Control](/prompting/anatomy)** | Name the parts yourself: route, spec, beats, copy, technique, negatives — the skeleton that removes the decisions agents get wrong. |
| **3 — [Life](/prompting/motion)** | Motion and transitions that read premium instead of like a slideshow. |
| **4 — [Substance](/prompting/code-blocks)** | Add real capabilities: code animation, data-viz, overlays, captions, generated artwork, VFX, 3D. |
| **5 — [Voice & sound](/prompting/media-and-audio)** | Narration, music, and any footage you supply, scored and mixed correctly. |
| **6 — [Scale](/prompting/design-systems)** | Design systems, variables, storyboards, editing, iterating, matching references, and export — a video as a system, not a one-off. |
| **7 — [Capstone](/prompting/capstone)** | Everything above, composed into one real prompt. Each chapter on the way up shows its own region of this film cut out in isolation; the capstone prompt is the glue that binds them into one continuous camera journey, rendered twice from one template. |
## One-time setup
Install the skills in your project (or globally for your agent):
```bash
npx skills add heygen-com/hyperframes --full-depth
```
The installer shows a picker. Select the **core skills** below — every project needs them. In Claude Code, restart the session after installing; the skills register as **slash commands**. Start at `/hyperframes`: it orients you to the whole surface and routes "make me a video" requests to the right workflow.
**Core skills — install all of these**
| Slash command | What it loads |
| ----------------------- | -------------------------------------------------------------------------- |
| `/hyperframes` | **Read first.** The entry skill — capability map + video router; sends "make me a video" intent to the right workflow |
| `/hyperframes-core` | Composition contract — HTML structure, `data-*` attributes, clips, tracks |
| `/hyperframes-animation`| All animation — motion rules, scene blueprints, transitions, and the runtime adapters (GSAP, Lottie, Three.js, Anime.js, CSS, WAAPI, TypeGPU) |
| `/hyperframes-creative` | Creative direction — design spec, palettes, typography, narration, beats |
| `/hyperframes-cli` | Dev-loop CLI — `init`, `lint`, `check`, `preview`, `render`, `doctor` |
| `/media-use` | Media OS — TTS voiceover (`tts`), `transcribe`, `remove-background`, plus BGM / SFX / image resolution |
| `/hyperframes-registry` | Block and component installation via `hyperframes add` |
| `/hyperframes-keyframes`| Seek-safe keyframe authoring across runtimes, plus `hyperframes keyframes` diagnostics |
| `/general-video` | The general authoring workflow — fallback for any video that doesn't match a specific workflow below |
**Optional workflows — add the ones that match your inputs** (`/hyperframes` routes to whichever you've installed)
| Slash command | Input → output |
| ------------------------ | --------------------------------------------------------------------------- |
| `/product-launch-video` | Any website URL / brief / script → launch or promo video, or a site tour / showcase |
| `/faceless-explainer` | Arbitrary text (no URL) → faceless explainer with its own TTS narration |
| `/pr-to-video` | A GitHub PR → code-change explainer |
| `/embedded-captions` | An existing talking-head video → the same footage with captions / subtitles |
| `/talking-head-recut` | An existing talking-head video → footage packaged with designed graphic cards |
| `/motion-graphics` | A short, unnarrated, design-led motion graphic (logo sting, kinetic type, stat / chart) |
| `/music-to-video` | A music track + your images → beat-synced video (lyric / slideshow / kinetic promo) |
| `/slideshow` | A deck outline or slides → navigable presentation with presenter mode (not a rendered MP4) |
| `/remotion-to-hyperframes` | Port an existing Remotion (React) composition to HyperFrames HTML |
| `/figma` | A Figma file / frame / URL → imported assets, brand tokens, and reconstructed motion |
<Tip>
To skip the picker and install everything (core + every workflow) in one shot, run `npx skills add heygen-com/hyperframes --all --full-depth`. And start HyperFrames prompts with `/hyperframes` (or invoke the skill another way for non-Claude agents) — it loads the routing + composition context explicitly so the agent picks the right workflow and gets the rules right the first time.
</Tip>
## Claude Design
Claude Design uses a different setup. Download [`claude-design-hyperframes.md`](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/claude-design-hyperframes.md) from GitHub (click the ↓ button), then **attach it to your chat** (don't paste the URL — file attachments produce better output):
```text
Use the attached skill. 25-second LinkedIn video for my startup.
Problem: Sales teams waste 3 hours/day on manual CRM updates.
Solution: AutoCRM — AI that logs every call, email, and meeting.
Traction: 200+ teams, $1.2M ARR, 18% MoM growth.
CTA: autocrmhq.com
```
Claude Design produces a valid first draft (brand identity, scene content, animations, transitions). Download the ZIP and refine in any AI coding agent with `npx hyperframes preview` running. See the [Claude Design guide](/guides/claude-design) for the full workflow.
## The two prompt shapes
Most successful HyperFrames prompts fall into one of two shapes.
**Cold start — describe the video.** You tell the agent what you want from scratch — best for greenfield work where you already have the creative direction in your head.
> Using `/hyperframes`, create a 10-second product intro with a fade-in title over a dark background and subtle background music.
Cold-start prompts work best when you specify **duration** ("10 seconds", "5 scenes of 3s each"), **aspect ratio** ("16:9", "9:16 vertical" — defaults to 1920x1080 otherwise), **mood / style** ("minimal Swiss grid", "high-energy social"), and **key elements** (title, lower third, captions, music).
**Warm start — turn context into a video.** You give the agent something to work with — a URL, a doc, a CSV, a transcript — and ask it to synthesize that into a video. This is where HyperFrames shines because the agent does the research/summarization step *and* the production step in one flow.
> Take a look at this GitHub repo https://github.com/heygen-com/hyperframes and explain its uses and architecture to me using `/hyperframes`.
> Turn this CSV into an animated bar chart race using `/hyperframes`.
Warm-start prompts produce richer, more grounded videos because the agent is writing about *something specific* instead of inventing copy.
The four prompts above illustrate shape, not results — every prompt in this guide that ships with an embedded render was run exactly as written, and the gallery of those lives in [Verified examples](/prompting/examples).
## Recommended workflow
1. `npx hyperframes init my-video` — scaffold a project (skills install automatically)
2. Open the project in Claude Code (or Cursor / Codex)
3. Prompt with `/hyperframes` and one of the shapes above
4. `npx hyperframes preview` — watch in the browser as the agent edits
5. Iterate with small targeted prompts
6. `npx hyperframes lint && npx hyperframes check` — the gate: structure, runtime errors, layout collisions, motion, and contrast. Both must pass before you render
7. `npx hyperframes render --output final.mp4` when you're happy
`check` is the step people skip and regret. It runs the composition in a headless browser and reports what a still frame can't tell you — an element overflowing its region, two text blocks colliding, a runtime error that only fires mid-timeline, contrast below WCAG AA. It is fast and it is not optional: a render that took ten minutes will happily contain a defect `check` would have named in seconds.
## What a prompt buys you
Three prompts from this guide and their unedited renders — one workflow warm start, one registry-block piece, one dense freeform spec:
> /product-launch-video Make a 45-second 1920x1080 launch video for https://linear.app. Energetic but minimal, use the site's own palette and screenshots. Structure: hook stating the problem, 3 feature beats with UI captures and one-line captions, end card with logo + "Try it free". Female TTS voice, confident tone, subtle electronic BGM under -18dB.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-product-launch.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
> /motion-graphics 6-second 1920x1080 video, dark navy background. Beat 1 (0-1s): label "ARR" fades up small, top-center. Beat 2 (1-4s): a giant number counts up to $4.2M with an odometer roll, easing out as it lands. Beat 3 (4-6s): "+312% YoY" stamps in below in green, then everything settles into a gentle ambient idle. Use the `apple-money-count` registry block as base. No narration.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-stat-countup.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
And at the far end of the [specification dial](/prompting/specification-dial), a full visual+motion spec one-shots a broadcast-style animated globe — see [Recreating something you saw](/prompting/recreating-references) for the spec:
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/recreate-globe-oneshot.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*One-shot render from the distilled spec, no iteration.*
## Explore the guide
<CardGroup cols={2}>
<Card title="Prompt anatomy" href="/prompting/anatomy">The six-part skeleton every one-shot prompt shares</Card>
<Card title="Verified examples" href="/prompting/examples">Copy-paste prompts, each one-shots a finished video</Card>
<Card title="The specification dial" href="/prompting/specification-dial">How much to specify, and what density buys</Card>
<Card title="Vocabulary" href="/prompting/vocabulary">Words that map to specific framework settings</Card>
<Card title="Premium motion" href="/prompting/motion">The eight-rule grammar that keeps video from feeling cheap</Card>
<Card title="Recreating references" href="/prompting/recreating-references">Match something you saw, from text alone</Card>
<Card title="Capstone" href="/prompting/capstone">The film above, dissected frame by frame</Card>
</CardGroup>
*Next: [Your first video](/prompting/product-launch) — one URL, one prompt, a finished launch video.*
+99
View File
@@ -0,0 +1,99 @@
---
title: Product launch videos
description: "What to say to turn a product URL, a script, or a brief into a launch or promo video — and when to reach for a site tour instead."
---
This is Level 1 of the Prompt Guide: a run of one-prompt rides, each handing a workflow a single ask and getting a finished video back. You don't need any technique yet — one sentence describing your product, aimed at the right workflow, is already enough for a first draft.
## Your first win
One prompt to [`/product-launch-video`](/prompting/overview), aimed at a live URL, is enough for a finished launch video — no technique required yet.
Verified, from the [examples](/prompting/examples) page — a 45-second launch from a live URL:
> /product-launch-video Make a 45-second 1920x1080 launch video for https://linear.app. Energetic but minimal, use the site's own palette and screenshots. Structure: hook stating the problem, 3 feature beats with UI captures and one-line captions, end card with logo + "Try it free". Female TTS voice, confident tone, subtle electronic BGM under -18dB.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-product-launch.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
Read the [anatomy](/prompting/anatomy) of that skeleton — route, spec, structure, copy, voice, level — then swap in your own product.
## What this makes
A launch or promo that *sells*: SaaS promos, feature reveals, product demos, app and company launches. The [`/product-launch-video`](/prompting/overview) workflow captures the product's site (or takes a pasted script), reads its brand, writes a story, and builds it frame by frame.
**Route it right — the distinction is intent, not input:**
| You want… | Route |
| --- | --- |
| To market, launch, promote, or reveal a product (the default for any commercial URL) | `/product-launch-video` |
| A video *of* a general site — a portfolio / blog / docs / landing-page tour or showcase, not a sales pitch | `/product-launch-video` too — say *tour* or *showcase* in the brief (see the [website-to-video guide](/guides/website-to-video)) |
Both route to the same workflow now; what changes is the brief. "Promo for our site" is a launch — sell it. A neutral walkthrough of a docs site is a tour — say so, and the workflow shows the site's own captured screens instead of pitching. Unsure → start at `/hyperframes` and let it route.
## The knobs that matter
What you can already steer from the prompt, before you've learned any technique. Set the ones you care about; leave the rest to the workflow's taste.
| Knob | What to say | Why it matters |
| --- | --- | --- |
| **Capture vs no-capture** | Give a URL to capture the real site; give a script or brief for the no-capture path; give just a brand name to have the agent find the URL | Capture borrows the real palette and screenshots; no-capture invents visuals, so it needs your brand colors named |
| **Palette source** | "use the site's own palette" | The workflow remixes the captured brand tokens onto its frame preset — you get the product's real colors, not a generic theme |
| **Structure** | "hook stating the problem, N feature beats with captions, end card with CTA" | The workflow leads value-before-evidence; naming the beats keeps the hook and CTA from getting dropped |
| **Voice & tone** | "female TTS voice, confident" / "calm male voice" | Voice gender and tone are prompt words; the provider itself is a workflow decision — see the [skill](/prompting/overview) for provider mechanics |
| **BGM level** | "subtle electronic BGM under -18dB" (or "no BGM") | A stated ceiling keeps music under the voice; leave it off entirely for a teaser |
| **Length & destination** | "~45 seconds", "9:16 for TikTok" | Sweet spot is 30-90s; destination sets the aspect (16:9 embed · 1:1 feed · 9:16 Shorts) |
<Tip>
Duration and destination are the two cheapest, highest-leverage things to state. Everything else the workflow will choose well if you stay quiet — see [the specification dial](/prompting/specification-dial) for how much to delegate.
</Tip>
## Variants
<AccordionGroup>
<Accordion title="20-second vertical teaser (9:16)">
> /product-launch-video Make a ~20-second 1080x1920 teaser for https://linear.app. Super minimal, just the hook. Beat 1 (0-4s): the problem in one line, big type. Beat 2 (4-16s): two feature beats, one UI capture each with a three-word caption. Beat 3 (16-20s): logo + "Try it free" end card, then settle into a gentle idle. Use the site's own palette. Female TTS voice, confident; no BGM.
A teaser trades feature coverage for pace — fewer beats, one idea each. Keep the destination (Shorts / TikTok → 9:16) and let the workflow scale the story to the shorter runtime.
</Accordion>
<Accordion title="Pasted script, no capture">
> /product-launch-video Make a ~20-second 1920x1080 launch video from this script — use it verbatim: "Your CRM is three hours of busywork a day. AutoCRM logs every call, email, and meeting for you. 200 teams already switched. Try it free at autocrmhq.com." No site to capture — invent clean product-y visuals from the script. Male TTS voice, calm and confident; subtle BGM under -18dB.
With no URL the workflow takes the no-capture path: no screenshots, no site palette to borrow, so name your brand colors and fonts if you have them (or the agent invents a palette). Verbatim scripts set the duration — figure roughly 130 spoken words per minute, so a 30-second video wants a 6570 word script. Saying "use it verbatim" pre-answers the workflow's keep-or-restructure question.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/variant-launch-script.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from this prompt shape with a different, longer script (78 words, fictional dev-tool "Relay"), unedited — it ran 35.5s because the words set the length.*
</Accordion>
<Accordion title="Brand name only (agent finds the URL)">
> /product-launch-video Make a ~45-second 1920x1080 launch video for Linear. Find the official site, capture it, and use its own palette and screenshots. Angle: speed as the whole pitch. End card with logo + "Try it free". Confident female TTS voice, subtle electronic BGM under -18dB.
Given a name instead of a link, the workflow searches for the official URL, confirms it in one line, then captures — you get the site-grounded result without pasting the link yourself.
</Accordion>
<Accordion title="Site tour instead (same workflow, tour brief)">
> /product-launch-video Make a 30-second 1920x1080 tour of https://example.com built from its own screenshots. Calm, editorial pace — show the homepage, two inner pages, and the footer. Full narration, warm male voice. This is a showcase, not a sales pitch.
Reach for this when the goal is to *show the site*, not sell a product. It builds from captured screenshots and the site's brand assets. A launch or promo — even from the same URL — belongs to `/product-launch-video`.
</Accordion>
</AccordionGroup>
## Common failure modes
**Hard-timing a verbatim script.** With supplied narration, the real TTS duration sets the length — a hard number forces the agent to cut or pad your words.
- ❌ `a 45-second launch video from this exact script: ...`
- ✅ `a ~45-second launch video from this script: ...`
**Overriding the designed structure.** The workflow builds hook → value → evidence → CTA for a reason; drop the hook and the promo never answers "why should I care?"
- ❌ `skip the intro, just list all six features back to back`
- ✅ `hook stating the problem, then 3 feature beats, then the CTA end card`
**Fighting the art-directed preset.** Each workflow adopts a frame preset and injects transitions; forcing a foreign theme yields a compromise, not your look (see [rules and anti-patterns](/prompting/rules-and-anti-patterns)).
- ❌ `/product-launch-video ... plain white, no transitions between scenes`
- ✅ pick the angle and tone, and let the preset carry the visual system
**Assuming the agent knows your assets.** On the no-capture path there's no site to read; an unnamed logo or color is invented.
- ❌ `use our brand colors`
- ✅ `brand colors #5E6AD2 on off-black; logo at assets/logo.svg`
*Next: [Explainers](/prompting/explainers) — no product, no site, just text turned into a faceless explainer.*
+51
View File
@@ -0,0 +1,51 @@
---
title: Recreating something you saw
description: "Transcribe motion, iterate with absolute targets, distill the constants — and know where the text-only ceiling is."
---
[Iterating](/prompting/iterating) covered the discipline in general. This page is its hardest test: matching a specific reference you watched rather than a look you're inventing. In practice, matching a specific reference from text alone reaches roughly 90% — but only with a specific workflow, and knowing where the ceiling is. (The percentages on this page are observed results from the guide's own recreation builds, judged frame-against-frame; treat them as the shape of the curve, not a guarantee.)
## Transcribe motion, not just composition
Watch the reference frame by frame and write down: the exact duration, the camera's path, what each element does with timestamps, how entrances overlap, which layers are blurred, sampled colors. A prompt built this way one-shots ~75% of the target — structure and motion arc land, rendering calibration doesn't.
## Iterate with absolute targets
Compare your render against the reference frame by frame, then correct one axis at a time while freezing everything that already matches. State corrections as absolute values, not relative nudges — "dot radius = 25% of row spacing", not "make dots 2x finer". Relative corrections pendulum: too big, then too small, then too big. Expect a handful of rounds to converge.
## Distill the converged values back into the prompt
Iteration is a search; the found constants are reusable. A prompt carrying them one-shots ~8090% of the converged quality on a fresh build — the discrete facts (timings, counts, hexes, ratios, camera arcs) transfer losslessly, while continuous qualities (glow prominence, framing feel) still vary by a calibration note or two. And the converged composition file itself is the pixel-exact artifact: renders are deterministic, so re-rendering it reproduces the result bit-for-bit.
A distilled spec that one-shots a broadcast-style animated globe:
<Accordion title="Worked example: the hologram globe (full distilled spec)">
> 1.8-second 1920x1080 video, Three.js via the adapter (seek-driven, no rAF). One continuous shot; every element is still moving on the final frame.
>
> FIELD: blue-violet background, linear #2a24a8 → #12105e top-to-bottom with a soft radial lift at center; faint blurred vertical cyan light-streaks (#5ee0e8 at ~10% opacity, ~340px spacing) drifting 70px left across the piece; deep corner vignette; soft-light film grain at ~6% (seeded noise).
>
> GLOBE: royal-blue sphere (#4348f2), lit from upper-left with a 0.58 ambient floor; a broad subtle satin band (#6470ff, very wide falloff, ~30% mix) sweeping the upper curve; a strong cyan rim-light line (#5ee8f0) tracing only the top edge. Continents in TWO layers: (a) a heavily-blurred darker-blue silhouette (#3439c2, 80% opacity) just under the surface, reading as a soft shadow shape; (b) a dot-matrix just above the surface on an equal-area grid (0.9° latitude rows, longitude step widening with latitude): dot radius = 2428% of row spacing — clear blue gaps between dots — growing slightly toward the equator, 85% dot opacity; two color populations — cyan-aqua #5ee0e8 north, spring-green #7ce97a from latitude ~32° southward — with seeded ±30% per-dot brightness variance; dots dimmed to 40% in the view-space lower-right shadow zone. Continents read as distinct dotted landmasses covering ~3035% of the visible hemisphere, with royal-blue ocean dominating the rest.
>
> CAMERA: open EXTREMELY close — the sphere's curve fills the entire frame, horizon exiting the upper corners — then one continuous pull-back + crane (fov 52°→40°) ending with the dome filling the lower half edge-to-edge, its silhouette touching both frame edges, horizon at ~45%. Ease power1.inOut computed over a 2.0s window while rendering 1.8s so the move never settles on-screen. The globe rotates 28° about its vertical axis, linear, continents drifting right-to-left, never stopping.
>
> ORBS — 12 across three depth planes, world-anchored on the upper hemisphere so stems stay vertical, popping at staggered starts 0.45s→1.15s (0.060.13s apart), each rising 0.440.56s with back.out overshoot (vary 1.72.6 per orb), then bobbing ±8px on phase-offset sines forever. 5 midground (~90110px at end framing): soft mint body #a7ecc4 with a darker-green under-shade #3f9b5e at lower-left and a pale rim #d6ffe8 top-right, no white core; each wrapped in a soft additive bloom sprite ~3.5x its diameter whose texture is HOLLOW-centered peaking ~35% just outside the orb edge (a bright-cored additive glow over the opaque orb blows the mint to lime); plus a thin soft halo ring ~4.2x radius at 60% opacity, always subtler than the orb itself, with a slow 5% scale pulse. 4 background (~30px, sharp, tighter bloom). 3 foreground near-lens bokeh (~160190px, dense mint radial-gradient sprites riding the camera at center-left / lower-center / upper-right, ~70% opacity, drifting ±30px laterally, no stems). Stems: 23px additive cyan cylinders fading to transparent at the surface. Tag canvas-generated sprite textures sRGB or the mints wash out pale.
>
> CAPTION: "Across 82 Countries" — Inter 300, 34px, 0.06em tracking, white at 90%, top-center 12% from the top; left-to-right per-letter fade starting t=1.0s completing ~1.45s, then a slow 6px upward drift still easing at the final frame.
>
> No audio.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/recreate-globe-oneshot.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*The one-shot render produced by this exact spec on a fresh build — no iteration.*
</Accordion>
The honest ceiling: words carry discrete, countable things losslessly and underdetermine continuous perceptual qualities — bloom falloff, material feel, optical color mixing. That last 10% doesn't close from text; it oscillates. If pixel-exact matters, keep the composition file.
## Related
<CardGroup cols={2}>
<Card title="Iterating" href="/prompting/iterating">The correction loop this page pushes to its limit</Card>
<Card title="High-fidelity looks" href="/prompting/visual-specs">Writing the spec density a recreation needs</Card>
<Card title="Runtimes and 3D" href="/prompting/runtimes-and-3d">The adapter the worked example uses</Card>
<Card title="Capstone" href="/prompting/capstone">Every technique composed into one film</Card>
</CardGroup>
*Next: [Rendering and output](/prompting/rendering-and-output) — once the cut is locked, the words that pick the right export.*
+66
View File
@@ -0,0 +1,66 @@
---
title: Porting from Remotion
description: "What to say to migrate an existing Remotion (React) composition's source into HyperFrames HTML — and what to expect the agent to refuse."
---
This is the last chapter of Level 6, and the last before the capstone level — bringing an existing Remotion project into everything you now know: brand systems, variables, editing, iteration, reference-matching, and export.
## What this makes
Not a new video — a **translation**. The [`/remotion-to-hyperframes`](/prompting/overview) workflow reads an existing Remotion (React) composition's source and rewrites it as HyperFrames HTML: the same frames, the same timing, the same visual output, running on GSAP instead of React's frame-callback model.
This is a migration, not a creation workflow. It only fires on an explicit ask to port, convert, migrate, or translate a **Remotion** source. Two things that look similar but aren't this:
- "Make something like my Remotion video" with no ask to migrate the actual source → a fresh build, [`/general-video`](/prompting/overview#one-time-setup).
- A non-Remotion source (After Effects, Framer Motion, plain React/CSS) → there's no Remotion source to translate; re-create it natively instead.
The direction is one-way. There's no export back out to Remotion or any other framework.
## Base prompt
> /remotion-to-hyperframes Port the composition at `src/HeroReveal.tsx` (Remotion project root: `.`) to HyperFrames. Keep the same duration, fps, and dimensions. Write `TRANSLATION_NOTES.md` for anything that doesn't translate cleanly.
Naming the exact file (not just "my Remotion project") matters once a project has more than one `<Composition>` registered — say which one.
## What won't translate — and why that's correct
The skill lints the source before translating anything, and **refuses** rather than approximates when it hits a pattern HyperFrames' seek-driven model can't represent deterministically:
- **State-driven animation** — `useState`, `useReducer`, or `useEffect`/`useLayoutEffect` with real dependencies. HyperFrames seeks to an arbitrary frame and expects the same pixels every time; a state machine that reacts to its own history can't guarantee that. The skill recommends a runtime-interop pattern instead of silently producing a translation that "looks right" but isn't frame-accurate.
- **Third-party React UI kits** — MUI, Chakra, Mantine, antd, shadcn, Radix, NextUI. There's no HTML/CSS/GSAP equivalent to translate them into.
- **`@remotion/lambda` deploy config** — not a blocker. It's dropped (deployment config, not animation) and the rest of the composition still translates.
If your source hits a blocker, expect the agent to stop and explain the interop recommendation rather than push through — that's the correct behavior, not a failure. Don't ask it to "just approximate" past a blocker; the whole point of this workflow is a validated, measured translation, not a best-effort guess.
## Validation is not optional
Every translation is graded against the Remotion original by SSIM (structural similarity) on rendered frames, not eyeballed. If you're porting something non-trivial, ask for the check explicitly:
> After translating, render both the Remotion original and the HyperFrames version and report the SSIM diff.
A translation that "looks right" in preview can still measure meaningfully below the validated baseline — that's the difference this workflow is built to catch.
## The knobs that matter
| Knob | What to say | Why it matters |
| --- | --- | --- |
| **Exact source file** | "the composition at `src/HeroReveal.tsx`" | A Remotion project can register several `<Composition>`s — name the one you mean |
| **Preserve exact timing** | "keep the same duration, fps, and dimensions" | The default; state it anyway so the agent doesn't round or resize |
| **Gap documentation** | "write `TRANSLATION_NOTES.md` for anything that doesn't translate cleanly" | Approximated fonts, dropped volume ramps, and substituted presentations get recorded, not silently absorbed |
| **Validation** | "report the SSIM diff against the original" | Makes the fidelity check part of the deliverable instead of an optional afterthought |
## Common failure modes
**Asking for a fresh build with Remotion as inspiration.** If you don't have an actual Remotion source to translate, this isn't the workflow — it's a normal build.
- ❌ `/remotion-to-hyperframes make something like my Remotion intro`
- ✅ `/general-video make an intro like this: [description]` — no Remotion source, no migration
**Asking it to push past a lint blocker.** A `useState`-driven composition isn't a "translate it anyway, best effort" situation — it's a different problem (runtime interop), and forcing a translation produces something that looks plausible but isn't frame-accurate.
- ❌ `just convert it, don't worry about the state stuff`
- ✅ let the agent recommend the interop pattern, then decide whether that fits your case
**Skipping validation on a non-trivial port.** A visual-only check misses timing/easing drift that only shows up in a frame-by-frame SSIM diff.
- ❌ *(no request to validate — accepting on "looks right")*
- ✅ `render both and report the SSIM diff`
*Next: [Capstone](/prompting/capstone) — one prompt, one film, every technique in this guide, dissected frame by frame.*
+130
View File
@@ -0,0 +1,130 @@
---
title: Rendering and output
description: "What to say to get the right file out — quality tier, format, resolution, framerate, and cloud rendering — without over-speccing a render that slows to no benefit."
---
Everything before this point — including the frame-by-frame matching in [Recreating something you saw](/prompting/recreating-references) — shapes the composition. This page is about the *export*: the words that pick a quality tier, a container format, a resolution, and where the render runs. The defaults — MP4, 1920×1080, 30fps, `standard` quality — are deliberately good, so most of the skill here is knowing when *not* to ask for more. The mechanics live in the [Rendering guide](/guides/rendering); this page owns what to say.
## Quality tier
Say the tier by name and the agent selects the matching encode preset — you don't specify CRF or encoder speed:
| Say this | Tier | Best for |
| --- | --- | --- |
| "draft" / "quick render" | `draft` | Fast iteration while you're still judging the cut |
| nothing, or "review render" | `standard` (default) | General use — visually lossless at 1080p |
| "final" / "high quality" | `high` | Delivery masters |
The tiers trade encode time for fidelity. `standard` (the default) is already visually lossless at 1080p — most people can't tell it from source — so reserve `high` for the master you'll actually hand off, and use `draft` freely while iterating.
- ❌ `render everything at high quality`
- ✅ `draft renders while we iterate, then one high-quality final` — you spend the slow encode once, on the cut you've already approved
## Format
MP4 is the default and the right answer for almost everything — it plays everywhere. Ask for a different container only when the delivery target needs one:
> Render this as a transparent WebM overlay.
> Export a MOV I can drop into Premiere with the background knocked out.
Transparency has a container hierarchy, and the tradeoffs are real:
| Ask for | You get | Watch out for |
| --- | --- | --- |
| "transparent MOV" | ProRes 4444 with alpha | The editor-grade choice (Premiere, Final Cut, Resolve, After Effects). Files are large — expected for an editing intermediate. |
| "transparent WebM" | VP9 with alpha | Small, but **only browsers decode the alpha** — every video editor renders the transparent areas black. Browser playback only. |
| "PNG sequence" | Lossless RGBA frames | For compositing in After Effects / Nuke / Fusion. Largest of all. |
Transparency also only *means something* on a design that has empty space to see through. A lower third, a subscribe card, or a logo sting is mostly empty canvas — transparency lets it composite over other footage. A full-frame scene (edge-to-edge background, full-bleed video, a title card with its own backdrop) has nothing to be transparent; the request produces a file that looks identical to the opaque one but is larger and plays in fewer places.
- ❌ `render my full-screen product promo as a transparent WebM`
- ✅ `render the promo as MP4; export just the lower-third overlay as transparent WebM` — transparency belongs to the layer meant to sit *over* other footage, not the finished full-frame film
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/overlay-spotify-preview.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*A transparent VP9 WebM overlay previewed over a checkerboard. To verify alpha from the CLI: VP9 stores it out-of-band, so look for ALPHA_MODE=1 in ffprobe (a pix_fmt-only check false-negatives) or extract it with ffmpeg alphaextract.*
<Note>
A transparent render also depends on the composition leaving `html` / `body` backgrounds unset — the transparency comes through only where nothing is painted. The workflow skills handle this; see the [Rendering guide](/guides/rendering#transparent-video) if you're hand-authoring an overlay.
</Note>
## Resolution and framerate
1920×1080 at 30fps is the default. Both cost real time when you raise them, and both are frequently asked for out of habit rather than need.
**4K** is a render-time flag — the composition stays at its authored size and Chrome supersamples it to 3840×2160. That buys crisp text, SVG, and CSS at any scale, but it does *nothing* for content already locked to a pixel grid: a 1080p `<video>`, a fixed-size `<canvas>`, or a sub-4K image gain no detail from it. And it isn't free — a 4K render is roughly **4× slower per frame** and produces a **35× larger file**. Ask for it when the delivery surface genuinely needs it (a 4K display, a client spec), not reflexively.
> Render this at 4K for the trade-show display.
**Framerate** follows the same logic: 60fps doubles the frames the engine captures and encodes. It's worth it for fast motion graphics destined for a high-refresh screen; it's wasted on a talking-head clip or a slow title sequence.
- ❌ `render in 4K 60fps` for a clip headed to Instagram — the platform will transcode it down anyway, and you paid the slow render for nothing
- ✅ say nothing for social; name `4K` (or `60fps`) only when the target actually resolves it
<Warning>
A few 4K constraints will stop a render before it starts (all grounded in the [4K guide](/guides/4k-rendering#constraints)): the target orientation must match the composition's aspect ratio, the scale must be a whole number (1080p → 4K is exactly 2×), and **4K cannot be combined with HDR** in one pass. If you need both, render HDR at composition resolution and upscale separately.
</Warning>
## HDR
HDR output is **HDR10 MP4** (H.265 10-bit, BT.2020) and it is *source-driven* — the render only goes HDR when your composition actually references HDR media (video tagged BT.2020 with PQ or HLG transfer, or a 16-bit PNG). Text, gradients, and GSAP animation are not HDR sources; a composition made entirely of them has nothing to render in HDR.
> This composition has an HDR drone clip — render it as HDR10.
By default HDR is auto-detected, so with a real HDR source in the project you often need to say nothing. Force it explicitly only to override the probe:
- "force HDR" → forces the HDR path even without a detected HDR source
- "force SDR" → forces standard range even when HDR sources are present
HDR is **MP4 only** (a transparent MOV/WebM request falls back to SDR) and it is **not available on Lambda** (distributed rendering is SDR-only). See the [HDR guide](/guides/hdr) for source requirements and verification.
## Where the render runs
Local rendering is the default and the right choice for the whole iteration loop. Reach for cloud rendering only when a single machine is the bottleneck:
> Render this on Lambda.
That routes to HyperFrames' AWS Lambda path, which fans the render across many parallel workers. It's the right call for renders that are **too long or too large for one host** — multi-minute videos, 4K masters, or large parallel batches — and it needs AWS credentials configured first. For dev-loop iteration, stay on local `render`; the round-trip is faster than any cloud dispatch. Lambda is SDR-only (no HDR) and bills by compute time; the [AWS Lambda guide](/deploy/aws-lambda) covers setup, cost shape, and the conservative concurrency default.
- ❌ `set up Lambda so I can preview edits faster` — cloud dispatch adds latency to a fast local loop
- ✅ `render the final 3-minute 4K cut on Lambda` — the workload that actually justifies fanning out
Lambda is not the only remote target. **HeyGen-hosted cloud rendering** ([guide](/deploy/cloud)) takes the infrastructure off your hands entirely — no AWS account to configure — and **Google Cloud Run** ([guide](/deploy/gcp-cloud-run)) is the option when your stack already lives on GCP. Name the one you want ("render this on Cloud Run"); the routing is explicit, never inferred.
## Preview before you commit the slow render
The cheapest way to avoid a wasted `high`/4K/HDR render is to judge the frame first. The habit the workflow skills follow:
1. Keep `preview` running and scrub the timeline — same runtime as the render, so what you see is what you get.
2. Iterate with `draft` renders when you need a real file to check.
3. Only when the cut is locked, ask for the final tier / resolution / format.
- ❌ `render the final 4K HDR master` on a cut you haven't watched end to end
- ✅ `draft render so I can check timing` → approve → `now the 4K final`
<Tip>
Rendering is user-gated by design — the agent pauses at preview and renders only when you approve. Use that pause to lock the cut before you pay for the expensive export.
</Tip>
## Related
<CardGroup cols={2}>
<Card title="Iterating" href="/prompting/iterating">Small targeted edits between renders, not re-specification</Card>
<Card title="Rules and anti-patterns" href="/prompting/rules-and-anti-patterns">Why over-speccing resolution and framerate backfires</Card>
<Card title="Rendering guide" href="/guides/rendering">Formats, quality presets, workers — the mechanics</Card>
<Card title="AWS Lambda" href="/deploy/aws-lambda">Cloud rendering setup and cost</Card>
</CardGroup>
<Note>
**Capstone thread** — the [Level 7 film](/prompting/capstone) ends on this chapter's core promise: the protagonist chip snaps into a render slot and seeded confetti holds every piece for exactly two frames — deterministic, identical on every render (cut from the film, below). Its Everywhere region names the real cloud render targets: Lambda and Cloud Run.
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> A **seeded confetti burst** fires — mulberry32, **seed 42, each piece holding position for exactly two frames before stepping** (stop-motion feel) — and the VO lands the honest punchline: identical on every render, because determinism is the whole point.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-render.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause, rendered — the seeded confetti, identical on every render of this composition.*
*Next: [Porting from Remotion](/prompting/remotion-migration) — bringing an existing Remotion project into everything you now know.*
+112
View File
@@ -0,0 +1,112 @@
---
title: Rules and anti-patterns
description: "The technical rules that keep renders correct, and the prompt patterns that cause friction."
---
Almost every rule below is taught in context somewhere earlier in this guide; a few of the narrower lint rules appear only here, because they surface as an error message before they ever matter to a prompt. This page is the lookup table: skim it when you're debugging a render or hand-editing a composition, and follow a rule's link back to the chapter that explains *why* it exists. The lint-rule rows extend the same seven rules with more specific cases the linter now catches automatically.
## Rules to know
The skills enforce these automatically, but if you hand-edit compositions or debug issues, these are the rules that matter:
1. **Register all timelines** on `window.__timelines` — the renderer can't seek animations it doesn't know about.
2. **Video elements must be `muted`** — audio goes in separate `<audio>` elements so the renderer can mix it.
3. **No `Math.random()`** — random values produce different frames on each render, breaking determinism. Use a seeded PRNG (e.g. mulberry32) if you need pseudo-random values. And when you want a stepped, stop-motion hold, quantize it on the **integer frame index**, not on elapsed seconds — seek times don't land on exact 1/fps doubles, so second-domain arithmetic drifts and the hold length stutters. See [Handmade, still deterministic](/prompting/motion#handmade-still-deterministic).
4. **Synchronous timeline construction** — no `async`/`await` or `fetch()` during GSAP timeline setup.
5. **Timed elements need `class="clip"`** — plus `data-start`, `data-duration`, and `data-track-index`.
6. **Add entrance animations to every scene** — elements appearing without animation feel broken on video.
7. **Add transitions between scenes** — jump cuts between scenes are almost always unintentional in composed video. See [What transitions do and when they trigger](/prompting/transitions#what-transitions-do-and-when-they-trigger).
<Warning>
Rules 15 are technical requirements — breaking them produces incorrect renders. Rules 67 are best practices that the skills apply by default. You can override them when you have a reason to.
</Warning>
## Lint rules to know
The linter added eight more rules that catch subtler seek-order and SVG-drawing mistakes — cases where a render can look right in the live preview and still render wrong on a cold, non-linear render worker. Two further hazards below carry no rule code: they surface in the render, not the linter. Phrase prompts to avoid these up front rather than debugging them after a render.
### Cold-seek visibility
Elements that start hidden need their visible end state stated explicitly — a render worker that seeks straight to a later frame restores the *authored* hidden state, not whatever the preview showed a moment ago.
- **Reveal the destination, not just the source.** If an element starts hidden and a `gsap.fromTo()` reveals it, ask for the destination vars — not just the `from` vars — to include `opacity: 1` (or `autoAlpha: 1`). Cold render workers restore the hidden authored state, so an element can stay invisible even when sequential preview looks correct. (`gsap_cold_seek_hidden_fromto_missing_reveal`, PR [#2503](https://github.com/heygen-com/hyperframes/pull/2503))
- **Set the initial hidden state outside the timeline.** Don't rely on a `tl.set(...)` at position 0 inside the timeline itself to hide an element at the start — a zero-duration set exactly at frame 0 may not have applied yet when frame 0 renders. Ask for a bare `gsap.set(...)` outside the timeline, or author the hidden state directly in CSS/HTML. (`gsap_timeline_set_initial_hide`, PR [#2612](https://github.com/heygen-com/hyperframes/pull/2612))
- **A `fromTo` shows its from-state *before* it starts.** `immediateRender` back-renders the `from` vars at every time earlier than the tween's own start, so an element you authored to "appear at 3s" is already on screen at frame 0 wearing its start pose. Sequential preview hides this — you scrub past frame 0 before the tween exists. Ask for `to()` plus `keyframes`, or a zero-duration `tl.set()` at the beat boundary, whenever an element must be absent before its cue. (Surfaced while validating this guide's kinetic-quote and map-route examples.)
<Note>
**One caveat on "identical every time": parallel workers are not bit-identical to each other.** Building this chapter's rule-1 demo — whose left half is deliberately frozen — a default multi-worker render produced four distinct frame hashes across those frozen frames instead of one, with the changes landing exactly on the 30-frame worker-chunk boundaries. The deltas were a handful of ±1-level pixels (109113 dB), i.e. per-Chrome-process rasterization variance, not animation. Determinism in the sense that matters is intact: the same worker seeking the same time renders the same frame, and your composition is not the variable. But if you need a *bit-exact* result — hashing frames, proving a hold, diffing two renders — pass `--workers 1`. Encoding adds its own noise on top, so compare lossless frames rather than the encoded MP4 when you need certainty.
</Note>
### Seek-order safety
These four all come from the same root cause: a cold render worker seeks non-linearly, so anything whose value depends on *when* or *in what order* it runs can render differently than the live preview did.
- **Don't stack a relative tween on a property another writer is still animating.** `"+=50"` or `"-=20"` captures its base at tween init — sequential playback inits mid-flight of the other writer, a cold worker landing later inits from its end state, and the same frame renders at two different positions. State absolute end values instead, or sequence the tweens so they don't overlap on that property. (`gsap_relative_value_second_writer`, PR [#2612](https://github.com/heygen-com/hyperframes/pull/2612))
- **Don't combine `repeatRefresh: true` with a relative value on a repeating tween.** The relative offset accumulates per iteration, so a worker seeking straight into iteration N never performed the earlier iterations' accumulation and lands somewhere else. Ask for absolute endpoints (a `fromTo()`) instead if the loop needs to render correctly from any seek position. (`gsap_repeat_refresh_relative_value`, PR [#2611](https://github.com/heygen-com/hyperframes/pull/2611))
- **Function-valued tween vars receive `(index, target, targets)` — the first argument is a number, not the element.** Don't ask for a function value that calls an element method on its first parameter, or that reads transform-sensitive layout (its result would depend on the worker's own seek order). Use the second parameter for the element, index arithmetic like `(i) => i * 20`, or a value computed once at build time. (`gsap_function_value_hazard`, PR [#2611](https://github.com/heygen-com/hyperframes/pull/2611))
- **Don't measure DOM geometry inside a timeline callback.** `getBoundingClientRect()`, `getTotalLength()`, and `getComputedStyle()` all depend on whatever DOM state the render happens to be in — and timeline callbacks re-fire on every seek, so a cold worker's own non-linear seek order can hand the callback a different measurement than the live preview did. Ask for geometry to be computed once at build time instead. (`gsap_callback_dom_measurement`, PR [#2611](https://github.com/heygen-com/hyperframes/pull/2611))
### SVG draw-on
Two more ways an SVG "line draws itself" effect (animated `strokeDasharray` / `strokeDashoffset`) can render as a static, undrawn line.
- **Don't declare a multi-value CSS `stroke-dasharray` on the same element GSAP is animating.** GSAP merges dash lists per component, so the CSS gap survives the animation and the draw-on hide only covers one gap's worth — the line stays visible for the whole scene. Put decorative dashing on a separate element if you need both effects. (`svg_drawon_css_dasharray_conflict`, PR [#2611](https://github.com/heygen-com/hyperframes/pull/2611))
- **`stroke-linecap: round` paints a dot at zero dash length.** An un-drawn stroke isn't nothing — a round cap renders a visible dot at the path's start from frame 0, so a "line draws itself" effect begins with a stray mark sitting on screen. Gate the group's opacity until the draw begins, or use a butt cap. (Surfaced while validating this guide's examples.)
- **Give the path a static `d` attribute before anything measures it.** `getTotalLength()` returns 0 in Chrome if the path's `d` isn't set yet — whether because `d` is only assigned inside a function that hasn't run, or never assigned as a static attribute at all — and a dash animation built on a 0-length path is silently dead. (`svg_measure_before_path_d`, PR [#2611](https://github.com/heygen-com/hyperframes/pull/2611))
## Layout waivers, and the one that bites
`hyperframes check` flags a text block covered by another element as `text_occluded`, and
two attributes waive it: `data-layout-allow-overlap` for intentional layering, and
`data-layout-allow-occlusion` for something deliberately painted over type. Both are
legitimate — a caption designed to sit behind a matted subject needs one.
Two things about them are worth knowing before you reach for either:
- **`data-layout-allow-occlusion` also silences the WCAG contrast gate for that whole
subtree.** Validating this guide's confetti example, moving the attribute onto a cluster
root took contrast coverage from 73 checks to 13 — and a deliberately near-invisible
numeral inside it then passed. Scope the attribute to the narrowest node that needs it,
and check the contrast count afterwards; if it dropped, you have waived more than you
meant to.
- **The occlusion audit is stricter than it looks on atomic labels.** A single glyph — a
digit, a `$`, a `.` — flags at *any* coverage, so one confetti particle grazing one
character is a hard error. `pointer-events: none` does not exempt an element, and a CSS
`mask-image` lets the audit probe through masked-away cells and attribute the occlusion
to whatever paints behind them.
The cheaper fix is usually compositional: layer a particle burst *behind* the type rather
than over it. The type's ink cuts through, the reading stays clean, and no waiver is needed.
## Anti-patterns
Each one causes friction or wrong output for a specific engine reason — with the fix.
**Don't ask for React / Vue components.** Compositions are plain HTML with `data-*` attributes and a GSAP timeline; framework components force a translation step.
- ❌ `build a React component for the intro`
- ✅ `build the intro scene` (the agent writes composition HTML directly)
**Don't over-spec resolution or framerate.** Defaults (1920×1080, 30fps) render fast and look great; higher specs slow rendering meaningfully.
- ❌ `render in 4K 60fps` (for a social clip)
- ✅ say nothing — or `4K` only when the delivery target actually needs it
**Don't skip the slash command.** Without `/hyperframes`, the agent guesses at HTML video conventions instead of loading the framework's actual rules.
- ❌ `make me a video of...`
- ✅ `/hyperframes make me a video of...`
**Don't paste raw error logs.** `check` localizes the problem first (lint, then a browser gate); a bare log makes the agent re-derive what the tool already knows.
- ❌ pasting 200 lines of console output
- ✅ `check reports a missing asset in scene 2 — fix it`
**Don't assume the agent knows your assets.** It will look, but a path skips the search.
- ❌ `use my logo`
- ✅ `use assets/logo.svg`
**Don't override a workflow's designed style.** Each workflow skill carries an art-directed preset; fighting it produces a compromise, not your theme.
- ❌ `/pr-to-video ... dark theme`
- ✅ let the preset carry the look, or use a freeform build when you need full style control
**Don't hard-time a verbatim script.** With supplied narration text, duration follows the spoken words.
- ❌ `a 60-second explainer from this script: ...`
- ✅ `a ~60-second explainer from this script: ...`
+100
View File
@@ -0,0 +1,100 @@
---
title: Runtimes and 3D
description: "GSAP is the default and you rarely name it — but real 3D, existing animation files, and scene transitions each have a runtime worth pinning in the prompt."
---
Level 2's copy-paste examples include an isometric-cards prompt that asks to "Build the scene in Three.js via the adapter" — this chapter is why that line is there, and the rest of the runtime map for when GSAP, the default, isn't the right tool.
HyperFrames animates through the [frame-adapter](/concepts/frame-adapters) pattern: any runtime that can answer "what should the screen look like at frame N?" plugs in and renders deterministically. [GSAP](/guides/gsap-animation) is the default adapter and covers most motion — you rarely need to name it. The cases below are the ones where the default choice can go wrong, so the prompt should pick the runtime for you.
## Real 3D → Three.js via the adapter
This is the one pin to state every time. For anything with genuine **depth, lighting, or a camera** — a rotating product, a scene you move through, surfaces that catch light — ask for Three.js explicitly:
> Build the scene in **Three.js via the adapter**: a product model on a turntable, one key light and a soft fill, slow rotation.
- ❌ `isometric cards floating in CSS 3D with perspective`
- ✅ `build the isometric scene in Three.js via the adapter, with real depth and lighting`
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-3d-cards.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*The Three.js version of the isometric-cards prompt — real shadows and lighting, one-shot.*
The engine rationale: CSS `perspective` transforms skew flat planes — they read flat the moment lighting or parallax matters, because there is no light source and no camera, only projected rectangles. Three.js is a first-party seek-safe runtime (`hf-seek` events plus `window.__hfThreeTime`), so a real 3D scene renders frame-accurately like everything else. This is a validated default, not a preference — treat "real 3D" as "Three.js" unless you specifically want a flat, stylized fake-3D look.
Camera moves are part of the same rule. A "drone orbit", dolly, or push-in only exists where there's an actual camera:
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/camera-orbit.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*A seek-driven Three.js drone orbit — the camera sweeps a continuous arc; impossible with CSS transforms.*
- ❌ `a drone-orbit camera move around the logo` (with no runtime named — CSS has no camera to orbit)
- ✅ `orbit the camera around the logo — Three.js via the adapter`
## Existing animation files → Lottie
If you already have a designed animation — an After Effects export, a `.json` or `.lottie` file, an icon animation from a designer — don't ask the agent to redraw it. Point at the file and ask for Lottie:
> Play this Lottie file (`assets/loader.lottie`) centered, then fade to the title.
The Lottie adapter seeks the existing animation frame-by-frame, so the designer's work renders exactly as authored. Asking the agent to recreate it in GSAP throws away the source and lands somewhere approximate.
## Simple UI and text motion → the default
Fades, slides, staggers, counters, kinetic type, hover-style reveals — the everyday motion — is what GSAP does natively, and it's already the default. You don't name a runtime here; you describe the motion (see [Premium motion](/prompting/motion)):
> The headline slides up per word, staggered 0.1s apart, easing out as it lands.
CSS keyframes and the Web Animations API are also supported adapters, worth naming only when you're bringing existing CSS `@keyframes` or WAAPI code you want kept as-is. For a fresh ask, let the default handle it.
An SVG "line draws itself" effect (animated `strokeDasharray` / `strokeDashoffset`) is also GSAP-default territory — see the appendix's [SVG draw-on rows](/prompting/rules-and-anti-patterns#svg-draw-on) for two lint gotchas worth knowing before you ask for one.
## Scene-to-scene → shader transitions
Motion *within* a scene is one thing; the handoff *between* scenes is another. For a designed transition — a wipe, a glitch, a liquid dissolve — ask for a shader transition at that specific moment:
> Hard-cut between the first three scenes; use a **shader transition** (glitch) into the final logo scene.
Name the moments — shader transitions are for the two or three beats that deserve them, not every cut. See [Transitions](/prompting/transitions) for the vocabulary.
## Determinism surfaces in the prompt
Every runtime renders under the same [determinism](/concepts/determinism) contract: the frame clock is `t = frame / fps`, and there is **no wall clock, no live network at render time, and no unseeded randomness**. Two asks bump into this, so phrase them accordingly:
- ❌ `fetch the current BTC price and count up to it` — a render-time fetch isn't allowed; the render must be reproducible
- ✅ `count up to $67,400` (a fixed value baked in), or `read the target from a variable I pass at render time`
- ❌ `scatter 200 particles randomly` — unseeded randomness renders differently each frame and breaks reproducibility
- ✅ `scatter 200 particles from a seeded random layout` — say **seeded** and the positions are stable across frames and re-renders
The rule of thumb: anything the video needs to *know* must be present before rendering starts — baked in, or passed as a [variable](/prompting/variables-and-templating). Anything random must be seeded.
## Related
<CardGroup cols={2}>
<Card title="Frame adapters (concept)" icon="plug" href="/concepts/frame-adapters">
The seek-by-frame contract and the full list of supported runtimes.
</Card>
<Card title="Deterministic rendering" icon="lock" href="/concepts/determinism">
Why no live data and no unseeded randomness — the reproducibility guarantee.
</Card>
<Card title="Premium motion" icon="wand-magic-sparkles" href="/prompting/motion">
Describing everyday GSAP motion so it doesn't read as cheap.
</Card>
<Card title="Transitions" icon="film" href="/prompting/transitions">
Naming the scene-to-scene handoffs worth a shader transition.
</Card>
</CardGroup>
<Note>
**Capstone thread** — the [Level 7 film](/prompting/capstone)'s Depth region is real Three.js through the frame adapter: the timeline wire coils around a rim-lit faceted form, and the protagonist chip threads the coil's loops and passes behind the form with true depth occlusion (cut from the film, below).
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> **Depth (5256s).** The wire spirals off the flat plane into real 3D — a **Three.js scene via the frame adapter** (never CSS fake-3D): the camera descends following the wire as it coils around a rim-lit faceted form (ink material on charcoal), mono axis readouts landing on cue, then rises back to the plane with the wire leading the way out. The coil winds up out of the wire and collapses back onto it — its ends never float cut off in mid-air — and the protagonist chip joins the 3D scene for the crossing: it rides the wire straight through the coil's loops and passes behind the form with true depth occlusion, never floating over the geometry as a flat overlay.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-depth.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause, rendered — the region cut from the finished film.*
*Next: [Media and audio](/prompting/media-and-audio) — precise phrasing for voiceover, music, sound, and assets, instead of motion and rendering.*
+19
View File
@@ -0,0 +1,19 @@
---
title: The specification dial
description: "Spec density controls how far the result drifts from what you imagined — not whether it works."
---
The [skeleton](/prompting/anatomy) tells you where each decision lives; this page tells you how hard to turn each one. How much you specify sets how much you delegate — it controls how far the result can drift from what you imagined, not whether it works. Mood-level prompts ("snappy", "cinematic") get a designer's interpretation: coherent, but the agent picks the colors, staging, and details. Dense specs — hexes, timestamps, easings, pinned techniques — get *your* frame.
Three settings:
1. **Mood words** — "a warm, minimal logo sting". Fastest to write; the agent owns the look.
2. **Style tokens** — exact hexes, named textures ("halftone-dot", "paper-cut matte", "8-bit pixel"), type direction ("condensed caps", "thin geometric sans, wide tracking"). Cheap precision, no downside.
3. **Full visual spec** — every element gets shape + color + position + timing (see [visual specs](/prompting/visual-specs)). Use when you're recreating a specific look.
Density never hurts — a stronger model just uses less of its own taste. It matters most for technique choices a smaller model won't default well. Two worth pinning every time:
- **3D**: any real depth, lighting, or camera motion → say **"Three.js via the adapter"**. CSS perspective transforms read flat on lighting-critical scenes, and Three.js is a supported seek-safe runtime.
- **Sequencing**: resolve simultaneity in the text. "The counter fades out and READY stamps in at 4s" produces an overlap; "fades out fully by 4.2s; at 4.2s READY stamps in" doesn't.
*Next: [Vocabulary that changes output](/prompting/vocabulary) — the exact words that turn a mood into a specific framework setting.*
+115
View File
@@ -0,0 +1,115 @@
---
title: Storyboards
description: "For multi-scene work, don't prompt the scenes one by one — prompt the plan: the arc, the per-frame beats, and the pacing rule the build follows to fill them in."
---
[Variables and templating](/prompting/variables-and-templating) was about reusing one composition across many renders. This page is the other axis of scale: one film with many scenes. Past a handful of beats, describing each scene from a blank page — "then frame 2 shows X, then frame 3 shows Y" — is the slow way and the way that drifts, because nothing ties the frames to each other. The fast way is to prompt the **plan** once — the throughline, the job each frame does, the rule that paces reveals — and let the build put frames against it.
This narrative vocabulary is a writing discipline, not additional `STORYBOARD.md` schema: the workflow translates the plan into the smaller machine-readable shape the build consumes.
## Prompt the plan, not the scenes
A storyboard is a short, structured document that sits above the individual frames: one arc, one direction block that every frame inherits, and a light per-frame spec (not a full description) for each key moment. The workflow reads the plan and builds each frame's HTML sub-composition against it — so a plan that's precise about the *shape* of the film produces frames that already agree with each other on pacing, palette, and payoff, without you re-stating any of that per frame.
The trigger is naming the arc and asking for a storyboard rather than a single scene:
> Storyboard a 3-frame piece: hook → substance → landing, silent, ~15 seconds, with a callback that pays off the opening motif.
Everything below is the vocabulary that turns "storyboard" from a loose word into a plan the build can execute in one pass.
## State the film's shape once
Before any frame, fix four things that every frame will be judged against:
- **Message** — the one-sentence thesis the whole film has to prove. If a frame doesn't serve it, cut the frame, not the message.
- **Arc** — the beat sequence, named plainly: `Hook → Substance → Landing`, or `Hook → Problem → Solution → Proof → CTA`, or a shape word like "listicle" if the frames are parallel entries rather than a rising sequence.
- **Audience** — who it's for, in a phrase. It calibrates tone and jargon for every frame at once.
- **Mood** — one music/energy descriptor (e.g. "tense synth pulse, resolving to warm") that every frame's pacing should agree with, even in a silent piece.
Say these four once, up front, and no individual frame prompt needs to re-justify its tone.
## Set the direction once, apply it to every frame
A storyboard's direction block is the rules every frame obeys without restating them. Four are worth naming explicitly:
**Two-color discipline.** Name a ground color and one ink color, and say the rule out loud: nothing ever gets a second hue for emphasis — a bigger moment is bigger through inversion, weight, scale, or density, not a new color.
- ❌ `use the brand colors, plus a highlight color for the important bits`
- ✅ `ground: deep navy; ink: warm white. Emphasis = invert, scale up, or go denser — never a third color.`
**VO-paced reveals.** The rule itself is in [Media and audio](/prompting/media-and-audio#pace-reveals-to-the-narration); a storyboard is where you *apply* it per frame — at t=0 only what the narrator is saying is on screen, each part arriving on its spoken cue. Pair it with a hold behavior: say whether a held frame stays fully still or gets a subtle idle (never a slow drift or "breathing" — that reads as unfinished, not as a choice). If the piece is silent, keep the rule's shape but swap the trigger: reveals land on named timestamps instead of spoken clauses — the pacing still has to be deliberate, there's just no VO to key it to.
**One breather.** Across the whole film, name exactly one frame as the breather — the deliberately calmer, more static beat, or the longest held read. Every other frame keeps developing continuously. Naming it prevents the build from either over-animating the one frame that's supposed to let the audience exhale, or under-animating the rest to match it.
**The negative list.** One list of banned visual clichés, stated once and checked against every frame as it's built — not a fresh list per frame, but a standing filter applied per frame: no purple-blue AI gradients, no bokeh, no browser chrome, no drop-shadow cards, no infinite loops or randomness. Swap in whatever clichés are wrong for *your* film; the point is naming them before a frame drifts into one.
## Give each frame a job
With the direction block covering everything shared, each frame's own prompt only needs to say what's different about it:
```text
[type] the frame's category hook · benefit_highlight · social_proof · cta
[persuasion] the rhetorical device before/after · numbered enumeration · counterexample · callback + distillation
[beat] the emotional beat recognition + tension · aha · resolve + inevitability
[focal] the one thing the eye lands on
[roles] what's foreground / supporting / background, assigned explicitly
```
`persuasion` and `beat` are the two worth never skipping — they're what stops a frame from being "a scene that shows the stat" and turns it into "a scene that proves the stat, and here's how it *feels* to land." A frame with a named persuasion device and beat gives the build a reason for every choice; a frame with only a visual description gives it none.
## The callback
Introduce a motif early — a shape, a mark, a phrase, a piece of color — and have it return later, denser or fuller, as a deliberate payoff. Say both halves in the plan: where the motif is planted, and how it changes when it returns.
> A single thin accent dot appears top-right in frame 1 at low weight. In the landing frame, that same dot expands and fills into the full logo lockup — same motif, now complete.
Without stating the return explicitly, a rebuild is free to treat the early motif as throwaway texture — the callback only works if the plan says the second appearance is the *same* element, not a new one that resembles it.
## Worked example: a silent 3-frame storyboard
<Tip>
`storyboard-mini` below is deliberately small and silent — three frames, ~15 seconds, no narration — so the whole pattern (arc, direction block, per-frame job, one breather, one callback) is checkable in a single cheap render before you write a longer, narrated storyboard.
</Tip>
> Storyboard a 3-frame, ~15-second, 1920x1080 piece. Silent — no narration, no VO track. Message: "Fernwell gives you back the hours other tools take." Arc: Hook → Substance → Landing. Audience: small-team operators evaluating a new tool. Mood: tense synth pulse resolving to warm.
>
> Direction for every frame: ground color deep navy `#0b1220`, ink color warm off-white `#f4efe6` — nothing else gets a hue; emphasis is inversion, scale, or density only. Reveals stage on internal timestamps (the piece is silent, so no spoken cue) — at each frame's t=0 only its first element is on screen, the rest arrive on the timestamps below. Holds stay fully still, no drift or breathing. No purple-blue AI gradients, no bokeh, no browser chrome, no drop-shadow cards, no infinite loops or randomness.
>
> Frame 1 — Hook (0.04.0s), type: hook, persuasion: counterexample, beat: recognition + tension, focal: the headline. At 0.0s: bold ink headline "Most tools slow you down." slams in, centered. At 1.5s: a single thin accent dot (ink color, small, low weight) fades in top-right — the motif, planted quietly. Hold from 3.04.0s.
>
> Frame 2 — Substance, **the breather** (4.010.0s), type: benefit_highlight, persuasion: numbered enumeration, beat: aha, focal: the stat. This is the one deliberately calmer, more static frame in the piece — everything else develops continuously, this one mostly holds. At 4.0s: the accent dot from frame 1 carries over, now larger, sitting quietly left-of-center. At 5.0s: a big stat "3.2 hrs / week" fades in beside it, no motion after it lands. Static hold 6.010.0s.
>
> Frame 3 — Landing (10.015.0s), type: cta, persuasion: callback + distillation, beat: resolve + inevitability, focal: the completed motif. At 10.0s: the accent dot from frames 12 expands and fills into the full Fernwell wordmark lockup — same motif, now complete, denser and larger. At 12.0s: tagline "Fernwell. Built for flow." stamps in below it. Hold 13.515.0s.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/storyboard-mini.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited — no audio track, exactly as asked.*
## Related
<CardGroup cols={2}>
<Card title="Anatomy of a one-shot prompt" icon="list-ordered" href="/prompting/anatomy">
The six-part skeleton a single beat uses — the same discipline, one frame at a time.
</Card>
<Card title="Recreating something you saw" icon="film" href="/prompting/recreating-references">
Transcribing motion frame by frame — the same rigor a storyboard's per-frame timestamps need.
</Card>
<Card title="Design systems and brand" icon="palette" href="/prompting/design-systems">
The two-color discipline and brand tokens a storyboard's direction block draws from.
</Card>
<Card title="The HyperFrames pipeline" icon="route" href="/guides/pipeline">
`STORYBOARD.md` as a production artifact — the file format this chapter's prompts turn into.
</Card>
</CardGroup>
<Note>
**Capstone thread** — the [Level 7 film](/prompting/capstone) stretches this chapter's callback device across its whole runtime: the `<div class="clip">` chip typed in the opening rides the wire through every region and finally snaps into the render slot as the payoff (cut from the film, below).
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> **The clip card** — the `<div class="clip">` typed in the opening travels the whole journey: it slides onto the wire as a clip chip after being typed, rides ahead of the camera between regions (handing itself off — visible leaving one region and arriving in the next), and is the thing that finally renders at the end. It is the protagonist.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-render.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause paying off, rendered — the protagonist chip arriving at the render slot after a full minute on the wire.*
*Next: [Editing existing videos](/prompting/editing-existing-videos) — the editor verbs that turn a first render, storyboard or not, into the twenty edits after it.*
+104
View File
@@ -0,0 +1,104 @@
---
title: Transitions
description: "Map energy and mood to named shader and CSS transition blocks, and prompt them per seam."
---
[Motion](/prompting/motion)'s eight rules were written for what happens inside one scene. Rule 2's camera and rule 3's overlap apply *between* scenes too — a transition is that same grammar aimed at the cut itself, not a separate feature to bolt on afterward.
## What transitions do and when they trigger
A transition tells the viewer how two scenes relate — a crossfade says "this continues," a whip pan says "next point," a burn says "something changed." Most compositions with more than one scene want them: an *unmotivated* scene change reads as an accidental jump cut (see [rules and anti-patterns](/prompting/rules-and-anti-patterns)). A bare cut is legitimate when something else carries the continuity — a match cut on a shared shape, a beat grid, a prop that stays on screen — which is the alternatives table in [avoiding the slideshow](/prompting/motion#avoiding-the-slideshow). The skills add transitions by default, so prompts trigger this layer whenever you describe scene changes, crossfades, wipes, reveals, or a mood ("warm," "clinical," "glitchy") — or when you name a block directly.
Two families, both first-class:
- **[Shader transitions](/catalog/blocks/cross-warp-morph)** composite both scenes per-pixel on a WebGL canvas — they warp, dissolve, and morph in ways CSS cannot. Reach for these when the *handoff itself* is a moment (a hero reveal, a topic pivot with weight).
- **CSS transitions** animate the scene containers with opacity, transforms, clip-path, and filters. Simpler and lighter; reach for these for the 6070% of ordinary scene changes where the content is just continuing.
Choose by the effect you want, not by which is easier. See also the transitions table in [vocabulary](/prompting/vocabulary).
<Note>
The catalog pages linked here are standalone demos of each effect. In a real multi-scene build the agent wires the same effects through the `@hyperframes/shader-transitions` package (`HyperShader.init`) — you never need to say that; naming the transition is enough.
</Note>
## Energy → transition
Pick **one primary** transition for most scene changes, plus one or two accents for topic changes and the climax. Never use a different transition on every seam — that reads as chaos, not design.
| Energy | Shader primary | CSS primary | Feels like |
| ------ | -------------- | ----------- | ---------- |
| **Calm** (wellness, brand story, luxury) | [`cross-warp-morph`](/catalog/blocks/cross-warp-morph), [`thermal-distortion`](/catalog/blocks/thermal-distortion) | [`transitions-blur`](/catalog/blocks/transitions-blur), [`transitions-dissolve`](/catalog/blocks/transitions-dissolve) | Soft, slow, drifting |
| **Medium** (corporate, SaaS, explainer) | [`whip-pan`](/catalog/blocks/whip-pan), [`cinematic-zoom`](/catalog/blocks/cinematic-zoom) | [`transitions-push`](/catalog/blocks/transitions-push), [`transitions-cover`](/catalog/blocks/transitions-cover) | Clean, directional, decisive |
| **High** (promos, sports, music, launch) | [`ridged-burn`](/catalog/blocks/ridged-burn), [`glitch`](/catalog/blocks/glitch), [`chromatic-radial-split`](/catalog/blocks/chromatic-radial-split) | [`transitions-scale`](/catalog/blocks/transitions-scale), [`transitions-destruction`](/catalog/blocks/transitions-destruction), [`transitions-light`](/catalog/blocks/transitions-light) | Fast, punchy, aggressive |
## Mood → transition
Energy sets tempo; mood sets meaning. Describe the brand feeling and the agent picks a matching block.
| Mood | Shader | CSS |
| ---- | ------ | --- |
| **Warm / inviting** | [`light-leak`](/catalog/blocks/light-leak), [`thermal-distortion`](/catalog/blocks/thermal-distortion), [`cross-warp-morph`](/catalog/blocks/cross-warp-morph) | [`transitions-light`](/catalog/blocks/transitions-light), [`transitions-blur`](/catalog/blocks/transitions-blur) |
| **Cold / clinical** | [`gravitational-lens`](/catalog/blocks/gravitational-lens) | [`transitions-mechanical`](/catalog/blocks/transitions-mechanical), [`transitions-grid`](/catalog/blocks/transitions-grid) |
| **Editorial / magazine** | [`whip-pan`](/catalog/blocks/whip-pan) | [`transitions-push`](/catalog/blocks/transitions-push) |
| **Tech / futuristic** | [`glitch`](/catalog/blocks/glitch), [`chromatic-radial-split`](/catalog/blocks/chromatic-radial-split) | [`transitions-grid`](/catalog/blocks/transitions-grid) |
| **Tense / edgy** | [`ridged-burn`](/catalog/blocks/ridged-burn), [`glitch`](/catalog/blocks/glitch), [`domain-warp-dissolve`](/catalog/blocks/domain-warp-dissolve) | [`transitions-distortion`](/catalog/blocks/transitions-distortion) |
| **Playful / fun** | [`ripple-waves`](/catalog/blocks/ripple-waves), [`swirl-vortex`](/catalog/blocks/swirl-vortex) | [`transitions-3d`](/catalog/blocks/transitions-3d), [`transitions-radial`](/catalog/blocks/transitions-radial) |
| **Dramatic / cinematic** | [`cinematic-zoom`](/catalog/blocks/cinematic-zoom), [`gravitational-lens`](/catalog/blocks/gravitational-lens), [`domain-warp-dissolve`](/catalog/blocks/domain-warp-dissolve) | [`transitions-scale`](/catalog/blocks/transitions-scale) |
| **Premium / luxury** | [`cross-warp-morph`](/catalog/blocks/cross-warp-morph), [`thermal-distortion`](/catalog/blocks/thermal-distortion) | [`transitions-blur`](/catalog/blocks/transitions-blur), [`transitions-dissolve`](/catalog/blocks/transitions-dissolve) |
| **Retro / analog** | [`light-leak`](/catalog/blocks/light-leak) | [`transitions-light`](/catalog/blocks/transitions-light) |
Special-purpose seams: [`flash-through-white`](/catalog/blocks/flash-through-white) for a bright cut on an impact beat, [`sdf-iris`](/catalog/blocks/sdf-iris) for a clean iris reveal into a hero shot.
## Example prompts
Name the block and the seam — transitions are the one place where per-seam control usually beats letting the agent decide.
> /general-video Six-scene SaaS explainer. Use [`whip-pan`](/catalog/blocks/whip-pan) as the primary transition between related points, and one [`cinematic-zoom`](/catalog/blocks/cinematic-zoom) into the final pricing reveal. Medium energy, ~0.4s each.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/validate-transitions.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited — whip-pan on four seams, cinematic-zoom into the pricing reveal.*
> Between beats 2 and 3, transition with [`swirl-vortex`](/catalog/blocks/swirl-vortex); keep every other seam on a plain blur crossfade.
> Warm transitions for this wellness brand — [`light-leak`](/catalog/blocks/light-leak) between scenes, nothing sharp or mechanical. Slow, 0.60.8s.
> Music promo, high energy. [`glitch`](/catalog/blocks/glitch) on the phrase changes, [`ridged-burn`](/catalog/blocks/ridged-burn) on the drop. Fast cuts, 0.150.25s.
## Knobs
- **Duration** follows energy: calm 0.50.8s, medium 0.30.5s, high 0.150.3s. Say a number to pin it.
- **Primary + accents.** One primary carries most seams; spend your boldest accent on the climax. State the split ("`whip-pan` throughout, one `ridged-burn` on the reveal").
- **Per-seam placement.** "on phrase changes," "between beats 2 and 3," "into the final scene" all bind a transition to a specific cut.
- **Blur intensity** (CSS blur crossfades): heavier (2030px) for calm, light (36px) for high energy.
- **Easing presets:** `snappy`, `smooth`, `gentle`, `dramatic`, `instant`, `luxe` map to tuned duration/ease pairs.
## Failure modes
**Don't fade the outgoing scene out, then fade the next one in.** The renderer holds each scene's final state, so an explicit fade-out followed by an entrance renders as a jump cut with a dip in the middle — not a transition. The transition *is* the exit; both scenes hand off at the same instant.
- ❌ `fade scene 1 out, then fade scene 2 in`
- ✅ `cross-warp-morph from scene 1 to scene 2`
**Don't ask for a different transition on every seam.** A new effect at each cut reads as noise; consistency is what makes the one bold accent land.
- ❌ `use a different transition between each scene`
- ✅ `whip-pan as the primary, one glitch on the hero reveal`
**Don't leave "add transitions" unqualified when tone matters.** Bare requests get a sensible default; if the brand feeling is load-bearing, name the energy or mood (see [the specification dial](/prompting/specification-dial)).
- ❌ `add some transitions`
- ✅ `medium-energy editorial transitions — whip-pan primary`
**Don't invent transition names.** Only the blocks in the [shader](/catalog/blocks/cross-warp-morph) and CSS transition groups exist; a made-up name (`page-curl`, `star-iris`) sends the agent guessing at raw GLSL or an unsupported CSS effect.
- ❌ `add a page-curl transition`
- ✅ pick a real block, e.g. `sdf-iris` for an iris reveal
<Note>
**Capstone thread** — the [Level 7 film](/prompting/capstone) allows itself exactly one shader seam: the camera pushes through an `sdf-iris` lens into the Surface region (cut from the film, below) with the timeline wire visible through the iris the whole way — a lens the journey passes through, not a cut.
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> SANCTIONED SEAM #1: the camera pushes through an **`sdf-iris` shader transition** — the iris opens ONTO the continuation of the same wire (the wire is visible through the iris throughout; this is a lens the journey passes through, not a cut).
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-surface.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause, rendered — the region cut from the finished film.*
*Next: [Code animations](/prompting/code-blocks) — naming a block and pasting real code for walkthroughs, diffs, and terminal takes.*
+118
View File
@@ -0,0 +1,118 @@
---
title: Variables and templating
description: "Ask for the parts that should change to become named slots, then re-render the same composition with different values — one output per record."
---
[Design systems](/prompting/design-systems) covered the parts of a video that should *never* change per render — the brand. This page covers the parts that should: a card per customer, a stat per quarter, a name per recipient. When you know a composition will be reused, say so in the prompt, and name the parts that change. The agent turns them into declared [variables](/concepts/variables): typed, labeled slots filled at render time instead of hardcoded into the HTML.
The trigger phrase is simple — call out the slots:
> Build a 6-second title card. Make the **name**, the **logo**, and the **accent color** variables; everything else stays fixed.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/validate-variables-default.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Default variable values.*
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/validate-variables-variant.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*The same composition re-rendered with `--variables` overrides — different name, logo, and accent, zero re-prompting.*
The agent declares `data-composition-variables` on the composition root with the right type for each slot — `string` for the name, `color` for the accent, and a `string` URL for the logo (the escape hatch for any media asset — image, video, audio, or logo). A plain `<img>` logo needs no timing attributes; only `<video>`/`<audio>` variables involve the media wiring described in [variables](/concepts/variables). One composition, many fills.
## Say what type each slot is
The five variable types (`string`, `number`, `color`, `boolean`, `enum`) each render a different input in [Studio](/packages/studio) and validate differently at render time. You don't write the JSON — but naming the type in the prompt removes a guess:
> Variables: `plan` (enum: Free / Pro / Enterprise), `price` (number, shown as `$`), `featured` (boolean — toggles the ribbon), `headline` (text).
- ❌ `make the plan and price editable`
- ✅ `plan is an enum (Free / Pro / Enterprise); price is a number in dollars`
The engine rationale: an `enum` with declared options gets validated against that list at render time (`enum-out-of-range` is caught), and a `number` with a `unit` renders `$` formatting the odometer needs. A vague "editable" leaves the agent to pick a type, and a mistyped value only surfaces later.
## Template, then render one per record
Once the varying parts are variables, the same source renders once per data row. This is a real batch mode, not a copy-paste-per-video loop — the composition is authored once and fed a list of value sets:
> Build this as a template with `name` and `title` variables, then render one video per row of my data — output to `renders/{name}.mp4`.
The agent authors the composition, then runs a [batch render](/concepts/variables#batch-renders): a JSON array where each row is one set of variable values, one output file per row, with `{key}` placeholders in the output path drawn from each row. If your source is a CSV, say so — the agent converts it to the row array the batch expects. Add "fail on any undeclared or mistyped value" and it renders with `--strict-variables`, so a typo in a column name stops the run instead of silently rendering the default.
Everything shares one composition, so a design fix propagates to every output on the next render — you're not editing a hundred near-duplicate files.
## Personalization asks
Personalized-at-scale videos are the same pattern with the value set coming from your data:
> A 10-second welcome clip that greets each new signup by first name and shows their company logo. I'll supply a list of `{ firstName, logoUrl }` records.
The `firstName` is a `string`; the `logoUrl` is a `string` variable your composition assigns to an `<img src>`. Pass assets as **URL references, not inlined data** — URL-shaped values travel cleanly through both the local renderer and distributed [Lambda renders](/deploy/templates-on-lambda). If you're wiring this behind your own product UI or an agent rather than the CLI, the [`@hyperframes/sdk`](/packages/sdk) opens a base template and layers a sparse override set per instance, so the host stores only each record's delta.
## Declare up front — don't bake values in
The most common miss is describing the finished video with the values already fixed, then asking to "make it reusable" afterward:
- ❌ `Make a card that says "Acme — Pro plan — $49". Later I'll want other companies too.`
- ✅ `Make a plan card. Variables: company (text), plan (enum), price (number, $). Show "Acme / Pro / 49" as the default.`
The engine rationale: variables are runtime values a script applies to the live DOM, resolved from declared defaults, per-instance overrides, or the CLI in that precedence order. Declaring them up front means the reusable structure exists from the first render and the default is just one more value set. Baking `"Acme — Pro — $49"` into the markup produces a composition with no slots — reuse then means an edit pass over hardcoded text for every variant, which is exactly what variables exist to avoid.
## Prove the template actually re-skins
A template that never re-skins can pass every gate you have. `lint` and `check` verify structure, and `--strict-variables` catches an undeclared or mistyped key — but none of them can tell you whether the values you passed ever reached the DOM. The failure looks like success: the render completes, exits clean, and is **pixel-identical to the default**.
So test it differentially — render twice and compare:
```bash
hyperframes render --output default.mp4
hyperframes render --variables '{"ground":"#0d1420","ink":"#c8ff3d"}' --strict-variables --output reskin.mp4
```
Two identical files mean the override never reached the property you expected. Check that the variable ID is declared, the render command uses that exact ID, and the visible property is actually bound to its CSS custom property or variable value.
Render-time `--variables` overrides are global by variable ID. The compiler applies a matching override to CSS custom properties declared by the root and by sub-compositions pulled in with `data-composition-src`, while `data-variable-values` remains the per-instance way to give two mounts different values. Scoped JavaScript inside a sub-composition reads its per-instance variable table, so forward values at the mount point when that script calls `getVariables()` instead of reading CSS.
> **Variables:** expose `ground` (default `#0a0a0a`) and `ink` (default `#3CE6AC`) as composition variables on the single root file, bound via CSS custom properties everywhere (including the duotoned mural), so one `--variables` call re-skins the entire journey. It will be rendered twice: the default brand palette, and a second full render with `{"ground":"#0d1420","ink":"#c8ff3d"}`.
The [capstone](/prompting/capstone) keeps these variables on one root file for simplicity, not because templates require one file. Sub-compositions work as long as shared CSS-bound IDs are declared consistently; use mount-point values for instance-specific or JavaScript-read inputs.
## What can't be a variable
A few inputs are read once at compile time and no variable can move them: composition **dimensions** (`data-width` / `data-height`), the **root composition's total duration**, **frame rate**, and **output format / codec**. So this doesn't do what it reads like:
- ❌ `make the video length a variable so each render can be a different duration`
- ✅ `author one composition per target length` — or vary a *clip's* duration (that one is re-read from the live DOM)
If total length must differ per output, that's a different root `data-duration` per render, not a variable. See [what can't be a variable](/concepts/variables#what-cant-be-a-variable) for the full list and the compile-time-vs-live-DOM rule behind it.
<Note>
An authored CSS custom property always wins over a same-named variable. If your composition already defines its own `:root { --accent: ... }` (a hand-written theme token, say), a variable called `accent` never overwrites it — the authored value stands. A render-time `--variables` override still wins over both. If you need to override an authored value per render, use `--variables`, not a same-named declared variable.
</Note>
## Related
<CardGroup cols={2}>
<Card title="Variables (concept)" icon="sliders" href="/concepts/variables">
The mechanics: declaring types, per-instance overrides, batch renders, precedence.
</Card>
<Card title="@hyperframes/sdk" icon="code" href="/packages/sdk">
Template + sparse-override editing behind your own product UI or agent.
</Card>
<Card title="The specification dial" icon="gauge" href="/prompting/specification-dial">
How much to specify — and why naming the type is cheap precision.
</Card>
<Card title="Design systems and brand" icon="palette" href="/prompting/design-systems">
Brand tokens as variables that re-skin every reuse from one value.
</Card>
</CardGroup>
<Note>
**Capstone thread** — the [Level 7 film](/prompting/capstone) is a working template: one single-file composition, one variable scope, and its second render — navy ground, acid-green ink, every region re-skinned including the generated mural — is nothing but one `--variables` flag. Both full renders are embedded on the capstone page.
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> **Variables:** expose `ground` (default `#0a0a0a`) and `ink` (default `#3CE6AC`) as composition variables on the single root file, bound via CSS custom properties everywhere (including the duotoned mural), so one `--variables` call re-skins the entire journey. It will be rendered twice: the default brand palette, and a second full render with `{"ground":"#0d1420","ink":"#c8ff3d"}`.
>
> **Architecture constraint (technical):** single composition file — one `index.html`, one variable scope. […] No `data-composition-src` sub-files.
*Next: [Storyboards](/prompting/storyboards) — for multi-scene work, prompt the plan a frame-by-frame build fills in, not the scenes one by one.*
+131
View File
@@ -0,0 +1,131 @@
---
title: VFX and liquid glass
description: "Prompt device mockups, liquid-glass UI, shatter/portal/magnetic moments, and ambient polish — and know which effects need the canvas pipeline."
---
Your motion-graphics terminal piece from Level 1 already named one of these — `vfx-shatter`, for the beat where the terminal breaks apart. This chapter is the rest of that showy end of the catalog: 3D device mockups, frosted-glass Apple UI, and cinematic moments where HTML shatters or gets sucked through a portal. Two groups do the work — the [HTML-in-Canvas](/catalog/blocks/vfx-iphone-device) blocks (real WebGL, live HTML rendered as GPU textures) and the [Effects](/catalog/components/vignette) components (lightweight CSS polish). Knowing which is which is the difference between an effect that renders and one that surprises you. All of it slots into the [one-shot skeleton](/prompting/anatomy) at the "technique" step.
### Device mockups
To put your product UI inside a real phone or laptop, name [`vfx-iphone-device`](/catalog/blocks/vfx-iphone-device) — real GLTF iPhone 15 Pro Max and MacBook Pro models with live HTML-in-Canvas screen content, a product-review camera choreography, and a 360° turntable. For a styled iOS/macOS *environment* (home screen, desktop, dock) rather than a bare device, reach for the liquid-glass system blocks below.
> /product-launch-video 15-second 1920x1080 video. Our dashboard UI lives on the screen of a real iPhone 15 Pro Max that turntables slowly under product-review lighting, then a MacBook Pro slides in beside it showing the same UI wider. Use the `vfx-iphone-device` registry block. No narration.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/validate-device-mockup.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above (the block's demo UI on screen), unedited.*
**Ask for the device *and* what's on its screen.** The block renders live HTML into the screen but ships with its own demo UI — describing your UI (or pointing at screenshots/paths) is what makes the agent replace the block's screen content instead of shipping the demo.
- ❌ `show my app on an iPhone` (you'll get the block's built-in demo UI)
- ✅ `our dashboard UI (screenshots in assets/ui/) on the screen of the iPhone 15 Pro Max, turntabling`
### Liquid-glass UI treatments
The liquid-glass blocks are frosted-glass Apple-style UI floating over an aurora shader background. Pick by the surface you want:
| You want… | Name this block | Length |
| ------------------------------------------------ | ---------------------------------------------------------------- | ------ |
| A full iOS 26 home screen on a 3D iPhone | [`ios26-liquid-glass`](/catalog/blocks/ios26-liquid-glass) | 15s |
| A macOS Tahoe desktop on a 3D MacBook | [`macos-tahoe-liquid-glass`](/catalog/blocks/macos-tahoe-liquid-glass) | 15s |
| Glass notification cards | [`liquid-glass-notification`](/catalog/blocks/liquid-glass-notification) | 8s |
| A glass context menu | [`liquid-glass-context-menu`](/catalog/blocks/liquid-glass-context-menu) | 8s |
| Glass media / playback controls | [`liquid-glass-media-controls`](/catalog/blocks/liquid-glass-media-controls) | 8s |
| Glass stat cards, panels, pill chips | [`liquid-glass-widgets`](/catalog/blocks/liquid-glass-widgets) | 8s |
The four `liquid-glass-*` panel blocks share the aurora-shader stage, so they compose cleanly into one scene; `ios26-liquid-glass` and `macos-tahoe-liquid-glass` are complete device environments and generally stand alone.
> /motion-graphics 8-second 1920x1080 video. Frosted glass notification cards drift in and stack over an aurora shader background, each reading a fake alert ("Build passed", "Deploy live", "0 incidents"). Real translucency — the aurora must be visible through each card. Smoke the glass enough to keep white text above 3:1 against the brightest part of the aurora. No audio.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/glass-notify.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited — CSS `backdrop-filter` glass over a Three.js aurora.*
<Warning>
**The `liquid-glass-notification` block needs a GPU and fails quietly without one.** It is marked `stability: experimental` and tagged `webgpu` because its frosted surface is painted through `drawElementImage` with refraction computed by a WebGPU renderer. Validating this chapter, `navigator.gpu.requestAdapter()` returned `null` in every headless and headful Chrome tried — with `--enable-unsafe-webgpu`, `--use-angle=metal`, and hardware GPU mode — and the block's init simply bails. The failure mode is not an error: you get **no cards at all**, just the text floating on the background.
So for a render you need to be able to reproduce, ask for the *effect* — "real translucency, the background visible through the card" — and let the agent build it with CSS `backdrop-filter`, which renders anywhere. That's what the video above is. Reach for the WebGPU block only when you have confirmed a working adapter in your render environment and you specifically want refraction, specular, and chromatic aberration, which CSS cannot do.
</Warning>
**"Liquid glass" means the block, not a filter you're describing.** These are complete WebGL stages; asking for "a glassy blur on my div" gets you a CSS `backdrop-filter`, not this look.
- ❌ `add a liquid glass effect over my text`
- ✅ `use the liquid-glass-widgets registry block for the stat cards`
### Shatter, portal, magnetic, and cursor moments
The `vfx-*` blocks are single cinematic beats — spend them on a transition or a reveal, not a whole video:
| The moment | Name this block | Length |
| ------------------------------------------------ | --------------------------------------------------- | ------ |
| HTML shatters into glass fragments | [`vfx-shatter`](/catalog/blocks/vfx-shatter) | 12s |
| A dimension breach with volumetric light | [`vfx-portal`](/catalog/blocks/vfx-portal) | 10s |
| A magnetic-field particle visualization | [`vfx-magnetic`](/catalog/blocks/vfx-magnetic) | 15s |
| HTML floating over an organic liquid surface | [`vfx-liquid-background`](/catalog/blocks/vfx-liquid-background) | 12s |
| A dramatic text reveal with chromatic shadow rays | [`vfx-text-cursor`](/catalog/blocks/vfx-text-cursor) | 8s |
> /motion-graphics 8-second 1920x1080 video. Beat 1 (0-4s): a landing-page hero holds under directional light. Beat 2 (4-6s): the whole page shatters into glass fragments that scatter. Beat 3 (6-8s): bold white text slams in on black. Use the `vfx-shatter` registry block; the final beat reads "HTML IS VIDEO". No narration, no image or media files.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/vfx-shatter.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited.*
**Name the exact effect — "explode," "break," "burst" don't map.** Each block is a specific simulation.
- ❌ `make the UI explode`
- ✅ `the page shatters into glass fragments` → `vfx-shatter`, or `gets pulled through a portal` → `vfx-portal`
### Ambient polish
The [Effects](/catalog/components/vignette) components are lightweight, pure-CSS finishing passes you layer *on top* of a finished scene — grain, vignette, a light sweep, a subtle push:
| Say this | Component |
| ------------------------------ | ------------------------------------------------------ |
| Film grain / texture | [`grain-overlay`](/catalog/components/grain-overlay) |
| Darkened cinematic edges | [`vignette`](/catalog/components/vignette) |
| A light sweep across text | [`shimmer-sweep`](/catalog/components/shimmer-sweep) |
| Slow push-in on a card | [`parallax-zoom`](/catalog/components/parallax-zoom) |
| Card pulls back to reveal siblings | [`parallax-unzoom`](/catalog/components/parallax-unzoom) |
| Screen dissolves into a grid | [`grid-pixelate-wipe`](/catalog/components/grid-pixelate-wipe) |
These are the ambient layer of the [motion grammar](/prompting/motion): grain and a slow `parallax-zoom` keep a "held" beat alive instead of freezing. Never write "holds motionless" — a still final second is the biggest cheap-motion tell; let a grain overlay and a 2% push carry the hold.
> /motion-graphics 6-second 1920x1080 video. A wordmark logo — "HYPERFRAMES" in platinum on near-black — settles center-frame, then holds — but keep it alive with a film grain overlay and a slow 3% push-in, plus one shimmer sweep across the wordmark at 4s. Use the `grain-overlay`, `parallax-zoom`, and `shimmer-sweep` registry components. No audio.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/logo-polish.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the prompt above, unedited — the "hold" stays alive on grain, a 3% push, and one shimmer pass.*
**Reach for grain over a literal freeze.** The engine holds the final state exactly as written.
- ❌ `logo appears and holds still to the end`
- ✅ `logo settles, then a grain overlay and slow push keep the hold breathing` (see [ambient idle](/prompting/motion))
### When an effect needs the canvas pipeline
The distinction that trips people up: the **HTML-in-Canvas blocks are not CSS**. The device mockups, liquid-glass stages, and `vfx-*` blocks render live DOM into WebGL textures via the experimental `drawElementImage` API — which needs a Chrome flag. The [HTML-in-Canvas guide](/guides/html-in-canvas) documents the real behavior:
- **Rendering enables CanvasDrawElement automatically** (`--enable-features=CanvasDrawElement`), including inside Docker. That is enough for CanvasDrawElement-only blocks. Liquid-glass blocks that combine it with WebGPU still need a compatible browser such as Brave or Chrome Canary configured through `PRODUCER_HEADLESS_SHELL_PATH`; the bundled headless shell cannot run that combination.
- **Live preview in the Studio needs the flag turned on manually** (`chrome://flags/#canvas-draw-element` → *Enabled* → restart). Without it, these blocks fall back rather than showing the effect in preview.
- The blocks **feature-detect and degrade gracefully**, so a browser without the flag won't crash — it just won't show the WebGL treatment.
The Effects components above have none of this — they're plain CSS and animate everywhere, preview included. So if you need something visible in Studio preview today with zero setup, prefer the CSS Effects; the HTML-in-Canvas group is where the flag caveat lives.
<Warning>
Don't promise a stakeholder a live Studio preview of a liquid-glass or device block without confirming the Chrome flag is enabled on that machine — the rendered MP4 is unaffected, but the in-browser preview may fall back. See the [HTML-in-Canvas guide](/guides/html-in-canvas).
</Warning>
### Where to go next
- [Anatomy of a one-shot prompt](/prompting/anatomy) — the skeleton, and quoting on-screen copy.
- [Motion that reads premium](/prompting/motion) — the ambient-idle rule these polish layers serve.
- [Copy-paste examples](/prompting/examples) — a `vfx-liquid-background` social-card prompt to adapt.
- [HTML-in-Canvas guide](/guides/html-in-canvas) — how `drawElementImage` works and the flag details.
<Note>
**Capstone thread** — the [Level 7 film](/prompting/capstone)'s Surface region floats its inspector panels on the brand's glass tokens — translucent white over real backdrop blur, the mural visibly smearing through each panel (cut from the film, below).
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys the piece — prompt language you can lift for your own video:
> Inside: frosted-glass inspector panels (real translucency — blur over what's behind; the product's Studio design language) hover above the wire carrying live values (`ease: power3.out`, a color token, an fps readout) […] Glass surfaces use the file's `--glass-*` tokens with real backdrop blur.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-surface.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*That clause, rendered — the region cut from the finished film.*
*Next: [Runtimes and 3D](/prompting/runtimes-and-3d) — picking GSAP, Three.js, or Lottie by what the moment actually needs, including the real depth these effects render on.*
+45
View File
@@ -0,0 +1,45 @@
---
title: High-fidelity looks
description: "Write a visual spec — every element named, positioned, colored, and timed — to carry a specific look in words."
---
This is the dial's third setting, [full visual spec](/prompting/specification-dial), pushed all the way: not a word from the [vocabulary](/prompting/vocabulary) list, but every element of a scene written out like a designer's spec. You can carry a specific look in words alone when the prompt reads like a designer's spec — every element named, positioned, colored, and timed. Compare a loose description ("dark night scene, mountain, glowing ring, title fades in") with a spec:
> 8-second 1920x1080 title card. Scene, back to front: #0a0e2a night sky with faint grain; an orange radial glow (#ff6a2b core ~150px, falling off to transparent by ~430px) igniting at the mountain peak's right shoulder from 2s, positioned so its upper falloff reaches the wordmark's baseline; over it a huge concentric ring system (5 rings, 1px strokes at 8% white opacity, innermost ring glowing #4a5fd9) centered 40% from the top; a low-poly mountain (6-8 dark navy facets, #141a3d#1e2650) filling the lower third with its apex left of center, a white road S-curving up its face with a soft glow; thin horizontal cloud streaks (white, 6% opacity) drifting right at two heights; a man's silhouette, pure black, ~90px tall, bottom-right, fading in at 2.5s. At 3.5s "SHOWREEL" — thin geometric sans, ~140px, 0.35em tracking, white at 90% — fades in per letter across the ring center, the glow bleeding up through the letterforms above the peak. Slow 4% push-in across the full 8s. No audio.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/spec-showreel.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the spec above, unedited.*
<Note>
Two clauses in this spec were **corrected by building it.** The first draft listed the scene "back to front" but put the rings before a glow it described as sitting *behind* the rings, and it sized the glow at "~300px" — too small to physically reach the wordmark it was supposed to bleed through, given the ring center and mountain positions the same sentence pins down. Neither error is visible on the page; both are obvious the moment someone renders it. That is the argument for the [validation rule](/prompting/iterating#the-gates-cant-tell-you-its-good) this guide runs on: a spec dense enough to be useful is dense enough to be internally inconsistent, and only a render finds out.
</Note>
Every element is a primitive the agent can build directly. Two honest limits:
- **Organic illustration** (drawn characters, painterly texture) can't be fully carried by text — words underdetermine a drawing. Steer to geometric shape language ("flat rounded-geometric figure, circle head, no facial features"), or generate the artwork (see [When to generate artwork](/prompting/generated-artwork)).
- **Photographic and live-action content** must be supplied as files — mention the paths explicitly.
## The density contract
A spec tells the builder what each element *is*; a density contract tells it how full every frame must be. The [Level 7 film](/prompting/capstone) states it once and every region obeys — this is the clause in the [full capstone prompt](/prompting/capstone#the-full-prompt-verbatim) that buys it:
> Density: every region fills three roles — one focal element at display scale, at least two supporting elements on their own cues, and the chrome/wire. Asymmetric compositions; display type ~a tenth of frame width; three depth layers with parallax between them […]
That's the whole formula, reusable in any prompt: **one focal + at least two supporting + persistent chrome**, composed asymmetrically (60/40, never one element centered in emptiness), display type around a tenth of the frame width, and at least three depth layers so parallax can sell the space. Supporting elements land on their own cues — a frame that fills all three roles at t=0 is a poster, not a scene. Ask for the contract explicitly when a build keeps coming back sparse: "every scene carries one focal element, two supporting elements on staggered cues, and the persistent chrome" is a sentence a builder can be held to.
## Two more worked specs
The same density applied to a product-UI piece and a typographic piece — both one-shot from these exact words. Where a builder had to make a judgment call on the first pass, the spec below pins it, which is the editing loop these specs live by: build, see what the words underdetermined, tighten the words.
> 6-second 1920x1080. A frosted-glass command palette (640x84px, 20px radius, rgba(255,255,255,0.08) fill, 1px rgba(255,255,255,0.25) border, heavy backdrop blur) centered on a #0b0f1a field with two soft accent glows drifting slowly — #5b6cff upper-left, #22d3a5 lower-right, ~400px, 20% opacity. At 0.4s the palette scales in 0.96→1 settling with back.out(1.2). At 0.8s a grey placeholder "Search commands…" types on; at 2.2s it fades out over 0.2s and the query "render 4k" types in white. At 2.8s three result rows (56px tall, 12px gaps: icon square, label, shortcut chip) cascade in as a detached list below the fixed bar, staggered 0.12s, each rising 12px with back.out(1.4). At 4.2s a 10%-opacity #5b6cff fill sweeps left to right across the first row and its shortcut chip pulses once. Rows and glows keep a barely-visible drift to the end. No audio.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/spec-command-palette.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the spec above, unedited.*
> 7-second 1920x1080. Off-black #101014 field with fine static film grain at 4%. The word "PRECISION" in ~220px heavy condensed caps (a heavy system face condensed with scaleX 0.82), white, 0.02em tracking, centered: its letters assemble from alternating top/bottom 40px offsets with power3.out and a 0.05s stagger, starting 0.3s. At 1.8s a 2px hairline rule draws left-to-right beneath the word, 60% of its width, centered. At 2.4s a 40px tabular-mono counter fades in below and ticks 99.999 → 00.001 mm over 2.2s with expo.out deceleration. At 5.2s the whole lockup eases to 1.03 scale over 0.5s while the word cools from white to #d8d8de, then settles into a slow ±1% breathing idle to the end. No audio.
<video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/spec-precision-type.mp4#t=0.1" style={{ borderRadius: "0.5rem", marginTop: "0.75rem" }}></video>
*Rendered from the spec above, unedited.*
*Next: [Verified example prompts](/prompting/examples) — the level's gallery, read with the vocabulary you now have.*
+200
View File
@@ -0,0 +1,200 @@
---
title: Vocabulary that changes output
description: "Natural-language adjectives the skills map to specific framework settings — easing, captions, transitions, audio, voices."
---
[The dial](/prompting/specification-dial) told you *how much* to specify; this page is the word list for the cheap-precision setting — style tokens that cost you nothing to say and remove real ambiguity. The skills map natural-language adjectives to specific framework settings. Using the right word gets you the right result without specifying technical details.
## Motion & easing
Describe how motion should *feel* and the agent picks the matching GSAP ease:
| Say this | Agent uses | Feels like |
| ----------- | ---------------- | ------------------------------ |
| smooth | `power2.out` | Natural deceleration |
| snappy | `power4.out` | Quick and decisive |
| bouncy | `back.out` | Overshoots then settles |
| springy | `elastic.out` | Oscillates into place |
| dramatic | `expo.out` | Fast start, long glide |
| dreamy | `sine.inOut` | Slow, symmetrical |
**Timing shorthand:** fast (0.2s) = energy, medium (0.4s) = professional, slow (0.6s) = luxury, very slow (12s) = cinematic.
Each word, rendered — the same move, only the ease changes:
<table><tbody>
<tr><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/ease-power2-out.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>smooth · power2.out</div></td><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/ease-power4-out.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>snappy · power4.out</div></td><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/ease-back-out.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>bouncy · back.out</div></td></tr>
<tr><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/ease-elastic-out.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>springy · elastic.out</div></td><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/ease-expo-out.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>dramatic · expo.out</div></td><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/ease-sine-inout.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>dreamy · sine.inOut</div></td></tr>
</tbody></table>
## Camera language
Compositions have no physical camera, but camera words translate directly into scene transforms (or real camera moves in Three.js scenes):
| Say this | Agent builds |
| ------------------- | --------------------------------------------------------- |
| slow push-in | 48% scale increase across the scene, gentle ease |
| pull back / widen | scale decrease, often paired with elements entering |
| pan across | horizontal translate of the scene layer |
| drone orbit | continuous camera orbit (Three.js scenes) |
| crane down/up | vertical camera drift combined with a look-at shift |
| whip to | fast blurred slide into the next framing |
| parallax | layers translating at different rates for depth |
The same scene under each camera word:
<table><tbody>
<tr><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/camera-push-in.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>slow push-in</div></td><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/camera-pull-back.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>pull back</div></td><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/camera-pan.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>pan across</div></td><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/camera-crane.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>crane down</div></td></tr>
<tr><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/camera-whip.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>whip to</div></td><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/camera-parallax.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>parallax</div></td><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/camera-orbit.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>drone orbit (Three.js)</div></td></tr>
</tbody></table>
## Depth language
| Say this | Agent builds |
| ---------------------- | -------------------------------------------------------- |
| near-lens / foreground bokeh | large, heavily-blurred elements drifting close to camera |
| depth planes | 23 layers moving at different speeds |
| out-of-focus background | blurred, slower-moving back layer |
| shallow depth of field | sharp subject, blurred everything-else |
## Pacing language
| Say this | Agent builds |
| ----------------- | --------------------------------------------------- |
| punchy cuts | 1.54s per idea, hard cuts, overlapping entrances |
| cinematic holds | longer beats with ambient idle motion |
| beat-synced | cuts and accents on the music's beat grid |
| breathing room | a held moment before the next beat starts |
| ambient idle | 12% breathing scale + slow drift during holds |
## Caption tones
*The full chapter: [Caption styles](/prompting/captions-catalog).*
Describe the *energy* of your captions and the agent picks matching typography, size, and animation:
| Tone | Typography | Animation | Size range |
| ------------ | ---------------------- | ------------ | ---------- |
| Hype | Heavy weight fonts | Scale-pop | 7296px |
| Corporate | Clean sans-serif | Fade + slide | 5672px |
| Tutorial | Monospace | Typewriter | 4864px |
| Storytelling | Serif | Slow fade | 4456px |
| Social | Rounded, playful | Bounce | 5680px |
```
"Hype-style captions with scale-pop"
"Calm, elegant subtitles with slow fades"
"Karaoke-style word highlighting"
```
Per-word styling also works:
```
"Make brand names larger with accent color"
"Add bounce to emotional keywords"
"Highlight numbers differently"
```
## Transitions
*The full chapter: [Transitions](/prompting/transitions).*
Every multi-scene composition benefits from transitions. Describe the energy level:
| Energy | CSS option | Shader option |
| ------- | ---------------- | ------------------- |
| Calm | Blur crossfade | Cross-warp morph |
| Medium | Push slide | Whip pan |
| High | Zoom through | Glitch, ridged burn |
Or describe by mood:
```
"Warm transitions for this wellness brand"
"Cold, clinical transitions for tech"
"Playful bouncy transitions"
"Dramatic zoom for the reveal"
```
## Audio-reactive animation
*The full chapter: [Media and audio](/prompting/media-and-audio).*
Map audio frequency bands to visual properties. The agent uses these defaults:
| Audio band | Maps to | Visual effect |
| ---------- | --------- | ------------------- |
| Bass | `scale` | Pulse on the beat |
| Treble | `glow` | Shimmer intensity |
| Amplitude | `opacity` | Breathing |
| Mids | `shape` | Morphing |
```
"Make the text pulse with the beat"
"Add bass-driven scale to the logo"
"Create glow that responds to treble"
```
<Tip>
Keep audio-reactive effects subtle for text (36% intensity). Go bigger for backgrounds (1030%).
</Tip>
## Marker highlights
Hand-drawn emphasis effects for text:
| Mode | Effect | Best for |
| ----------- | ------------------ | ------------- |
| `highlight` | Marker sweep | Key phrases |
| `circle` | Hand-drawn ellipse | Single words |
| `burst` | Radiating lines | Hype moments |
| `scribble` | Chaotic scratch | Crossing out |
| `sketchout` | Rectangle outline | Callouts |
```
"Add a marker highlight sweep on 'revolutionary'"
"Circle this keyword with hand-drawn effect"
"Add burst lines around 'AMAZING'"
```
The five modes on the same sentence:
<table><tbody>
<tr><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/marker-highlight.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>highlight</div></td><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/marker-circle.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>circle</div></td><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/marker-burst.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>burst</div></td><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/marker-scribble.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>scribble</div></td><td style={{ padding: "6px", border: "none" }}><video controls muted loop playsinline preload="metadata" src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/marker-sketchout.mp4#t=0.1" style={{ width: "100%", borderRadius: "0.4rem" }}></video><div style={{ fontSize: "0.8em", textAlign: "center", opacity: 0.7 }}>sketchout</div></td></tr>
</tbody></table>
## Text-to-speech voices
TTS runs locally via Kokoro (no API key needed). Describe the content and the agent picks a voice, or request one directly:
| Content type | Recommended voices |
| ------------- | -------------------------- |
| Product demo | `af_heart`, `af_nova` |
| Tutorial | `am_adam`, `bf_emma` |
| Marketing | `af_sky`, `am_michael` |
```
"Generate narration for this script"
"Create voiceover with a professional female voice"
"Add TTS with British male voice at 1.1x speed"
```
## Rendering quality
*The full chapter: [Rendering and output](/prompting/rendering-and-output).*
| Quality | Use for |
| ---------- | ------------------------ |
| `draft` | Fast iteration |
| `standard` | Review and feedback |
| `high` | Final delivery |
```
"Quick draft render"
"Render at high quality"
"Export as transparent WebM"
```
*Next: [High-fidelity looks](/prompting/visual-specs) — past single words, into full visual specs that carry an entire look.*
+1 -1
View File
@@ -63,7 +63,7 @@ Copy any of these into your agent to get started.
</Card>
</CardGroup>
The agent handles scaffolding, animation, and rendering. See the [prompting guide](/guides/prompting) for more patterns, or the [pipeline guide](/guides/pipeline) for the 7-step structure (DESIGN, SCRIPT, STORYBOARD, …) that AI agents follow for multi-beat videos.
The agent handles scaffolding, animation, and rendering. See the [prompting guide](/prompting/overview) for more patterns, or the [pipeline guide](/guides/pipeline) for the 7-step structure (DESIGN, SCRIPT, STORYBOARD, …) that AI agents follow for multi-beat videos.
<Tip>
Skills encode HyperFrames-specific patterns — like required `class="clip"` on timed elements, GSAP timeline registration, adapter registries such as `window.__hfLottie`, and `data-*` attribute semantics — that are not in generic web docs. Using skills produces correct compositions from the start.
+1 -1
View File
@@ -960,7 +960,7 @@ export default defineCommand({
console.log(
` ${c.dim('"Using /hyperframes, create a 15-second intro about [your topic]"')}`,
);
console.log(` ${c.dim("More patterns: hyperframes.heygen.com/guides/prompting")}`);
console.log(` ${c.dim("More patterns: hyperframes.heygen.com/prompting/overview")}`);
console.log();
console.log(` ${c.accent("4.")} Preview in the browser:`);
console.log(` ${c.accent(`cd ${name}`)} && ${c.accent("npm run dev")}`);
+2 -2
View File
@@ -22,7 +22,7 @@
"files": 17
},
"hyperframes-animation": {
"hash": "3d9855346af7d51c",
"hash": "6c13cdce4f3a23c2",
"files": 121
},
"hyperframes-cli": {
@@ -30,7 +30,7 @@
"files": 11
},
"hyperframes-core": {
"hash": "a270c70ced8cf952",
"hash": "e9daaccaed05b8b4",
"files": 19
},
"hyperframes-creative": {
@@ -54,6 +54,7 @@ Animate any custom property. Works for color, length, number — anything CSS wi
- `svgOrigin` sets transform origin in the SVG's global coordinate space (not the element's local box). **Do not** combine `svgOrigin` with `transformOrigin` on the same element — pick one.
- Animate SVG transform attributes via the same alias names (`x`, `y`, `rotation`) — GSAP handles the SVG-specific quirks.
- **Resolve SVG geometry before building center-based transforms.** `createElementNS` is supported, but a detached, hidden, or zero-size element may not expose usable geometry when GSAP resolves a percentage `transformOrigin`. Attach and size the SVG before constructing the timeline, use an explicit `svgOrigin` when you know the canvas coordinates, or draw animated geometry around local `(0,0)` inside a positioning `<g>` for a center pivot that does not depend on a measured bounding box.
## Performance Rules
+7
View File
@@ -53,6 +53,13 @@ The standalone root needs an explicit **sized box** (`width`/`height` in px), an
Each composition registers **exactly one** `gsap.timeline({ paused: true })` at `window.__timelines["<id>"]` (key = root `data-composition-id`), built **synchronously** at page load. Render duration = root `data-duration`, not timeline length. Don't manually nest sub-timelines into the host. Full contract (incl. non-GSAP runtimes) → `references/determinism-rules.md` + `hyperframes-animation/adapters/`.
### First-pass lint gotchas (a guaranteed first build failure)
Two rules that `lint` **does** catch, but only after the fact — write them right the first time:
- The **root** composition element must carry `data-start="0"` (alongside `data-composition-id`/`data-width`/`data-height`); omitting it fails `lint` with `root_composition_missing_data_start`.
- Never pair a CSS initial `transform` with a GSAP tween on the **same** property — the CSS value and the tween's start fight and `lint` rejects it with `gsap_css_transform_conflict`. Set the initial state inside the tween with `gsap.fromTo(el, { x: -40 }, { x: 0 })` instead of a CSS `transform: translateX(-40px)`.
### Non-negotiable rules (silent bugs automated gates may miss)
Surfaced here; full rationale in the linked reference. Do not violate: