docs: make the ten hardest-reading prompting pages readable

Measured across all 136 hand-written pages, the twelve hardest to read were all
in prompting/. These ten were untouched — 29% to 40% of their sentences ran over
28 words, against a site median under 15%.

  media-and-audio           39.7% -> 6.2%    15 semicolons -> 6
  visual-specs              37.1% -> 19.0%   11 -> 9
  weekly-updates            39.1% -> 0.0%
  runtimes-and-3d           34.1% -> 17.5%
  design-systems            34.0% -> 4.3%
  recreating-references     33.3% -> 7.3%    16 -> 1
  storyboards               31.9% -> 5.4%
  variables-and-templating  30.3% -> 7.3%
  generated-artwork         29.4% -> 9.1%
  explainers                29.1% -> 3.3%    12 -> 1

Word counts barely move — 1752 to 1748, 1633 to 1617 — which is the point. The
facts and worked examples all survived; the sentences carrying them got shorter.
weekly-updates is an archive, so no date, version or fact changed there at all.

One anchor came back: capstone#the-full-prompt-verbatim, which these pages link
to and which an earlier commit had already moved to #the-prompt-word-for-word.
Repointed. Branch total: 0 broken pages, 0 broken anchors.
This commit is contained in:
ukimsanov
2026-08-04 12:05:32 -07:00
parent 663f3e8325
commit 8dba394d4d
10 changed files with 768 additions and 282 deletions
+50 -21
View File
@@ -5,74 +5,103 @@ description: "Point the agent at a source of brand truth — a design spec, a si
import { DocsVideo } from "/snippets/docs-video.jsx";
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.
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.
"Make it on-brand" is the vaguest thing you can ask. The agent has no way to know what your brand *is*, so it invents one.
The fix never changes. Give the agent a **source of brand truth**, and name it in the prompt. That source is a design spec, a live site, or a Figma file. Every section below is one way to do it.
## 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:
A HyperFrames project can carry a design spec: `frame.md`. Its frontmatter holds the machine-readable brand exact hex values, font families, weight relationships. The prose below the frontmatter carries intent and the brand's Do's and Don'ts.
When a spec 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.
Why this works: `on-brand` is a mood the agent has to guess at. A spec's frontmatter is normative. The agent quotes your hex and font family verbatim instead of approximating, then reads the prose for intent.
If your brand lives somewhere else, attach it. A PDF brand guide, a screenshot, or pasted hex codes all 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:
A design spec says what the brand *looks like*. It does **not** say how to compose a video frame. Name 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.
Why this works: 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.
So treat the spec in two halves:
- **Strict** — brand colors, fonts, weight relationships, and the background choice. If the brand is a light canvas, keep it light.
- **Scaled up for the medium** — type sizes, decorative opacity, border weight.
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:
When there's no spec but the brand is 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.
For a well-known brand, naming it is often enough. The agent researches the palette and typography itself.
One caveat is worth stating. A single-page-app homepage often returns a near-empty shell. 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:
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:
[Figma import](/guides/figma) freezes each import as a local file with recorded provenance. Renders never call Figma, so they stay deterministic. Brand variables come in as composition brand tokens.
- **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.
Two things are 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:
A series is a launch set, a weekly clip, or 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.
The constant parts come from that one authoring spec, or from one set of imported Figma tokens. 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. That's how the authored HTML picks up the new brand value.
Some compositions already exist and have to re-skin at render time. Declare the shared brand tokens as composition variables instead. Every scalar variable is applied as a `--{id}` CSS custom property on the composition root, so `var(--id)` in your CSS follows the override.
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:
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.
Prefer an SVG logo over a raster one. It scales and it animates.
State the paths explicitly. That way the agent wires up the real assets instead of generating stand-ins. It also keeps the render deterministic, because every asset is present locally before the render 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:
Each creation workflow comes with a designed look — `/product-launch-video`, `/faceless-explainer`, and the rest. Feed that look your brand inputs. Don't 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.
Why this works: a workflow's preset is a coherent, tested system. Its colors, spacing, motion, and component treatments hang together. Supply your brand inputs at the start and it applies your palette and type *within* that system.
Restyling scene by scene afterward pulls threads out of a design that was balanced as a whole. You spend more effort fighting the preset than handing it a spec would have cost.
## Related
@@ -92,10 +121,10 @@ The engine rationale: a workflow's preset is a coherent, tested system — color
</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).
**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 carries the display type, and the mint→cyan CTA gradient makes its single sanctioned appearance (cut from the film, below).
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-prompt-word-for-word) that buys the piece prompt language you can lift for your own video:
This is the clause in the [full capstone prompt](/prompting/capstone#the-prompt-word-for-word) that buys the piece. It's 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.
@@ -104,6 +133,6 @@ This is the clause in the [full capstone prompt](/prompting/capstone#the-prompt-
src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/capstone-region-render.mp4#t=0.1"
loop
/>
*That clause paying off, rendered — the closing lockup, where the brand file's display face and its one sanctioned gradient finally appear together.*
*That clause paying off, rendered. The closing lockup is 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.*
+65 -24
View File
@@ -5,49 +5,74 @@ description: "What to say to turn an article, notes, or a topic into a faceless
import { DocsVideo } from "/snippets/docs-video.jsx";
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.
The last page pointed a workflow at a website. This one drops the site. Your
text is the whole input. Every visual is invented, not 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.
Paste your text into one prompt to [`/faceless-explainer`](/prompting/overview).
That is enough for a finished explainer. No site, no footage, no design
decisions yet.
Verified, from the [examples](/prompting/examples) page — a ~60-second vertical explainer from pasted text:
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.
<DocsVideo
title="HyperFrames video: Example Explainer"
src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-explainer-v2.mp4#t=0.1"
src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/example-explainer.mp4#t=0.1"
portrait
loop
/>
*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.
Note the `~`. When you supply a 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.
A faceless explainer. Your text becomes a narrated video — an article, notes, a
topic, a brief. Every visual is invented per scene: typography, abstract
graphics, diagrams, data-viz.
**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 [`/faceless-explainer`](/prompting/overview) workflow does four things. It
picks a design system. It reshapes your text into a teaching story. It generates
its own TTS narration. Then it builds the video frame by frame.
**Faceless means there is nothing to capture.** No site, no footage, no asset
inventory. The visuals are designed downstream.
Pick a different workflow when you do have something to show:
- A product to sell → [`/product-launch-video`](/prompting/product-launch)
- A real site to show → [`/product-launch-video`](/prompting/product-launch)
with a tour brief
- A GitHub PR → [`/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.
You can steer all of these from the prompt, before you have 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 |
| **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 leave nothing to build the motion around. It reads as a text dump. |
| **Angle** | "concept" / "how-to" / "listicle" / "story" | The angle decides the story shape. The workflow reshapes your text into it instead of reading your paragraphs in order. |
| **Caption style** | "embedded captions, keywords highlighted in the accent color" | Captions are burned in. Naming the highlight color ties them to your palette instead of a default pill. |
| **Palette** | "brand color #FF5533 on off-black" | There is no site to borrow from, so the preset supplies a full palette. A named accent and ground personalize 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.
Scene density is the single biggest quality lever here. "One idea per scene"
turns a dense paragraph into a paced sequence. The workflow reorders and
compresses your text to hit it. That is what makes an explainer teach instead
of recite.
</Tip>
## Variants
@@ -56,7 +81,8 @@ What you can already steer from the prompt, before you've learned any technique.
<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.
Shorter runtime, landscape for YouTube or an embed. Fewer scenes means the
topic has to compress. Naming the takeaway tells the workflow what to keep.
<DocsVideo
title="HyperFrames video: Variant Explainer Landscape"
@@ -69,30 +95,45 @@ What you can already steer from the prompt, before you've learned any technique.
<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.
The listicle angle gives each item its own scene. Every scene reuses the same
number-and-label shape. The result reads as a countdown, not 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.
A how-to leans on diagrams as the load-bearing visual. Describe the diagram
*shape* per step — "node-and-arrow", or "a pipeline that fills left to
right". 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.
**"60 seconds" instead of "~60 seconds".** Same rule as on the
[product launch page](/prompting/product-launch#common-failure-modes). It bites
harder here, because the script is the whole video. You cannot know a supplied
script's spoken duration 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.
**Cramming ideas into a scene.** Every faceless visual is invented around a
single focal. Overload the scene and there is 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.
**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)
- ✅ that's a site or product video — use
[`/product-launch-video`](/prompting/product-launch)
**Leaving the look unspecified when you care.** There is no brand to read, so
the preset picks the palette. If you have colors, name them.
**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`
+41 -11
View File
@@ -1,25 +1,51 @@
---
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."
description: "Code-drawn wins for UI, type, geometry, and 3D. Illustration-led hero art comes from an image model, animated as layers."
---
import { DocsVideo } from "/snippets/docs-video.jsx";
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.
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, or sculptural objects that do not exist yet as a photo or a capture.
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 objectshand-drawn SVG tops out at clipart quality no matter how well you prompt. The fix is a hybrid:
## What code draws well — and what it doesn't
Code-drawn HTML, CSS, and SVG excel where the medium is native. That means UI
mockups, typography, geometric shapes, charts, pixel art, and 3D via Three.js.
**Illustration-led** hero art is the exception. Characters, painterly scenes,
and sculptural objects are what hand-drawn SVG is worst at. It tops out at
clipart quality no matter how well you prompt.
## The hybrid: generate the art, animate it in code
Ask for the still art from an image model, then move it with code:
> 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:
The generated image is one flat layer. Everything that has to move on its own
stays a code layer above it.
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.
## Three rules that make this work
1. **Generate on a solid contrasting background.** Magenta works well. It lets
the subject key 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` runs a human-segmentation matting
model, so it is tuned for photographs and people. It reads flat art
unreliably, and sometimes keeps the background instead of the subject. Save
`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. 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.
The same brief built both ways: four people arm in arm, warm gradient sweaters,
flat-illustration style. Same animation beats, same craft layer of lighting,
atmosphere, and grade. The only variable is where the figures came from.
<DocsVideo
title="HyperFrames video: Artwork Hand Drawn"
@@ -33,13 +59,17 @@ The same brief — four people arm in arm, warm gradient sweaters, flat-illustra
src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/artwork-generated.mp4#t=0.1"
loop
/>
*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.*
*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).
**Capstone thread** — the [Level 7 film](/prompting/capstone)'s Surface region
rides on generated artwork. The mural is generated palette-neutral, then
duotoned live by CSS from the composition's two variables. So even the raster
re-skins on a `--variables` render. That region is cut from the film below.
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-prompt-word-for-word) that buys the piece — prompt language you can lift for your own video:
This is the clause in the [full capstone prompt](/prompting/capstone#the-prompt-word-for-word)
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.**
+139 -37
View File
@@ -1,28 +1,50 @@
---
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."
description: "Ask for the voiceover, music, sound, captions, cutouts, and assets a composition needs, in phrasing the media pipeline acts on."
---
import { DocsVideo } from "/snippets/docs-video.jsx";
By now your video moves and reads right; this level gives it a voice. HyperFrames owns media *playback*; a sibling 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.
Your video moves and reads right. This level gives it a voice.
HyperFrames owns media *playback*. A sibling media pipeline resolves everything
else: voice, music, sound effects, images, icons, logos, captions, and
background removal. You describe what the composition needs. The agent resolves
each need to a frozen local file.
Precision is the whole craft here. Vague asks come back wrong. "Add some music"
and "no sound" are the two that bite most often, 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:
The agent picks a voice engine in a fixed order. HeyGen Starfish goes first if
your HeyGen account is configured. ElevenLabs is next if that key is set.
Otherwise the local Kokoro model runs, and it needs no API key.
Describe the content and let the agent pick a fitting voice. Or name the voice,
tone, and speed yourself:
> 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.
The [Vocabulary](/prompting/vocabulary#text-to-speech-voices) table maps content
types to Kokoro voices. `af_heart` and `af_nova` suit a product demo. `am_adam`
and `bf_emma` suit a tutorial. `af_sky` and `am_michael` suit marketing. Name a
voice 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:
Music resolves from a large catalog by mood. It should almost always sit *under*
the narration rather than 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.
@@ -32,12 +54,15 @@ Music resolves from a large catalog by mood, and it should almost always sit *un
- ✅ `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.
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:
SFX resolve from a bundled 19-file library plus the catalog. Cue them to
specific moments — a transition, a stamp-in, an impact. Don't sprinkle them.
> Add a whoosh on each of the three scene transitions.
@@ -45,56 +70,114 @@ SFX resolve from a small bundled library plus the catalog. Cue them to specific
## 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:
Once a video has a voice, the voice is the clock. Say 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. Skip this and 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:
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.
Two practical notes.
The agent gets word timings for free. 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.
The inverse rule matters just as much. The narration never waits for the
visuals. Pace the camera and the 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.
Captions come from word-level timestamps. Generate a voiceover and the timing
comes with it. For existing footage, transcription produces the timing: Parakeet
runs when it's installed, and whisper handles it otherwise. 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.
Caption *look* is its own vocabulary tone, size, per-word emphasis. See the
[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>`:
The `remove-background` command mattes a subject out of a video or image
locally. You get back 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.
One caveat is load-bearing. The built-in model (`u2net_human_seg`) is
**purpose-built for people**: head-and-shoulders or full-body, reasonably stable
framing, a background that contrasts with the subject. On **non-human subjects**
— products, animals, objects — it returns a mostly-empty mask.
So if you need to cut out a product, say so. The agent should route to a
different tool instead of running the person model and getting 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
- ✅ `matte the presenter out of assets/talk.mp4` for a person. 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.
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:
Layering footage is a compositing prompt: a talking head over a scene, a subject
in front of a headline, a PiP inset. The agent applies the frame-accuracy rules
for you. Naming the layout you want still 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.
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 goes inside a non-timed `<div>`, and the
*wrapper* is what gets animated. HyperFrames owns clip visibility, so
animating the media element directly fights the clip lifecycle.
- The base video and the cutout both mount at `data-start="0"`, so their
decoders stay in sync at the cut.
You rarely need to say either one. They're 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.
You don't need to pre-convert supplied footage before naming it in a prompt.
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.
Some codecs don'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 that 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. Proxying is optional. Pass `--no-proxy` per
command, or set `media.autoProxy: false` in `hyperframes.json` project-wide.
The same mechanism covers alpha-channel sources. ProRes 4444 and alpha WebM get
a VP8 + Opus WebM proxy 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.
@@ -103,42 +186,53 @@ None of this changes how you phrase the ask: name the footage by path like any o
src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/proxy-footage.mp4#t=0.1"
loop
/>
*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.*
*Rendered from the prompt above, unedited. The source clip is a plain H.265/HEVC
file. The render decoded it directly via FFmpeg. 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.
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:
For any asset you already have, an explicit path removes the most ambiguity. The
agent will search when you only describe an asset. A path settles *which* file.
- ❌ `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.)
This matters even when a search would have worked. 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 redraws them by hand. 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:
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.
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>
## The capstone thread
<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).
**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-prompt-word-for-word) that buys the piece — prompt language you can lift for your own video:
This is the clause in the [full capstone
prompt](/prompting/capstone#the-prompt-word-for-word) that buys the piece. It's
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.
@@ -149,4 +243,12 @@ This is the clause in the [full capstone prompt](/prompting/capstone#the-prompt-
/>
*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.*
*Next: [Design systems and brand](/prompting/design-systems) — pointing the
agent at a source of brand truth instead of describing a vibe.*
## Related topics
- [Vocabulary](/prompting/vocabulary) — voice names, caption tones, and audio-reactive mappings
- [Captions catalog](/prompting/captions-catalog) — styling the timed text this page produces
- [Remove a background](/guides/remove-background) — the matting command, its person-only caveat, and alternatives
- [Use images and video](/guides/video-components) — installable overlays, captions, and effects
+79 -13
View File
@@ -5,46 +5,112 @@ description: "Transcribe motion, iterate with absolute targets, distill the cons
import { DocsVideo } from "/snippets/docs-video.jsx";
[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.)
[Iterating](/prompting/iterating) covered the correction loop in general. This page is its hardest test: matching a specific reference you watched, rather than a look you're inventing.
From text alone, you can reach roughly 90% of a reference. That takes a specific workflow, and it takes knowing where the ceiling is. Both are below.
<Note>
The percentages on this page are observed results from this guide's own recreation builds, judged frame against frame. Treat them as the shape of the curve, not a guarantee.
</Note>
## 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.
Watch the reference frame by frame. 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 about 75% of the target. Structure and the 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.
Compare your render against the reference frame by frame. Then correct one axis at a time, freezing everything that already matches.
State each correction as an absolute value, not a relative nudge:
- ❌ `make dots 2x finer`
- ✅ `dot radius = 25% of row spacing`
Relative corrections pendulum — too big, then too small, then too big again. 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.
Iteration is a search. The constants it finds are reusable.
A distilled spec that one-shots a broadcast-style animated globe:
A prompt carrying them one-shots ~8090% of the converged quality on a fresh build. The discrete facts transfer losslessly: timings, counts, hexes, ratios, camera arcs. Continuous qualities still vary by a calibration note or two, like glow prominence or how the framing feels.
The converged composition file itself is the pixel-exact artifact. Renders are deterministic, so re-rendering that file reproduces the result.
Here is 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.
> 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).
> **FIELD**
>
> 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.
> - 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)
>
> 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.
> **GLOBE**
>
> 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.
> - 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
>
> 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.
> **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°)
> - end 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, a darker-green under-shade #3f9b5e at lower-left, 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.
<DocsVideo
title="HyperFrames video: Recreate Globe Oneshot"
src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/recreate-globe-oneshot-v2.mp4#t=0.1"
src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/recreate-globe-oneshot.mp4#t=0.1"
loop
/>
*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.
## Know where the text-only ceiling is
Words carry discrete, countable things losslessly. They underdetermine continuous perceptual qualities: bloom falloff, material feel, optical color mixing.
That last 10% doesn't close from text. It oscillates instead. If pixel-exact matters, keep the composition file.
## Related
+84 -40
View File
@@ -1,17 +1,27 @@
---
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."
description: "GSAP is the default and you rarely name it. Real 3D, existing animation files, and scene transitions each have a runtime worth pinning in the prompt."
---
import { DocsVideo } from "/snippets/docs-video.jsx";
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.
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. It's also the rest of the runtime map, for the cases where GSAP 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.
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, so you rarely need to name it. The cases below are the ones
where the default can go wrong. There, the prompt should pick the runtime.
## 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:
This is the one pin to state every time. Ask for Three.js explicitly for
anything with genuine **depth, lighting, or a camera** — a rotating product, a
scene you move through, surfaces that catch light:
> Build the scene in **Three.js via the adapter**: a product model on a turntable, one key light and a soft fill, slow rotation.
@@ -25,82 +35,107 @@ This is the one pin to state every time. For anything with genuine **depth, ligh
/>
*The Three.js version of the isometric-cards prompt — real shadows and lighting, one-shot.*
The engine rationale is simple. CSS `perspective` transforms skew flat planes.
There is no light source and no camera, only projected rectangles, so they read
flat the moment lighting or parallax matters.
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.
Three.js is a first-party seek-safe runtime. The adapter publishes HyperFrames
time as `window.__hfThreeTime` and dispatches an `hf-seek` event on each seek, so
a real 3D scene renders frame-accurately like everything else.
Camera moves are part of the same rule. A "drone orbit", dolly, or push-in only exists where there's an actual camera:
Treat "real 3D" as "Three.js." This is a validated default, not a preference.
The exception is when you specifically want a flat, stylized fake-3D look.
Camera moves are part of the same rule. A drone orbit, a dolly, or a push-in
only exists where there's an actual camera:
<DocsVideo
title="HyperFrames video: Camera Orbit"
src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/camera-orbit.mp4#t=0.1"
loop
/>
*A seek-driven Three.js drone orbit — the camera sweeps a continuous arc; impossible with CSS transforms.*
*A seek-driven Three.js drone orbit. The camera sweeps a continuous arc, which CSS transforms cannot do.*
- ❌ `a drone-orbit camera move around the logo` (with no runtime named — CSS has no camera to orbit)
- ❌ `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:
You may 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.
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)):
Fades, slides, staggers, counters, kinetic type, hover-style reveals — the
everyday motion — is what GSAP does natively. It's already the default. Don't
name a runtime here. Describe the motion instead. See [Motion that reads
premium](/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.
CSS keyframes and the Web Animations API are supported adapters too. Name them
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.
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:
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.
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:
Every runtime renders under the same [determinism](/concepts/determinism)
contract. The frame clock is `t = frame / fps`. There is **no wall clock, no live
network at render time, and no unseeded randomness**.
- ❌ `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`
Two asks bump into this, so phrase them accordingly.
- ❌ `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
Live data can't be fetched at render time, because the render must be
reproducible:
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.
- ❌ `fetch the current BTC price and count up to it`
- ✅ `count up to $67,400` with a fixed value baked in, or `read the target from a variable I pass at render time`
## Related
Unseeded randomness renders differently each frame and breaks reproducibility:
<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>
- ❌ `scatter 200 particles randomly`
- ✅ `scatter 200 particles from a seeded random layout` — say **seeded** and the positions stay 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.
## The capstone thread
<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).
**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-prompt-word-for-word) that buys the piece — prompt language you can lift for your own video:
This is the clause in the [full capstone
prompt](/prompting/capstone#the-prompt-word-for-word) that buys the piece. It's
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.
@@ -111,4 +146,13 @@ This is the clause in the [full capstone prompt](/prompting/capstone#the-prompt-
/>
*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.*
*Next: [Media and audio](/prompting/media-and-audio) — precise phrasing for
voiceover, music, sound, and assets, instead of motion and rendering.*
## Related topics
- [Frame adapters](/concepts/frame-adapters) — the seek-by-frame contract and the full list of supported runtimes
- [Deterministic Rendering](/concepts/determinism) — why no live data and no unseeded randomness
- [Motion that reads premium](/prompting/motion) — describing everyday GSAP motion so it doesn't read as cheap
- [Transitions](/prompting/transitions) — naming the scene-to-scene handoffs worth a shader transition
- [Animate with GSAP](/guides/gsap-animation) — the default adapter in detail
+45 -21
View File
@@ -5,17 +5,27 @@ description: "For multi-scene work, don't prompt the scenes one by one — promp
import { DocsVideo } from "/snippets/docs-video.jsx";
[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.
[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.
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.
Past a handful of beats, describing each scene from a blank page is the slow way. "Then frame 2 shows X, then frame 3 shows Y." It also drifts, because nothing ties the frames to each other.
Prompt the **plan** once instead — the throughline, the job each frame does, the rule that paces reveals. Then let the build put frames against it.
This narrative vocabulary is a writing discipline, not extra `STORYBOARD.md` schema. The workflow translates your plan into the smaller machine-readable shape the build consumes.
<Note>
"Storyboard" is also a question the agent asks in the [opening interview](/prompting/overview#the-interview-what-the-agent-asks-first) — answering yes there means the plan, the sketches, and the build get reviewed with you pass by pass on a live board. That answer changes the review process, not the route, and either way the plan this page teaches is what the build works from.
"Storyboard" is also a question the agent asks in the [opening interview](/prompting/overview#the-interview-what-the-agent-asks-first). Say yes there and the plan, the sketches, and the build all get reviewed with you pass by pass on a live board. That answer changes the review process, not the route. Either way, the plan this page teaches is what the build works from.
</Note>
## 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.
A storyboard is a short, structured document that sits above the individual frames. It holds three things:
- one arc
- one direction block that every frame inherits
- 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. Be precise about the *shape* of the film, and the frames come out already agreeing with each other on pacing, palette, and payoff. You never restate any of that per frame.
The trigger is naming the arc and asking for a storyboard rather than a single scene:
@@ -28,30 +38,40 @@ Everything below is the vocabulary that turns "storyboard" from a loose word int
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.
- **Arc** — the beat sequence, named plainly. `Hook → Substance → Landing`, or `Hook → Problem → Solution → Proof → CTA`. Use a shape word like "listicle" when 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.
- **Mood** — one music or energy descriptor, like "tense synth pulse, resolving to warm". Every frame's pacing should agree with it, even in a silent piece.
Say these four once, up front, and no individual frame prompt needs to re-justify its tone.
Say these four once, up front. Then no individual frame prompt has 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:
A storyboard's direction block is the set of 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.
**Two-color discipline.** Name a ground color and one ink color. Then say the rule out loud: nothing ever gets a second hue for emphasis. A bigger moment gets bigger through inversion, weight, scale, or density.
- ❌ `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.
**VO-paced reveals.** The rule itself lives 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, and each part arrives on its spoken cue.
**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.
Pair it with a hold behavior. Say whether a held frame stays fully still or gets a subtle idle. Never ask for a slow drift or "breathing" — that reads as unfinished, not as a choice.
**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.
If the piece is silent, keep the rule's shape and 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. It's the deliberately calmer, more static beat, or the longest held read. Every other frame keeps developing continuously.
Naming it prevents two failures. The build won't over-animate the one frame that's supposed to let the audience exhale, and it won't under-animate the rest to match it.
**The negative list.** One list of banned visual clichés, stated once. It's a standing filter, not a fresh list per frame. Every frame gets checked against it as it's built:
> 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:
The direction block covers everything shared. So 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
@@ -61,29 +81,33 @@ With the direction block covering everything shared, each frame's own prompt onl
[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.
Never skip `persuasion` and `beat`. Without them, a frame is "a scene that shows the stat." With them, it becomes "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.
Introduce a motif early: a shape, a mark, a phrase, a piece of color. 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.
State the return explicitly. Otherwise 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.
`storyboard-mini` below is deliberately small and silent: three frames, ~15 seconds, no narration. That makes the whole pattern checkable in one cheap render — arc, direction block, per-frame job, one breather, one callback. Do this 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.
> 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, since the piece is silent and there's 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 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.
@@ -112,10 +136,10 @@ Without stating the return explicitly, a rebuild is free to treat the early moti
</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).
**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. It 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-prompt-word-for-word) that buys the piece prompt language you can lift for your own video:
This is the clause in the [full capstone prompt](/prompting/capstone#the-prompt-word-for-word) that buys the piece. It's 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.
+116 -24
View File
@@ -5,9 +5,16 @@ description: "Ask for the parts that should change to become named slots, then r
import { DocsVideo } from "/snippets/docs-video.jsx";
[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.
[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.
The trigger phrase is simple — call out the slots:
When you know a composition will be reused, say so in the prompt. 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.
@@ -26,76 +33,156 @@ The trigger phrase is simple — call out the slots:
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.
The agent declares `data-composition-variables` on the composition root, with
the right type for each slot. The name is a `string`. The accent is a `color`.
The logo is an `image`, and a plain URL is a valid value for it. A plain `<img>`
logo needs no timing attributes. Only `<video>` and `<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:
There are seven variable types: `string`, `number`, `color`, `boolean`, `enum`,
`font`, and `image`. Each one validates differently at render time. In
[Studio](/packages/studio), `boolean`, `enum`, `color`, and `number` each get
their own control, while `string`, `font`, and `image` use a plain text input.
You don't write the JSON yourself. 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.
The engine rationale: an `enum` with declared options is checked against that
list at render time, so `enum-out-of-range` gets caught. A `number` can carry
`min`, `max`, `step`, and a `unit` label, which is what gives Studio a real
slider instead of a bare text box. Say "editable" and you leave the agent to
pick a type. A mistyped value then surfaces much 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:
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. You author the
composition once and feed it 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.
The agent authors the composition, then runs a
[batch render](/concepts/variables#batch-renders). The batch input is a JSON
array. Each row is one set of variable values, and each row produces one output
file. `{key}` placeholders in the output path get filled from that row.
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.
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`. A typo in a column name then 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 are 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:
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.
`firstName` is a `string`. `logoUrl` is the image slot your composition binds 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).
Wiring this behind your own product UI or an agent instead of the CLI? The
[`@hyperframes/sdk`](/packages/sdk) opens a base template and layers a sparse
override set per instance. The host then 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:
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.
The engine rationale: variables are runtime values a script applies to the live
DOM. They resolve from declared defaults first, then per-instance overrides,
then the CLI.
Declare them up front and the reusable structure exists from the first render.
The default is then just one more value set. Bake `"Acme — Pro — $49"` into the
markup and you get a composition with no slots. Reuse then means an edit pass
over hardcoded text for every variant. That 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**.
A template that never re-skins can pass every gate you have. `lint` and `check`
verify structure. `--strict-variables` catches an undeclared or mistyped key.
Neither can tell you whether the values you passed ever reached the DOM.
So test it differentially — render twice and compare:
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.
Two identical files mean the override never reached the property you expected.
Check three things. Is the variable ID declared? Does the render command use
that exact ID? Is the visible property 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.
Render-time `--variables` overrides are global by variable ID. The compiler
applies a matching override to CSS custom properties on the root and on
sub-compositions pulled in with `data-composition-src`. `data-variable-values`
is still the per-instance way to give two mounts different values.
> **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"}`.
Scoped JavaScript inside a sub-composition reads its own per-instance variable
table. So forward values at the mount point when that script calls
`getVariables()` instead of reading CSS.
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.
The [capstone](/prompting/capstone) keeps its 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. Its exact variable clause is
quoted at the bottom of this page.
## 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:
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**
- **output format, codec, or quality**
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)
- ✅ `author one composition per target length` — or vary a *clip's* duration,
which 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.
If total length must differ per output, that is 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.
An authored CSS custom property always wins over a same-named variable. Say
your composition already defines its own `:root { --accent: ... }` as a
hand-written theme token. A variable called `accent` never overwrites it — the
authored value stands. A render-time `--variables` override still wins over
both. So when you need to override an authored value per render, use
`--variables`, not a same-named declared variable.
</Note>
## Related
@@ -116,10 +203,15 @@ If total length must differ per output, that's a different root `data-duration`
</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.
**Capstone thread** — the [Level 7 film](/prompting/capstone) is a working
template. One single-file composition, one variable scope. Its second render is
nothing but one `--variables` flag: navy ground, acid-green ink, every region
re-skinned including the generated mural. Both full renders are embedded on the
capstone page.
</Note>
This is the clause in the [full capstone prompt](/prompting/capstone#the-prompt-word-for-word) that buys the piece — prompt language you can lift for your own video:
This is the clause in the [full capstone prompt](/prompting/capstone#the-prompt-word-for-word)
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"}`.
>
+69 -11
View File
@@ -1,41 +1,91 @@
---
title: High-fidelity looks
description: "Write a visual spec — every element named, positioned, colored, and timed — to carry a specific look in words."
description: "Write a visual spec that names, places, colors, and times every element, so words alone carry a specific look."
---
import { DocsVideo } from "/snippets/docs-video.jsx";
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:
This is the [specification dial](/prompting/specification-dial)'s third setting,
pushed all the way. Not a word from the [vocabulary](/prompting/vocabulary)
list. Every element of a scene, written out like a designer's spec.
Words alone can carry a specific look, but only when the prompt reads like a
spec. Every element named, positioned, colored, and timed.
## A loose description, then a spec
A loose description leaves the agent room to guess: "dark night scene, mountain,
glowing ring, title fades in." Here is the same scene written as 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.
<DocsVideo
title="HyperFrames video: Spec Showreel"
src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/spec-showreel-v2.mp4#t=0.1"
src="https://static.heygen.ai/hyperframes-oss/docs/images/prompting/spec-showreel.mp4#t=0.1"
loop
/>
*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.
Two clauses in this spec were **corrected by building it.**
The first draft listed the scene "back to front," then put the rings before a
glow it had described as sitting *behind* the rings.
It also sized that glow at "~300px." Too small: it can't physically reach the
wordmark it is 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:
## What a spec can't carry
- **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.
Every element above is a primitive the agent can build directly. Two honest
limits:
- **Organic illustration** — drawn characters, painterly texture. Words
underdetermine a drawing. Steer to geometric shape language instead ("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-prompt-word-for-word) that buys it:
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 the contract once and every
region obeys it. This is the clause in the [full capstone
prompt](/prompting/capstone#the-prompt-word-for-word) 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.
That's the whole formula, and it's reusable in any prompt: **one focal element,
at least two supporting, plus persistent chrome.** Compose asymmetrically — 60/40,
never one element centered in emptiness. Size display type at roughly a tenth of
the frame width. Use 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.
The same density, applied to a product-UI piece and a typographic piece. Both
were one-shot from these exact words.
Where a builder had to make a judgment call on the first pass, the spec below
pins it. That's the editing loop these specs live by: build, see what the words
underdetermined, then 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.
@@ -55,5 +105,13 @@ The same density applied to a product-UI piece and a typographic piece — both
/>
*Rendered from the spec above, unedited.*
*Next: [Verified example prompts](/prompting/examples) — the level's gallery, read with the vocabulary you now have.*
*Next: [Verified example prompts](/prompting/examples) — the level's gallery,
read with the vocabulary you now have.*
## Related topics
- [The specification dial](/prompting/specification-dial) — the three settings this page sits at the top of
- [Vocabulary that changes output](/prompting/vocabulary) — the single words a full spec replaces
- [When to generate artwork](/prompting/generated-artwork) — for the looks words underdetermine
- [Iterating](/prompting/iterating) — the render-and-check loop a dense spec needs
- [Verified example prompts](/prompting/examples) — more prompts at this density
+80 -80
View File
@@ -21,55 +21,55 @@ For exact versioned release notes, see the [Changelog](/changelog).
<Frame>
<DocsVideo
title="HyperFrames video: Weekly Changelog 2026 07 27 2026 08 03"
src="https://static.heygen.ai/hyperframes/changelog-videos/weekly-changelog-2026-07-27-2026-08-03-v2.mp4"
src="https://static.heygen.ai/hyperframes/changelog-videos/weekly-changelog-2026-07-27-2026-08-03.mp4"
/>
</Frame>
The Studio timeline is the headline. Expanding a track now shows one lane per animated property, with keyframe track headers, drag retiming, and virtualization on by default, and a long correctness sweep makes a keyframe edit land on the element you actually clicked. Three themed registry families add 29 catalog items from an outside contributor. Transparent GIF output works, the engine's media probing is hardened end to end, and a clean install now resolves a dependency graph with no advisories.
The Studio timeline is the headline. Expanding a track now shows one lane per animated property. Each lane gets a keyframe track header and drag retiming. Virtualization is on by default. A long correctness sweep makes a keyframe edit land on the element you actually clicked. Three themed registry families add 29 catalog items from an outside contributor. Transparent GIF output works. The engine's media probing is hardened end to end. A clean install now resolves a dependency graph with no advisories.
## Features
- **Per-property keyframe lanes.** Expanding a Studio track shows one lane per animated property instead of a single collapsed row. Keyframe track headers, per-lane retiming interactions, and variable timing and layout land with it, and keyframe percentages are computed in the tween's own time frame rather than the clip's ([fed5e5b71](https://github.com/heygen-com/hyperframes/commit/fed5e5b71df505b2598fe31522eba1710ea62c7c), [8bb31b394](https://github.com/heygen-com/hyperframes/commit/8bb31b3949ca7320242fbeb26451512deedae9cc), [e36fb385b](https://github.com/heygen-com/hyperframes/commit/e36fb385bc71af52c16c7e6059305c4bc2da2b7a), [d518972f8](https://github.com/heygen-com/hyperframes/commit/d518972f8b2b5c163875cd5d99c4ef6f0fce2113), [4a12eb9d9](https://github.com/heygen-com/hyperframes/commit/4a12eb9d9c404f20419cbac427d9808f92fef41c), [2d1b905a5](https://github.com/heygen-com/hyperframes/commit/2d1b905a56c55286c3ab76b3271c277517cec496), [#2791](https://github.com/heygen-com/hyperframes/pull/2791)).
- **Timeline virtualization by default.** Long timelines render only the visible rows, dense keyframe clusters stay readable, and Studio now tracks timeline performance so regressions show up as telemetry instead of as a complaint ([c6925e471](https://github.com/heygen-com/hyperframes/commit/c6925e471a1ab4df911f5b4aab08740084c5412c), [#2926](https://github.com/heygen-com/hyperframes/pull/2926), [723d3381c](https://github.com/heygen-com/hyperframes/commit/723d3381c4cabb3c238d883adf4fca487ead9df6), [#2925](https://github.com/heygen-com/hyperframes/pull/2925), [10b517dab](https://github.com/heygen-com/hyperframes/commit/10b517dab9515988cb82100c3975b53a47da1f35), [#2898](https://github.com/heygen-com/hyperframes/pull/2898)).
- **Bulk easing edits.** Selecting merged keyframes and changing their easing applies to the whole selection in one edit, and ease mode switches optimistically so the curve updates without waiting on a round trip ([10d45def0](https://github.com/heygen-com/hyperframes/commit/10d45def058248d0d59d1448beef6a6ee91ddeba), [659e22656](https://github.com/heygen-com/hyperframes/commit/659e22656e51aef54d079b3822fa40086d93726b), [#2695](https://github.com/heygen-com/hyperframes/pull/2695)).
- **Three themed registry families, 29 items.** `mk-*` is minimal presentation (9 items), `yt-*` is retro-broadcast creator (9 items), and `hw-*` is hand-drawn scribble (11 items). Contributed by @jbernard077, consolidating four earlier pull requests, cutting five items already covered by something we ship, and fixing the defects CI never got to report. `beat-freeze-cut` joins the transition set as a thirtieth item ([f252ec6d9](https://github.com/heygen-com/hyperframes/commit/f252ec6d9942486b4bbdbbdae05f25e683c4ab26), [d3d286a1a](https://github.com/heygen-com/hyperframes/commit/d3d286a1a53ed52d78ab3994195e83d2188ec163), [#2939](https://github.com/heygen-com/hyperframes/pull/2939), [#1933](https://github.com/heygen-com/hyperframes/pull/1933), [#1992](https://github.com/heygen-com/hyperframes/pull/1992), [#1993](https://github.com/heygen-com/hyperframes/pull/1993), [#1994](https://github.com/heygen-com/hyperframes/pull/1994)).
- **Fast capture on Windows hardware GPUs.** drawElement fast capture is open to Windows hardware GPU configurations, and Windows FFmpeg discovery candidates are validated before use ([cb30157eb](https://github.com/heygen-com/hyperframes/commit/cb30157ebbbcfedd3606b9d566d565f4412de45e), [#2841](https://github.com/heygen-com/hyperframes/pull/2841), [4ad1cf455](https://github.com/heygen-com/hyperframes/commit/4ad1cf4551b724e7d54f82f86b233ef734545250), [#2871](https://github.com/heygen-com/hyperframes/pull/2871)).
- **Two new layout check options.** `--layout proseCoverageFloor` is available opt-in, and `data-layout-allow-caption-zone` waives the caption-zone rule where an overlap is intentional ([209e6e014](https://github.com/heygen-com/hyperframes/commit/209e6e01486c3a452cfd27b0c1eb8c04417df27f), [#2834](https://github.com/heygen-com/hyperframes/pull/2834), [3a7950fd6](https://github.com/heygen-com/hyperframes/commit/3a7950fd63b501788f39f1fd44c12fc317139d79), [#2853](https://github.com/heygen-com/hyperframes/pull/2853)).
- **CLI state that survives a reset.** Circuit-breaker state rolls over across a config wipe, and install-state moved into the config directory so deleting that directory is a genuine full reset ([dfe92b2aa](https://github.com/heygen-com/hyperframes/commit/dfe92b2aabcea8d934e4696f9dfe063307fccdf7), [#2874](https://github.com/heygen-com/hyperframes/pull/2874), [dae1b63d4](https://github.com/heygen-com/hyperframes/commit/dae1b63d4a59fc6dff7fe19861d90efce9dc6948), [#2904](https://github.com/heygen-com/hyperframes/pull/2904)).
- **Per-property keyframe lanes.** Expanding a Studio track shows one lane per animated property. Before, you got a single collapsed row. Keyframe track headers land with it, plus per-lane retiming interactions and variable timing and layout. Keyframe percentages are now computed in the tween's own time frame, not the clip's ([fed5e5b71](https://github.com/heygen-com/hyperframes/commit/fed5e5b71df505b2598fe31522eba1710ea62c7c), [8bb31b394](https://github.com/heygen-com/hyperframes/commit/8bb31b3949ca7320242fbeb26451512deedae9cc), [e36fb385b](https://github.com/heygen-com/hyperframes/commit/e36fb385bc71af52c16c7e6059305c4bc2da2b7a), [d518972f8](https://github.com/heygen-com/hyperframes/commit/d518972f8b2b5c163875cd5d99c4ef6f0fce2113), [4a12eb9d9](https://github.com/heygen-com/hyperframes/commit/4a12eb9d9c404f20419cbac427d9808f92fef41c), [2d1b905a5](https://github.com/heygen-com/hyperframes/commit/2d1b905a56c55286c3ab76b3271c277517cec496), [#2791](https://github.com/heygen-com/hyperframes/pull/2791)).
- **Timeline virtualization by default.** Long timelines render only the visible rows. Dense keyframe clusters stay readable. Studio also tracks timeline performance now, so regressions show up as telemetry instead of as a complaint ([c6925e471](https://github.com/heygen-com/hyperframes/commit/c6925e471a1ab4df911f5b4aab08740084c5412c), [#2926](https://github.com/heygen-com/hyperframes/pull/2926), [723d3381c](https://github.com/heygen-com/hyperframes/commit/723d3381c4cabb3c238d883adf4fca487ead9df6), [#2925](https://github.com/heygen-com/hyperframes/pull/2925), [10b517dab](https://github.com/heygen-com/hyperframes/commit/10b517dab9515988cb82100c3975b53a47da1f35), [#2898](https://github.com/heygen-com/hyperframes/pull/2898)).
- **Bulk easing edits.** Select merged keyframes, change their easing, and the whole selection updates in one edit. Ease mode switches optimistically, so the curve updates without waiting on a round trip ([10d45def0](https://github.com/heygen-com/hyperframes/commit/10d45def058248d0d59d1448beef6a6ee91ddeba), [659e22656](https://github.com/heygen-com/hyperframes/commit/659e22656e51aef54d079b3822fa40086d93726b), [#2695](https://github.com/heygen-com/hyperframes/pull/2695)).
- **Three themed registry families, 29 items.** `mk-*` is minimal presentation (9 items). `yt-*` is retro-broadcast creator (9 items). `hw-*` is hand-drawn scribble (11 items). @jbernard077 contributed all three. The work consolidates four earlier pull requests, cuts five items already covered by something we ship, and fixes the defects CI never got to report. `beat-freeze-cut` joins the transition set as a thirtieth item ([f252ec6d9](https://github.com/heygen-com/hyperframes/commit/f252ec6d9942486b4bbdbbdae05f25e683c4ab26), [d3d286a1a](https://github.com/heygen-com/hyperframes/commit/d3d286a1a53ed52d78ab3994195e83d2188ec163), [#2939](https://github.com/heygen-com/hyperframes/pull/2939), [#1933](https://github.com/heygen-com/hyperframes/pull/1933), [#1992](https://github.com/heygen-com/hyperframes/pull/1992), [#1993](https://github.com/heygen-com/hyperframes/pull/1993), [#1994](https://github.com/heygen-com/hyperframes/pull/1994)).
- **Fast capture on Windows hardware GPUs.** drawElement fast capture is open to Windows hardware GPU configurations. Windows FFmpeg discovery candidates are validated before use ([cb30157eb](https://github.com/heygen-com/hyperframes/commit/cb30157ebbbcfedd3606b9d566d565f4412de45e), [#2841](https://github.com/heygen-com/hyperframes/pull/2841), [4ad1cf455](https://github.com/heygen-com/hyperframes/commit/4ad1cf4551b724e7d54f82f86b233ef734545250), [#2871](https://github.com/heygen-com/hyperframes/pull/2871)).
- **Two new layout check options.** `--layout proseCoverageFloor` is available opt-in. `data-layout-allow-caption-zone` waives the caption-zone rule where an overlap is intentional ([209e6e014](https://github.com/heygen-com/hyperframes/commit/209e6e01486c3a452cfd27b0c1eb8c04417df27f), [#2834](https://github.com/heygen-com/hyperframes/pull/2834), [3a7950fd6](https://github.com/heygen-com/hyperframes/commit/3a7950fd63b501788f39f1fd44c12fc317139d79), [#2853](https://github.com/heygen-com/hyperframes/pull/2853)).
- **CLI state that survives a reset.** Circuit-breaker state rolls over across a config wipe. Install-state moved into the config directory. Deleting that directory is now a genuine full reset ([dfe92b2aa](https://github.com/heygen-com/hyperframes/commit/dfe92b2aabcea8d934e4696f9dfe063307fccdf7), [#2874](https://github.com/heygen-com/hyperframes/pull/2874), [dae1b63d4](https://github.com/heygen-com/hyperframes/commit/dae1b63d4a59fc6dff7fe19861d90efce9dc6948), [#2904](https://github.com/heygen-com/hyperframes/pull/2904)).
## Fixes
- **Transparent GIFs stay transparent.** `--format gif` silently flattened transparent compositions, because the palette encoder received JPEG frames with no alpha plane. GIF is now treated as an alpha-capable format, its frames are captured as RGBA PNG, and the palette is encoded with explicit transparency semantics. Page-side shader compositing stays enabled for GIF, so shader transitions blend instead of hard-cutting ([6cfb05e38](https://github.com/heygen-com/hyperframes/commit/6cfb05e38bcb0ad02fa848500b462f0193a639b1), [#2327](https://github.com/heygen-com/hyperframes/pull/2327)).
- **A clean install reports no advisories.** Installing `hyperframes` previously surfaced five npm advisories, four of them high severity, with no clean upgrade path. Patched floors land for Hono Node Server, adm-zip, and Sharp, applied consistently across the CLI, engine, producer, and Cloud Run. ONNX Runtime is pinned to 1.21.1 as an interim choice because 1.23.2 independently pulls the vulnerable adm-zip range, and it keeps all six platform bindings ([13ac9e390](https://github.com/heygen-com/hyperframes/commit/13ac9e390585e75b80f5efd46393531315f0be2c), [#2855](https://github.com/heygen-com/hyperframes/pull/2855)).
- **Media probing hardened end to end.** Malformed frame-rate ratios are rejected instead of becoming NaN, file paths are passed after `--` so a name starting with `-` is not read as an option, the PNG walk anchors at IHDR and stops at cICP using native crc32, colour metadata merges per field with correct alpha detection, the AAC duration refinement is cancellable and limited to AAC-LC, and stdin is rejected while stdout is decoded correctly and bounded ([f75ca076b](https://github.com/heygen-com/hyperframes/commit/f75ca076b790d38e518af892b67a9ea42cca3dd5), [#2740](https://github.com/heygen-com/hyperframes/pull/2740), [2af3f4d0e](https://github.com/heygen-com/hyperframes/commit/2af3f4d0ed9fac2fadc1266bcadde04e6513d800), [#2912](https://github.com/heygen-com/hyperframes/pull/2912), [62b96c227](https://github.com/heygen-com/hyperframes/commit/62b96c227e7934ec8be4f7a806a99d7cd8decef0), [#2913](https://github.com/heygen-com/hyperframes/pull/2913), [19dc83bc2](https://github.com/heygen-com/hyperframes/commit/19dc83bc2beebebc136da776b2718594f41a469d), [4d563fa75](https://github.com/heygen-com/hyperframes/commit/4d563fa7523f0ef2e630d0da214b3b667bf7c982), [#2914](https://github.com/heygen-com/hyperframes/pull/2914), [242a42f6c](https://github.com/heygen-com/hyperframes/commit/242a42f6c94d5cc780477ed94c7938448639f67f), [361fd4992](https://github.com/heygen-com/hyperframes/commit/361fd4992636188e484e719a149f452b87c98b2b), [#2915](https://github.com/heygen-com/hyperframes/pull/2915), [9e275423e](https://github.com/heygen-com/hyperframes/commit/9e275423e2f44e37d73d5b7372a3ead3ba95cf75), [#2916](https://github.com/heygen-com/hyperframes/pull/2916), [96a6e8bd9](https://github.com/heygen-com/hyperframes/commit/96a6e8bd95410695340b2671b1dc167f14f49999)).
- **Bounded HDR and video extraction.** HDR and video frame extraction now run against explicit resource bounds instead of scaling with whatever the source demands ([233975737](https://github.com/heygen-com/hyperframes/commit/2339757377f1534900af5c84ad4025c6e576da9e), [#2955](https://github.com/heygen-com/hyperframes/pull/2955)).
- **Keyframe edits land on the element you clicked.** A run of fixes replaces inferred targets with the real one: every new tween is authored against a single element, a keyframe added at the playhead targets one element, lane edits and fallbacks resolve against the clicked element, colliding keyframes are targeted exactly, tweens are attributed to their real target, and a target the DOM has proved is not unique is never re-authored ([3d9243606](https://github.com/heygen-com/hyperframes/commit/3d924360667a15745d85dc060689d913b5dcc5d7), [#2849](https://github.com/heygen-com/hyperframes/pull/2849), [fd5555be7](https://github.com/heygen-com/hyperframes/commit/fd5555be758450d73335dd3f89c70329aac8ee35), [#2847](https://github.com/heygen-com/hyperframes/pull/2847), [ba8df2661](https://github.com/heygen-com/hyperframes/commit/ba8df2661c4af74897c734ad6376a0b1dc3fd5e2), [c20c5366d](https://github.com/heygen-com/hyperframes/commit/c20c5366da34c1a3444d3ff9282e01d92bf274d8), [#2846](https://github.com/heygen-com/hyperframes/pull/2846), [7482c22d8](https://github.com/heygen-com/hyperframes/commit/7482c22d821c0a46b48cb17421c4df73014dbbd6), [#2692](https://github.com/heygen-com/hyperframes/pull/2692), [59a818e80](https://github.com/heygen-com/hyperframes/commit/59a818e80a3ef561c5cb4a25ea30128cd18458b2), [f04cdb79c](https://github.com/heygen-com/hyperframes/commit/f04cdb79c5f1475fa7d6048036a4e0af354a60a2), [1f3fd2800](https://github.com/heygen-com/hyperframes/commit/1f3fd2800cd2e4df9e8a2c2b81ea11ed5b799630)).
- **The preview reload loop is gone.** Writes to `.transcode-cache` and the waveform cache touched files the project watcher was watching, so a preview reloaded in a loop and shader transitions stuck partway through. Generated caches are ignored by the watcher and excluded from project metadata ([7ea8250f5](https://github.com/heygen-com/hyperframes/commit/7ea8250f50b6d8824f21a3a21ef290c52c42d6fa), [bf739a4db](https://github.com/heygen-com/hyperframes/commit/bf739a4db2807373a0c294224dd4a101c6ed8954), [#2952](https://github.com/heygen-com/hyperframes/pull/2952), [1d01b9f2c](https://github.com/heygen-com/hyperframes/commit/1d01b9f2cfe44cb7899410e742a9d553c7807e19)).
- **Preview reliability and resolution.** Preview recovery is hardened, loading reliability improved, retained preview resources are released instead of accumulating, Chrome resolves on Windows, and a selected render resolution is honored rather than silently replaced ([c62bd4c45](https://github.com/heygen-com/hyperframes/commit/c62bd4c454d7a53aac39e03b10da8eeb2a93d77e), [18b9acac1](https://github.com/heygen-com/hyperframes/commit/18b9acac12269899b0bfacb289d9bde3e1b065e4), [#2837](https://github.com/heygen-com/hyperframes/pull/2837), [fbfffb1aa](https://github.com/heygen-com/hyperframes/commit/fbfffb1aa785fe48aa94a3570bd04c9128a80d18), [#2924](https://github.com/heygen-com/hyperframes/pull/2924), [d8d626537](https://github.com/heygen-com/hyperframes/commit/d8d626537bf6a830711f38e24cc8756bc26f8d48), [#2878](https://github.com/heygen-com/hyperframes/pull/2878), [f1655b930](https://github.com/heygen-com/hyperframes/commit/f1655b9302dc870c1e2ee24f19fd23897c767b42), [#2876](https://github.com/heygen-com/hyperframes/pull/2876)).
- **Drilling into sub-compositions.** Every host row stays on the drill path rather than just the top one, children expand against their resolved parent host, hidden state persists on expanded rows, sub-compositions stay expanded at the end of the timeline, and sub-composition clip timing is computed correctly ([acad7b268](https://github.com/heygen-com/hyperframes/commit/acad7b268ea1e9e4b35e15265ac6c8a178e43865), [d48440a9b](https://github.com/heygen-com/hyperframes/commit/d48440a9bacb83aef8007b3667e78e02a2481d8f), [12546985f](https://github.com/heygen-com/hyperframes/commit/12546985f588df8cf984d241dd27e3417edb3320), [5dad52370](https://github.com/heygen-com/hyperframes/commit/5dad52370f77a9a0618827279b8a4dcd86868456), [3e342cff9](https://github.com/heygen-com/hyperframes/commit/3e342cff9c642258509df64e0a47da38502591bd), [3f0c20f63](https://github.com/heygen-com/hyperframes/commit/3f0c20f633e9ac064e45df838e03d4412ddafda8), [#2845](https://github.com/heygen-com/hyperframes/pull/2845)).
- **Timeline accessibility and pointer targets.** Track numbers announced to assistive technology are the real ones and `aria-controls` points at the lanes, toolbar and lane controls meet the 24x24 pointer-target minimum, ruler clicks seek to the pressed position, Escape cancels a retime, the scrub clamps to 0 instead of being dropped, and popovers and tooltips stop clipping at panel edges ([1faa0cbda](https://github.com/heygen-com/hyperframes/commit/1faa0cbdadc71abdea220d8705682c0a18e499bb), [#2848](https://github.com/heygen-com/hyperframes/pull/2848), [4e74eefdd](https://github.com/heygen-com/hyperframes/commit/4e74eefddd4c1e38f7c97662cc52fae6d2a01698), [69020699d](https://github.com/heygen-com/hyperframes/commit/69020699dfb595194c8bf85117525045439bcdb2), [#2843](https://github.com/heygen-com/hyperframes/pull/2843), [706f537f3](https://github.com/heygen-com/hyperframes/commit/706f537f33bcf182f6b294806384d377c1b1d747), [b386b55f7](https://github.com/heygen-com/hyperframes/commit/b386b55f735a0217751e26245fbf677c8ff5d303), [f81ac7457](https://github.com/heygen-com/hyperframes/commit/f81ac745726d3195d07b79d7de9c31357ce8ab43), [#2890](https://github.com/heygen-com/hyperframes/pull/2890)).
- **Colour field and shortcuts panel.** The hex field accepts 3-digit shorthand, can be edited, and commits on an outside press. The shortcuts popup drops `aria-modal` because it is not modal, and dismisses on Escape or an outside press ([86f633f98](https://github.com/heygen-com/hyperframes/commit/86f633f98573a79f88422c8a24a69bd55d718c3d), [55614033e](https://github.com/heygen-com/hyperframes/commit/55614033e501b5e55bfd3a2099396b74443652cd), [7f0cadcbb](https://github.com/heygen-com/hyperframes/commit/7f0cadcbb1f24df12bee091d5156e3cbf67f51e4), [aa2811642](https://github.com/heygen-com/hyperframes/commit/aa2811642fecabdef58ce45329b115993d1e43fb), [#2844](https://github.com/heygen-com/hyperframes/pull/2844)).
- **Audio and nested timing.** Hidden audio is excluded from the render mix, plateaus survive sampled audio automation, nested template video timing is offset correctly, and distributed video metadata is validated before a plan is built ([b7160f69b](https://github.com/heygen-com/hyperframes/commit/b7160f69bba87254f8d33853df21a8be862605e1), [#2870](https://github.com/heygen-com/hyperframes/pull/2870), [04e0ccce4](https://github.com/heygen-com/hyperframes/commit/04e0ccce429c0c7c579cd06db09e7bad8fb550f3), [#2863](https://github.com/heygen-com/hyperframes/pull/2863), [9bbb6d50a](https://github.com/heygen-com/hyperframes/commit/9bbb6d50a02e128fcdab9ae20d33649b65406788), [#2859](https://github.com/heygen-com/hyperframes/pull/2859), [557d82b6a](https://github.com/heygen-com/hyperframes/commit/557d82b6a9547e403598713e2840c41736d35544), [#2839](https://github.com/heygen-com/hyperframes/pull/2839)).
- **Fonts, downloads, and browsers.** Large local fonts stay file-backed instead of being inlined, transient deterministic font fetches retry, stalled download cleanup is awaited, a host-compatible cached browser is selected, and the check navigation timeout is honored ([87791fd01](https://github.com/heygen-com/hyperframes/commit/87791fd01d93ee82226bf09d98a4dfe339c42307), [#2864](https://github.com/heygen-com/hyperframes/pull/2864), [20f4f8f49](https://github.com/heygen-com/hyperframes/commit/20f4f8f49c19181adfb4194f1965ee00845219f9), [#2865](https://github.com/heygen-com/hyperframes/pull/2865), [0bf33cb11](https://github.com/heygen-com/hyperframes/commit/0bf33cb117b111e89ee8d031d565c291aabf9160), [#2835](https://github.com/heygen-com/hyperframes/pull/2835), [85f0c9d35](https://github.com/heygen-com/hyperframes/commit/85f0c9d3546b1973dfba884fe6d2a7a8188d4ad3), [#2861](https://github.com/heygen-com/hyperframes/pull/2861), [fdc593289](https://github.com/heygen-com/hyperframes/commit/fdc5932897ea3ecf778d520da77e6f6d493017d5), [#2860](https://github.com/heygen-com/hyperframes/pull/2860)).
- **Website capture is bounded and honest.** Capture runtime stages run against a live budget that is validated and propagated, a blocked page is rejected outright rather than passing a login wall through as a screenshot, failure diagnostics are preserved, skipped Lottie previews are omitted, and bounded vision failures are reported ([765a5ae83](https://github.com/heygen-com/hyperframes/commit/765a5ae83f31b01b65426f34a3c80a47330456a0), [49091e614](https://github.com/heygen-com/hyperframes/commit/49091e6142c98b9fcab967c88e210df601a118d9), [d3607606e](https://github.com/heygen-com/hyperframes/commit/d3607606eeced5c826923f536d0d33d0cd799fb9), [22e3cca96](https://github.com/heygen-com/hyperframes/commit/22e3cca966e4f3c5dfe0f8b716dec80259206300), [9ae000726](https://github.com/heygen-com/hyperframes/commit/9ae00072614f35785e4a1d7bad48787451462a91), [ac9458888](https://github.com/heygen-com/hyperframes/commit/ac9458888c7a6e05aa6b4d5d74063b5859782d02), [dfc60797a](https://github.com/heygen-com/hyperframes/commit/dfc60797a2adeddef120f4c800f06b30cc155117), [b38e90740](https://github.com/heygen-com/hyperframes/commit/b38e90740407ac50fd83e124b8211d3efc7282be), [#2933](https://github.com/heygen-com/hyperframes/pull/2933)).
- **Fewer false lint failures.** An asset `src` still holding an unresolved templating token is no longer reported as a missing asset, and the placeholder skip is consolidated into one shared predicate ([6c185f252](https://github.com/heygen-com/hyperframes/commit/6c185f252f38a5a1f1dc9e23f17ef98f63ea18c7), [#2893](https://github.com/heygen-com/hyperframes/pull/2893), [5a6e4b1a8](https://github.com/heygen-com/hyperframes/commit/5a6e4b1a8f4176250b5c92b63845399fcfe7f256), [#2894](https://github.com/heygen-com/hyperframes/pull/2894)).
- **Registry polish.** `mk` card offsets animate with transforms instead of `top` and `left`, connector geometry and family accents are corrected, demo legibility is improved, and the device timeline registers synchronously ([ce7d75dba](https://github.com/heygen-com/hyperframes/commit/ce7d75dbaa29e3920216f6e848e68b4b6257602e), [#2960](https://github.com/heygen-com/hyperframes/pull/2960), [35231e964](https://github.com/heygen-com/hyperframes/commit/35231e964daab6d6b73d8dc3917f9f95f5944157), [2bb620517](https://github.com/heygen-com/hyperframes/commit/2bb620517749eefaa19e5fefbb2a85b9d6f538b8), [#2546](https://github.com/heygen-com/hyperframes/pull/2546)).
- **Transparent GIFs stay transparent.** `--format gif` used to silently flatten transparent compositions. The palette encoder was receiving JPEG frames with no alpha plane. GIF is now treated as an alpha-capable format. Its frames are captured as RGBA PNG. The palette is encoded with explicit transparency semantics. Page-side shader compositing stays enabled for GIF, so shader transitions blend instead of hard-cutting ([6cfb05e38](https://github.com/heygen-com/hyperframes/commit/6cfb05e38bcb0ad02fa848500b462f0193a639b1), [#2327](https://github.com/heygen-com/hyperframes/pull/2327)).
- **A clean install reports no advisories.** Installing `hyperframes` used to surface five npm advisories, four of them high severity. There was no clean upgrade path. Patched floors land for Hono Node Server, adm-zip, and Sharp. They apply consistently across the CLI, engine, producer, and Cloud Run. ONNX Runtime is pinned to 1.21.1 as an interim choice, because 1.23.2 independently pulls the vulnerable adm-zip range. The pin keeps all six platform bindings ([13ac9e390](https://github.com/heygen-com/hyperframes/commit/13ac9e390585e75b80f5efd46393531315f0be2c), [#2855](https://github.com/heygen-com/hyperframes/pull/2855)).
- **Media probing hardened end to end.** Malformed frame-rate ratios are rejected instead of becoming NaN. File paths are passed after `--`, so a name starting with `-` is not read as an option. The PNG walk anchors at IHDR and stops at cICP, using native crc32. Colour metadata merges per field, with correct alpha detection. The AAC duration refinement is cancellable and limited to AAC-LC. Stdin is rejected. Stdout is decoded correctly and bounded ([f75ca076b](https://github.com/heygen-com/hyperframes/commit/f75ca076b790d38e518af892b67a9ea42cca3dd5), [#2740](https://github.com/heygen-com/hyperframes/pull/2740), [2af3f4d0e](https://github.com/heygen-com/hyperframes/commit/2af3f4d0ed9fac2fadc1266bcadde04e6513d800), [#2912](https://github.com/heygen-com/hyperframes/pull/2912), [62b96c227](https://github.com/heygen-com/hyperframes/commit/62b96c227e7934ec8be4f7a806a99d7cd8decef0), [#2913](https://github.com/heygen-com/hyperframes/pull/2913), [19dc83bc2](https://github.com/heygen-com/hyperframes/commit/19dc83bc2beebebc136da776b2718594f41a469d), [4d563fa75](https://github.com/heygen-com/hyperframes/commit/4d563fa7523f0ef2e630d0da214b3b667bf7c982), [#2914](https://github.com/heygen-com/hyperframes/pull/2914), [242a42f6c](https://github.com/heygen-com/hyperframes/commit/242a42f6c94d5cc780477ed94c7938448639f67f), [361fd4992](https://github.com/heygen-com/hyperframes/commit/361fd4992636188e484e719a149f452b87c98b2b), [#2915](https://github.com/heygen-com/hyperframes/pull/2915), [9e275423e](https://github.com/heygen-com/hyperframes/commit/9e275423e2f44e37d73d5b7372a3ead3ba95cf75), [#2916](https://github.com/heygen-com/hyperframes/pull/2916), [96a6e8bd9](https://github.com/heygen-com/hyperframes/commit/96a6e8bd95410695340b2671b1dc167f14f49999)).
- **Bounded HDR and video extraction.** HDR and video frame extraction now run against explicit resource bounds. They no longer scale with whatever the source demands ([233975737](https://github.com/heygen-com/hyperframes/commit/2339757377f1534900af5c84ad4025c6e576da9e), [#2955](https://github.com/heygen-com/hyperframes/pull/2955)).
- **Keyframe edits land on the element you clicked.** A run of fixes replaces inferred targets with the real one. Every new tween is authored against a single element. A keyframe added at the playhead targets one element. Lane edits and fallbacks resolve against the clicked element. Colliding keyframes are targeted exactly. Tweens are attributed to their real target. A target the DOM has proved is not unique is never re-authored ([3d9243606](https://github.com/heygen-com/hyperframes/commit/3d924360667a15745d85dc060689d913b5dcc5d7), [#2849](https://github.com/heygen-com/hyperframes/pull/2849), [fd5555be7](https://github.com/heygen-com/hyperframes/commit/fd5555be758450d73335dd3f89c70329aac8ee35), [#2847](https://github.com/heygen-com/hyperframes/pull/2847), [ba8df2661](https://github.com/heygen-com/hyperframes/commit/ba8df2661c4af74897c734ad6376a0b1dc3fd5e2), [c20c5366d](https://github.com/heygen-com/hyperframes/commit/c20c5366da34c1a3444d3ff9282e01d92bf274d8), [#2846](https://github.com/heygen-com/hyperframes/pull/2846), [7482c22d8](https://github.com/heygen-com/hyperframes/commit/7482c22d821c0a46b48cb17421c4df73014dbbd6), [#2692](https://github.com/heygen-com/hyperframes/pull/2692), [59a818e80](https://github.com/heygen-com/hyperframes/commit/59a818e80a3ef561c5cb4a25ea30128cd18458b2), [f04cdb79c](https://github.com/heygen-com/hyperframes/commit/f04cdb79c5f1475fa7d6048036a4e0af354a60a2), [1f3fd2800](https://github.com/heygen-com/hyperframes/commit/1f3fd2800cd2e4df9e8a2c2b81ea11ed5b799630)).
- **The preview reload loop is gone.** Writes to `.transcode-cache` and the waveform cache touched files the project watcher was watching. So a preview reloaded in a loop, and shader transitions stuck partway through. Generated caches are now ignored by the watcher and excluded from project metadata ([7ea8250f5](https://github.com/heygen-com/hyperframes/commit/7ea8250f50b6d8824f21a3a21ef290c52c42d6fa), [bf739a4db](https://github.com/heygen-com/hyperframes/commit/bf739a4db2807373a0c294224dd4a101c6ed8954), [#2952](https://github.com/heygen-com/hyperframes/pull/2952), [1d01b9f2c](https://github.com/heygen-com/hyperframes/commit/1d01b9f2cfe44cb7899410e742a9d553c7807e19)).
- **Preview reliability and resolution.** Preview recovery is hardened and loading reliability is improved. Retained preview resources are released instead of accumulating. Chrome resolves on Windows. A selected render resolution is honored rather than silently replaced ([c62bd4c45](https://github.com/heygen-com/hyperframes/commit/c62bd4c454d7a53aac39e03b10da8eeb2a93d77e), [18b9acac1](https://github.com/heygen-com/hyperframes/commit/18b9acac12269899b0bfacb289d9bde3e1b065e4), [#2837](https://github.com/heygen-com/hyperframes/pull/2837), [fbfffb1aa](https://github.com/heygen-com/hyperframes/commit/fbfffb1aa785fe48aa94a3570bd04c9128a80d18), [#2924](https://github.com/heygen-com/hyperframes/pull/2924), [d8d626537](https://github.com/heygen-com/hyperframes/commit/d8d626537bf6a830711f38e24cc8756bc26f8d48), [#2878](https://github.com/heygen-com/hyperframes/pull/2878), [f1655b930](https://github.com/heygen-com/hyperframes/commit/f1655b9302dc870c1e2ee24f19fd23897c767b42), [#2876](https://github.com/heygen-com/hyperframes/pull/2876)).
- **Drilling into sub-compositions.** Every host row stays on the drill path, not just the top one. Children expand against their resolved parent host. Hidden state persists on expanded rows. Sub-compositions stay expanded at the end of the timeline. Sub-composition clip timing is computed correctly ([acad7b268](https://github.com/heygen-com/hyperframes/commit/acad7b268ea1e9e4b35e15265ac6c8a178e43865), [d48440a9b](https://github.com/heygen-com/hyperframes/commit/d48440a9bacb83aef8007b3667e78e02a2481d8f), [12546985f](https://github.com/heygen-com/hyperframes/commit/12546985f588df8cf984d241dd27e3417edb3320), [5dad52370](https://github.com/heygen-com/hyperframes/commit/5dad52370f77a9a0618827279b8a4dcd86868456), [3e342cff9](https://github.com/heygen-com/hyperframes/commit/3e342cff9c642258509df64e0a47da38502591bd), [3f0c20f63](https://github.com/heygen-com/hyperframes/commit/3f0c20f633e9ac064e45df838e03d4412ddafda8), [#2845](https://github.com/heygen-com/hyperframes/pull/2845)).
- **Timeline accessibility and pointer targets.** Track numbers announced to assistive technology are the real ones. `aria-controls` points at the lanes. Toolbar and lane controls meet the 24x24 pointer-target minimum. Ruler clicks seek to the pressed position. Escape cancels a retime. The scrub clamps to 0 instead of being dropped. Popovers and tooltips stop clipping at panel edges ([1faa0cbda](https://github.com/heygen-com/hyperframes/commit/1faa0cbdadc71abdea220d8705682c0a18e499bb), [#2848](https://github.com/heygen-com/hyperframes/pull/2848), [4e74eefdd](https://github.com/heygen-com/hyperframes/commit/4e74eefddd4c1e38f7c97662cc52fae6d2a01698), [69020699d](https://github.com/heygen-com/hyperframes/commit/69020699dfb595194c8bf85117525045439bcdb2), [#2843](https://github.com/heygen-com/hyperframes/pull/2843), [706f537f3](https://github.com/heygen-com/hyperframes/commit/706f537f33bcf182f6b294806384d377c1b1d747), [b386b55f7](https://github.com/heygen-com/hyperframes/commit/b386b55f735a0217751e26245fbf677c8ff5d303), [f81ac7457](https://github.com/heygen-com/hyperframes/commit/f81ac745726d3195d07b79d7de9c31357ce8ab43), [#2890](https://github.com/heygen-com/hyperframes/pull/2890)).
- **Colour field and shortcuts panel.** The hex field accepts 3-digit shorthand, is editable, and commits on an outside press. The shortcuts popup drops `aria-modal`, because it is not modal. It dismisses on Escape or an outside press ([86f633f98](https://github.com/heygen-com/hyperframes/commit/86f633f98573a79f88422c8a24a69bd55d718c3d), [55614033e](https://github.com/heygen-com/hyperframes/commit/55614033e501b5e55bfd3a2099396b74443652cd), [7f0cadcbb](https://github.com/heygen-com/hyperframes/commit/7f0cadcbb1f24df12bee091d5156e3cbf67f51e4), [aa2811642](https://github.com/heygen-com/hyperframes/commit/aa2811642fecabdef58ce45329b115993d1e43fb), [#2844](https://github.com/heygen-com/hyperframes/pull/2844)).
- **Audio and nested timing.** Hidden audio is excluded from the render mix. Plateaus survive sampled audio automation. Nested template video timing is offset correctly. Distributed video metadata is validated before a plan is built ([b7160f69b](https://github.com/heygen-com/hyperframes/commit/b7160f69bba87254f8d33853df21a8be862605e1), [#2870](https://github.com/heygen-com/hyperframes/pull/2870), [04e0ccce4](https://github.com/heygen-com/hyperframes/commit/04e0ccce429c0c7c579cd06db09e7bad8fb550f3), [#2863](https://github.com/heygen-com/hyperframes/pull/2863), [9bbb6d50a](https://github.com/heygen-com/hyperframes/commit/9bbb6d50a02e128fcdab9ae20d33649b65406788), [#2859](https://github.com/heygen-com/hyperframes/pull/2859), [557d82b6a](https://github.com/heygen-com/hyperframes/commit/557d82b6a9547e403598713e2840c41736d35544), [#2839](https://github.com/heygen-com/hyperframes/pull/2839)).
- **Fonts, downloads, and browsers.** Large local fonts stay file-backed instead of being inlined. Transient deterministic font fetches retry. Stalled download cleanup is awaited. A host-compatible cached browser is selected. The check navigation timeout is honored ([87791fd01](https://github.com/heygen-com/hyperframes/commit/87791fd01d93ee82226bf09d98a4dfe339c42307), [#2864](https://github.com/heygen-com/hyperframes/pull/2864), [20f4f8f49](https://github.com/heygen-com/hyperframes/commit/20f4f8f49c19181adfb4194f1965ee00845219f9), [#2865](https://github.com/heygen-com/hyperframes/pull/2865), [0bf33cb11](https://github.com/heygen-com/hyperframes/commit/0bf33cb117b111e89ee8d031d565c291aabf9160), [#2835](https://github.com/heygen-com/hyperframes/pull/2835), [85f0c9d35](https://github.com/heygen-com/hyperframes/commit/85f0c9d3546b1973dfba884fe6d2a7a8188d4ad3), [#2861](https://github.com/heygen-com/hyperframes/pull/2861), [fdc593289](https://github.com/heygen-com/hyperframes/commit/fdc5932897ea3ecf778d520da77e6f6d493017d5), [#2860](https://github.com/heygen-com/hyperframes/pull/2860)).
- **Website capture is bounded and honest.** Capture runtime stages run against a live budget that is validated and propagated. A blocked page is rejected outright, instead of passing a login wall through as a screenshot. Failure diagnostics are preserved. Skipped Lottie previews are omitted. Bounded vision failures are reported ([765a5ae83](https://github.com/heygen-com/hyperframes/commit/765a5ae83f31b01b65426f34a3c80a47330456a0), [49091e614](https://github.com/heygen-com/hyperframes/commit/49091e6142c98b9fcab967c88e210df601a118d9), [d3607606e](https://github.com/heygen-com/hyperframes/commit/d3607606eeced5c826923f536d0d33d0cd799fb9), [22e3cca96](https://github.com/heygen-com/hyperframes/commit/22e3cca966e4f3c5dfe0f8b716dec80259206300), [9ae000726](https://github.com/heygen-com/hyperframes/commit/9ae00072614f35785e4a1d7bad48787451462a91), [ac9458888](https://github.com/heygen-com/hyperframes/commit/ac9458888c7a6e05aa6b4d5d74063b5859782d02), [dfc60797a](https://github.com/heygen-com/hyperframes/commit/dfc60797a2adeddef120f4c800f06b30cc155117), [b38e90740](https://github.com/heygen-com/hyperframes/commit/b38e90740407ac50fd83e124b8211d3efc7282be), [#2933](https://github.com/heygen-com/hyperframes/pull/2933)).
- **Fewer false lint failures.** An asset `src` still holding an unresolved templating token is no longer reported as a missing asset. The placeholder skip is consolidated into one shared predicate ([6c185f252](https://github.com/heygen-com/hyperframes/commit/6c185f252f38a5a1f1dc9e23f17ef98f63ea18c7), [#2893](https://github.com/heygen-com/hyperframes/pull/2893), [5a6e4b1a8](https://github.com/heygen-com/hyperframes/commit/5a6e4b1a8f4176250b5c92b63845399fcfe7f256), [#2894](https://github.com/heygen-com/hyperframes/pull/2894)).
- **Registry polish.** `mk` card offsets animate with transforms instead of `top` and `left`. Connector geometry and family accents are corrected. Demo legibility is improved. The device timeline registers synchronously ([ce7d75dba](https://github.com/heygen-com/hyperframes/commit/ce7d75dbaa29e3920216f6e848e68b4b6257602e), [#2960](https://github.com/heygen-com/hyperframes/pull/2960), [35231e964](https://github.com/heygen-com/hyperframes/commit/35231e964daab6d6b73d8dc3917f9f95f5944157), [2bb620517](https://github.com/heygen-com/hyperframes/commit/2bb620517749eefaa19e5fefbb2a85b9d6f538b8), [#2546](https://github.com/heygen-com/hyperframes/pull/2546)).
## Under the hood
- **Render routing telemetry.** Live DOM element count is now measured at capture-session init on every render rather than only on the 17% that open a probe session, so the fleet element-count distribution is finally observable. This is deliberately observational and does not feed the routing gate ([d74afc7b7](https://github.com/heygen-com/hyperframes/commit/d74afc7b7dc620036439b398c6bca823fb3911b4), [#2891](https://github.com/heygen-com/hyperframes/pull/2891)).
- **Groundwork for faster short compositions.** A controlled sweep found single-worker drawElement beating parallel screenshot by 1.16x to 1.24x on compositions between 250 and 899 frames, which is where the median fleet render sits. A 2x2 then showed motion and DOM size pulling in opposite directions, so the band is gated on an element ceiling rather than a bare floor drop, which would have handed large compositions a 1.8x regression. This release computes and emits that band decision on every render but changes no routing: it stays behind `HF_DE_SHORT_BAND_ROUTE`, so the follow-up flip is measurable against a real baseline rather than a guess ([0749cd9ff](https://github.com/heygen-com/hyperframes/commit/0749cd9ff83ad41c72c7dc6be1b224e9bc27731b), [e9de2fa14](https://github.com/heygen-com/hyperframes/commit/e9de2fa14f61f503287c9d87ec0487c165ecbcba), [def98f79c](https://github.com/heygen-com/hyperframes/commit/def98f79c3fa746e805c6fbe9ee51392c267ea1e), [4dbf0d90b](https://github.com/heygen-com/hyperframes/commit/4dbf0d90b05de2c3db7c983641c8785fd9988148), [23854f7c6](https://github.com/heygen-com/hyperframes/commit/23854f7c6a328ef1f6913160af345c9fda70b036), [#2875](https://github.com/heygen-com/hyperframes/pull/2875)).
- **Parallel drawElement router.** The router's minimum-frames floor drops from 2000 to 700 after a crossover sweep found three workers beating one at every size tested. The router itself stays off by default behind `HF_DE_PARALLEL_ROUTER`, so this tunes what it will do rather than changing today's renders. Render events also gain battery and low-power state on macOS, because the same composition was measured flipping between two power-management regimes with no signal to segment by ([3da31e399](https://github.com/heygen-com/hyperframes/commit/3da31e399b498d0ab6b1ce32b008b8b10cdcd2dc), [#2838](https://github.com/heygen-com/hyperframes/pull/2838)).
- **Render routing telemetry.** Live DOM element count is now measured at capture-session init on every render. Before, it was measured only on the 17% that open a probe session. So the fleet element-count distribution is finally observable. This is deliberately observational, and it does not feed the routing gate ([d74afc7b7](https://github.com/heygen-com/hyperframes/commit/d74afc7b7dc620036439b398c6bca823fb3911b4), [#2891](https://github.com/heygen-com/hyperframes/pull/2891)).
- **Groundwork for faster short compositions.** A controlled sweep found single-worker drawElement beating parallel screenshot by 1.16x to 1.24x. That was on compositions between 250 and 899 frames, which is where the median fleet render sits. A 2x2 then showed motion and DOM size pulling in opposite directions. So the band is gated on an element ceiling, not a bare floor drop. A bare floor drop would have handed large compositions a 1.8x regression. This release computes and emits that band decision on every render, but it changes no routing. It stays behind `HF_DE_SHORT_BAND_ROUTE`, so the follow-up flip is measurable against a real baseline rather than a guess ([0749cd9ff](https://github.com/heygen-com/hyperframes/commit/0749cd9ff83ad41c72c7dc6be1b224e9bc27731b), [e9de2fa14](https://github.com/heygen-com/hyperframes/commit/e9de2fa14f61f503287c9d87ec0487c165ecbcba), [def98f79c](https://github.com/heygen-com/hyperframes/commit/def98f79c3fa746e805c6fbe9ee51392c267ea1e), [4dbf0d90b](https://github.com/heygen-com/hyperframes/commit/4dbf0d90b05de2c3db7c983641c8785fd9988148), [23854f7c6](https://github.com/heygen-com/hyperframes/commit/23854f7c6a328ef1f6913160af345c9fda70b036), [#2875](https://github.com/heygen-com/hyperframes/pull/2875)).
- **Parallel drawElement router.** The router's minimum-frames floor drops from 2000 to 700. A crossover sweep found three workers beating one at every size tested. The router itself stays off by default behind `HF_DE_PARALLEL_ROUTER`. So this tunes what it will do rather than changing today's renders. Render events also gain battery and low-power state on macOS. The same composition was measured flipping between two power-management regimes with no signal to segment by ([3da31e399](https://github.com/heygen-com/hyperframes/commit/3da31e399b498d0ab6b1ce32b008b8b10cdcd2dc), [#2838](https://github.com/heygen-com/hyperframes/pull/2838)).
## Docs
- **Colour grading and film effects.** The prompting guide gains a colour grading and film effects chapter, agent guidance links to it, and the `--hf-color-grading-intensity` claim is corrected and made order-agnostic. Professional grading and media treatments are documented, with the treatment contracts spelled out ([819ed632d](https://github.com/heygen-com/hyperframes/commit/819ed632d92229297c833145546effc0ddb6fb26), [#2911](https://github.com/heygen-com/hyperframes/pull/2911), [2517f1773](https://github.com/heygen-com/hyperframes/commit/2517f1773fa6afcc9b54b651f93409d7e99e9e3f), [dd9c86d07](https://github.com/heygen-com/hyperframes/commit/dd9c86d07a12307ef0c15eff88b21b2df73fc8cd), [0c7c9bbdb](https://github.com/heygen-com/hyperframes/commit/0c7c9bbdb0b10acd370865cbdda540ac0ce3f408), [e4eac0c21](https://github.com/heygen-com/hyperframes/commit/e4eac0c215faec922a866e61f4e37d322442bd47), [1a2c30a79](https://github.com/heygen-com/hyperframes/commit/1a2c30a7994fc488685e9251a41e40eea2c3bcb2), [12452a115](https://github.com/heygen-com/hyperframes/commit/12452a11569812003a067073c0497d69279b620d), [182862773](https://github.com/heygen-com/hyperframes/commit/182862773187dc01f08b2c29d2a3a5951afe5c5e), [#2826](https://github.com/heygen-com/hyperframes/pull/2826)).
- **The motion chapter, rebuilt.** Each rule now carries before-and-after prompts and an exaggerated applied side, the chapter leads with meaning rather than amplitude, and it adds the motion-purpose filter, offset ratio, property coherence, the worker-boundary determinism caveat, and an "Avoiding the slideshow" continuity contract. Demos are rebuilt around motivated subjects and embedded as validated renders ([6f29f1454](https://github.com/heygen-com/hyperframes/commit/6f29f1454e4d84a94c36574188d92cc8dc3b953c), [cebf66e6e](https://github.com/heygen-com/hyperframes/commit/cebf66e6e8ca6251c8e83c88d1009a00024ffc63), [1f4d00e15](https://github.com/heygen-com/hyperframes/commit/1f4d00e15e627aa2e6b130fe4f78401980cd37c7), [d4bd2917b](https://github.com/heygen-com/hyperframes/commit/d4bd2917b5e279e43be8ac7cb811fc863aebc49b), [4f6fb8fc1](https://github.com/heygen-com/hyperframes/commit/4f6fb8fc16c566bd8f473820a559a16c15b1fbeb), [cdac57c07](https://github.com/heygen-com/hyperframes/commit/cdac57c0757cee22db67e9595b29809cfabd056c), [dd4fd9cef](https://github.com/heygen-com/hyperframes/commit/dd4fd9cefaa1f5816a8a2ff404e2152af542b25d), [#2109](https://github.com/heygen-com/hyperframes/pull/2109)).
- **The intent interview.** The guide documents the intent interview and aligns its pages with the skill contracts, so an agent's first move is a question rather than a guess ([2efbfd475](https://github.com/heygen-com/hyperframes/commit/2efbfd47583f75534b194d90ec0f689278b370f5), [#2872](https://github.com/heygen-com/hyperframes/pull/2872)).
- **Contributing and setup.** The catalog contribution guide is refreshed, and the Studio monorepo dev server port is documented in both the setup guide and the Studio docs ([67ffafb11](https://github.com/heygen-com/hyperframes/commit/67ffafb11c18cd599b856c411c614cf329377d22), [#2954](https://github.com/heygen-com/hyperframes/pull/2954), [a52dd9c30](https://github.com/heygen-com/hyperframes/commit/a52dd9c3087c63a5c2c7f38d480961794d8c5eae), [#2902](https://github.com/heygen-com/hyperframes/pull/2902), [ffe5e12cf](https://github.com/heygen-com/hyperframes/commit/ffe5e12cf8f404b86e019d28ca25dc75de903d57), [#2901](https://github.com/heygen-com/hyperframes/pull/2901)).
- **Guides and adopters.** THU-MAIC is added as a HyperFrames adopter. The Send-to guide is unlisted from the web nav because it is served through the tool rather than found by search, and it now states plainly that enhance turns are free while render is the paid step ([1738b5a11](https://github.com/heygen-com/hyperframes/commit/1738b5a11f7eee1c130b29b3c489f79473e7aec2), [#2761](https://github.com/heygen-com/hyperframes/pull/2761), [a3c8f897f](https://github.com/heygen-com/hyperframes/commit/a3c8f897f2cdb4231521a752d6b320c8edb77a45), [#2918](https://github.com/heygen-com/hyperframes/pull/2918), [e3636db07](https://github.com/heygen-com/hyperframes/commit/e3636db07e7e242cd150e5a44c338658d84b13df), [#2827](https://github.com/heygen-com/hyperframes/pull/2827)).
- **Product launch video.** The guidance uses real screenshots for site showcases, catches motion jumps at frame cuts, and avoids weak music openings in short launch videos. Defects the end-to-end run exposed in capture, audio, and docs are fixed ([860954d71](https://github.com/heygen-com/hyperframes/commit/860954d71c15dcd7d47ce9d8bda55009010f08f7), [#2881](https://github.com/heygen-com/hyperframes/pull/2881), [5466bcecc](https://github.com/heygen-com/hyperframes/commit/5466bceccef70a3663f5a92bc567455fd018f050), [#2880](https://github.com/heygen-com/hyperframes/pull/2880), [30900c346](https://github.com/heygen-com/hyperframes/commit/30900c3465f457bf65f07ed4b4b55c384286bd20), [#2882](https://github.com/heygen-com/hyperframes/pull/2882), [e0dc255e8](https://github.com/heygen-com/hyperframes/commit/e0dc255e8a4108833ac727d54f4b039cca397042), [#2892](https://github.com/heygen-com/hyperframes/pull/2892)).
- **Skills.** Blocked website captures are gated in the skills docs, media-treatment policy targets are named instead of alluded to, and transition roots without an explicit duration are extended rather than dropped ([30f383074](https://github.com/heygen-com/hyperframes/commit/30f38307411908237201ed227f5350aa18ba156e), [6cab53a68](https://github.com/heygen-com/hyperframes/commit/6cab53a681bf159ffe34f66506a8004e38213fae), [#2879](https://github.com/heygen-com/hyperframes/pull/2879), [14ced9051](https://github.com/heygen-com/hyperframes/commit/14ced9051795b32db75a479ac2580eef3c5df85c), [#2873](https://github.com/heygen-com/hyperframes/pull/2873)).
- **Colour grading and film effects.** The prompting guide gains a colour grading and film effects chapter. Agent guidance links to it. The `--hf-color-grading-intensity` claim is corrected and made order-agnostic. Professional grading and media treatments are documented, with the treatment contracts spelled out ([819ed632d](https://github.com/heygen-com/hyperframes/commit/819ed632d92229297c833145546effc0ddb6fb26), [#2911](https://github.com/heygen-com/hyperframes/pull/2911), [2517f1773](https://github.com/heygen-com/hyperframes/commit/2517f1773fa6afcc9b54b651f93409d7e99e9e3f), [dd9c86d07](https://github.com/heygen-com/hyperframes/commit/dd9c86d07a12307ef0c15eff88b21b2df73fc8cd), [0c7c9bbdb](https://github.com/heygen-com/hyperframes/commit/0c7c9bbdb0b10acd370865cbdda540ac0ce3f408), [e4eac0c21](https://github.com/heygen-com/hyperframes/commit/e4eac0c215faec922a866e61f4e37d322442bd47), [1a2c30a79](https://github.com/heygen-com/hyperframes/commit/1a2c30a7994fc488685e9251a41e40eea2c3bcb2), [12452a115](https://github.com/heygen-com/hyperframes/commit/12452a11569812003a067073c0497d69279b620d), [182862773](https://github.com/heygen-com/hyperframes/commit/182862773187dc01f08b2c29d2a3a5951afe5c5e), [#2826](https://github.com/heygen-com/hyperframes/pull/2826)).
- **The motion chapter, rebuilt.** Each rule now carries before-and-after prompts and an exaggerated applied side. The chapter leads with meaning rather than amplitude. It adds the motion-purpose filter, offset ratio, and property coherence. It also adds the worker-boundary determinism caveat and an "Avoiding the slideshow" continuity contract. Demos are rebuilt around motivated subjects and embedded as validated renders ([6f29f1454](https://github.com/heygen-com/hyperframes/commit/6f29f1454e4d84a94c36574188d92cc8dc3b953c), [cebf66e6e](https://github.com/heygen-com/hyperframes/commit/cebf66e6e8ca6251c8e83c88d1009a00024ffc63), [1f4d00e15](https://github.com/heygen-com/hyperframes/commit/1f4d00e15e627aa2e6b130fe4f78401980cd37c7), [d4bd2917b](https://github.com/heygen-com/hyperframes/commit/d4bd2917b5e279e43be8ac7cb811fc863aebc49b), [4f6fb8fc1](https://github.com/heygen-com/hyperframes/commit/4f6fb8fc16c566bd8f473820a559a16c15b1fbeb), [cdac57c07](https://github.com/heygen-com/hyperframes/commit/cdac57c0757cee22db67e9595b29809cfabd056c), [dd4fd9cef](https://github.com/heygen-com/hyperframes/commit/dd4fd9cefaa1f5816a8a2ff404e2152af542b25d), [#2109](https://github.com/heygen-com/hyperframes/pull/2109)).
- **The intent interview.** The guide documents the intent interview. Its pages line up with the skill contracts. An agent's first move is now a question rather than a guess ([2efbfd475](https://github.com/heygen-com/hyperframes/commit/2efbfd47583f75534b194d90ec0f689278b370f5), [#2872](https://github.com/heygen-com/hyperframes/pull/2872)).
- **Contributing and setup.** The catalog contribution guide is refreshed. The Studio monorepo dev server port is now documented. It appears in both the setup guide and the Studio docs ([67ffafb11](https://github.com/heygen-com/hyperframes/commit/67ffafb11c18cd599b856c411c614cf329377d22), [#2954](https://github.com/heygen-com/hyperframes/pull/2954), [a52dd9c30](https://github.com/heygen-com/hyperframes/commit/a52dd9c3087c63a5c2c7f38d480961794d8c5eae), [#2902](https://github.com/heygen-com/hyperframes/pull/2902), [ffe5e12cf](https://github.com/heygen-com/hyperframes/commit/ffe5e12cf8f404b86e019d28ca25dc75de903d57), [#2901](https://github.com/heygen-com/hyperframes/pull/2901)).
- **Guides and adopters.** THU-MAIC is added as a HyperFrames adopter. The Send-to guide is unlisted from the web nav. It is served through the tool rather than found by search. It also states plainly that enhance turns are free, and that render is the paid step ([1738b5a11](https://github.com/heygen-com/hyperframes/commit/1738b5a11f7eee1c130b29b3c489f79473e7aec2), [#2761](https://github.com/heygen-com/hyperframes/pull/2761), [a3c8f897f](https://github.com/heygen-com/hyperframes/commit/a3c8f897f2cdb4231521a752d6b320c8edb77a45), [#2918](https://github.com/heygen-com/hyperframes/pull/2918), [e3636db07](https://github.com/heygen-com/hyperframes/commit/e3636db07e7e242cd150e5a44c338658d84b13df), [#2827](https://github.com/heygen-com/hyperframes/pull/2827)).
- **Product launch video.** The guidance uses real screenshots for site showcases. It catches motion jumps at frame cuts. It avoids weak music openings in short launch videos. The end-to-end run exposed defects in capture, audio, and docs. Those are fixed ([860954d71](https://github.com/heygen-com/hyperframes/commit/860954d71c15dcd7d47ce9d8bda55009010f08f7), [#2881](https://github.com/heygen-com/hyperframes/pull/2881), [5466bcecc](https://github.com/heygen-com/hyperframes/commit/5466bceccef70a3663f5a92bc567455fd018f050), [#2880](https://github.com/heygen-com/hyperframes/pull/2880), [30900c346](https://github.com/heygen-com/hyperframes/commit/30900c3465f457bf65f07ed4b4b55c384286bd20), [#2882](https://github.com/heygen-com/hyperframes/pull/2882), [e0dc255e8](https://github.com/heygen-com/hyperframes/commit/e0dc255e8a4108833ac727d54f4b039cca397042), [#2892](https://github.com/heygen-com/hyperframes/pull/2892)).
- **Skills.** Blocked website captures are gated in the skills docs. Media-treatment policy targets are named instead of alluded to. Transition roots without an explicit duration are extended rather than dropped ([30f383074](https://github.com/heygen-com/hyperframes/commit/30f38307411908237201ed227f5350aa18ba156e), [6cab53a68](https://github.com/heygen-com/hyperframes/commit/6cab53a681bf159ffe34f66506a8004e38213fae), [#2879](https://github.com/heygen-com/hyperframes/pull/2879), [14ced9051](https://github.com/heygen-com/hyperframes/commit/14ced9051795b32db75a479ac2580eef3c5df85c), [#2873](https://github.com/heygen-com/hyperframes/pull/2873)).
For exact versioned release notes, see the [Changelog](/changelog).
</Update>
@@ -83,48 +83,48 @@ For exact versioned release notes, see the [Changelog](/changelog).
<Frame>
<DocsVideo
title="HyperFrames video: Weekly Changelog 2026 07 20 2026 07 27"
src="https://static.heygen.ai/hyperframes/changelog-videos/weekly-changelog-2026-07-20-2026-07-27-v2.mp4"
src="https://static.heygen.ai/hyperframes/changelog-videos/weekly-changelog-2026-07-20-2026-07-27.mp4"
/>
</Frame>
Professional color grading is the headline. Master and per-channel curves, hue curves, three-way wheels, and HSL secondaries land in core, Studio, and the CLI, so you can shape shadows, midtones, and highlights on any image or video without leaving the composition. The distributed plan gains an explicit protocol version and can publish artifacts straight to S3 and GCS. Studio adds a keyframe ease editor, registry caption components become transcript-driven, and a long run of audio, download, and capture reliability fixes lands underneath.
Professional color grading is the headline. Master and per-channel curves, hue curves, three-way wheels, and HSL secondaries land in core, Studio, and the CLI. You can now shape shadows, midtones, and highlights on any image or video without leaving the composition. The distributed plan gains an explicit protocol version. It can also publish artifacts straight to S3 and GCS. Studio adds a keyframe ease editor. Registry caption components become transcript-driven. A long run of audio, download, and capture reliability fixes lands underneath.
## Features
- **Professional color grading.** Core adds master and per-channel RGB tone curves, hue-vs-hue, hue-vs-saturation and hue-vs-luma curves, three-way shadows, midtones and highlights wheels, and HSL secondary qualifiers. Everything normalizes through one shared contract, so a grade you author resolves the same way in preview and in render ([f99fc4e56](https://github.com/heygen-com/hyperframes/commit/f99fc4e5686239f5ef56d4eb6083bee796ceeddc)).
- **Grading controls in Studio.** The inspector exposes the new curve and wheel controls with live previews, so you can grade a clip on the canvas instead of hand-writing a `data-color-grading` payload ([20ef48abc](https://github.com/heygen-com/hyperframes/commit/20ef48abcb190e3e9ef132d1b1dd711201d3c58b)).
- **Agent-native grading from the CLI.** `hyperframes media-treatment` applies, previews, and clears a grading payload by selector, with `--dry-run` before writing and `--clear` to remove. The media-use skill now reads the source and picks a treatment instead of guessing a preset name ([6d5961b80](https://github.com/heygen-com/hyperframes/commit/6d5961b8024fe70f87879f271dd91149717d41a8), [4582881d0](https://github.com/heygen-com/hyperframes/commit/4582881d002c361afff59cc1c74ae072a50e17f7)).
- **Media treatments.** A media treatment is a source-aware plan that composes existing color, effect, timeline, and registry primitives rather than adding a second runtime schema. Core defines the capability catalog, the runtime renders treatments deterministically, Studio ships an inspector for them, and the registry adds matching overlays ([70213c5a8](https://github.com/heygen-com/hyperframes/commit/70213c5a8526b12c8b26f01b8288d78bb9edc917), [944640c32](https://github.com/heygen-com/hyperframes/commit/944640c3283d604383fd437fcca8c63941d8e3d7), [39c2341c4](https://github.com/heygen-com/hyperframes/commit/39c2341c4d1edf52a00fb8db4a49553c739860cc), [b0d3164dd](https://github.com/heygen-com/hyperframes/commit/b0d3164ddb6177c6b31634f02a908fdae607850f)).
- **Keyframe ease editor.** Studio gets an ease curve editor with a preset library, editable ease parameters, and an SVG curve preview. A deterministic ease runtime backs it, so an authored ease replays identically at render time ([c253dec23](https://github.com/heygen-com/hyperframes/commit/c253dec23b0a6f1ccfe49c17371fdc9823f73b4c), [5acbf240c](https://github.com/heygen-com/hyperframes/commit/5acbf240cbab38594d06b59b9ef7f2a4b37d31b7)).
- **Versioned distributed plan protocol.** Distributed plans now carry an explicit protocol descriptor. Plans written before the descriptor still load as v1, while a partial, malformed, or unknown descriptor fails closed before any layout-specific read. Plan hashes and rendered pixels are unchanged ([f9f00b0ef](https://github.com/heygen-com/hyperframes/commit/f9f00b0efc2d1006967d5b2e0009ea3c3f6ed2e6), [#2777](https://github.com/heygen-com/hyperframes/pull/2777)).
- **Plan v2 publishing straight to object storage.** A storage-neutral, manifest-last publisher lets cloud adapters write plan artifacts directly to S3 and GCS instead of a shared filesystem, and AWS Lambda now reads the v2 protocol. This is the groundwork for lifting the plan size limit on very large distributed renders ([09998789b](https://github.com/heygen-com/hyperframes/commit/09998789b5ff012adcd97e9fb33537e473f1cc52), [74d7bfde4](https://github.com/heygen-com/hyperframes/commit/74d7bfde4870bbc1c6c4471cfc004964807df3e7), [5bf61d6df](https://github.com/heygen-com/hyperframes/commit/5bf61d6df0694c3077ecc7e84cd9d72f2029a5e6), [#2789](https://github.com/heygen-com/hyperframes/pull/2789), [07f9a3de9](https://github.com/heygen-com/hyperframes/commit/07f9a3de954d663e61d0e7da233fe8d33a06a5f9), [#2792](https://github.com/heygen-com/hyperframes/pull/2792)).
- **Data-driven caption components.** caption-highlight, caption-weight-shift, caption-pill-karaoke, caption-emoji-pop, and caption-editorial-emphasis now build themselves from a shared caption-data runtime, with automatic grouping, an emphasis heuristic, and a generic emoji lexicon. A transcript drives the animation instead of hand-authored per-word markup ([7d4e71d10](https://github.com/heygen-com/hyperframes/commit/7d4e71d10b617b858fde2e764e15d530e9ffdf19), [c67e9dc1f](https://github.com/heygen-com/hyperframes/commit/c67e9dc1f843122225468ce63a67c57f70c2bba2), [08620b75d](https://github.com/heygen-com/hyperframes/commit/08620b75df5275739fd9b0b8f480527fdc1d9a98), [392a9d251](https://github.com/heygen-com/hyperframes/commit/392a9d251a4feadbf7703ba4f9157c1857c7a59b), [ed8973952](https://github.com/heygen-com/hyperframes/commit/ed8973952dda2aa43931e0ffa05b3fc6ba0cb1cb)).
- **New layout lint checks.** Lint adds `rotation_pivot_drift` and `off_pivot_rotation` for hub-referenced rotation, and re-samples dense motion for `content_overlap` so fast collisions between samples are no longer missed ([222aec45a](https://github.com/heygen-com/hyperframes/commit/222aec45ab0553c014dddccd563e273affd9b71a), [#2741](https://github.com/heygen-com/hyperframes/pull/2741), [e710a1686](https://github.com/heygen-com/hyperframes/commit/e710a1686f2442b460ea4973dca0be97c81ef184), [#2744](https://github.com/heygen-com/hyperframes/pull/2744), [72e2f08f1](https://github.com/heygen-com/hyperframes/commit/72e2f08f15ceec105eb2bca6e9e35b8020e040be), [#2746](https://github.com/heygen-com/hyperframes/pull/2746)).
- **Live map capture warning.** The engine detects a live map viewport at capture init, names the map library it found, and points at the basemap-baking path, so streaming tiles do not silently produce a nondeterministic render ([30ca51c61](https://github.com/heygen-com/hyperframes/commit/30ca51c615fce20f5266278cdf5f4c97fd691c88)).
- **Leaner, larger skills corpus.** The composition skills gain seven blueprints and ten rules from a mining pass, while the router now routes once and dispatches packet-scoped workers so each run costs less context ([853256403](https://github.com/heygen-com/hyperframes/commit/853256403b3ffd3dc0b616785ab876b4c0f04a89), [#2680](https://github.com/heygen-com/hyperframes/pull/2680), [6ad738b58](https://github.com/heygen-com/hyperframes/commit/6ad738b580adf157393fde02351af64669c9fbc5), [#2618](https://github.com/heygen-com/hyperframes/pull/2618)).
- **Professional color grading.** Core adds master and per-channel RGB tone curves. It adds hue-vs-hue, hue-vs-saturation, and hue-vs-luma curves. It adds three-way shadows, midtones, and highlights wheels, plus HSL secondary qualifiers. Everything normalizes through one shared contract. A grade you author resolves the same way in preview and in render ([f99fc4e56](https://github.com/heygen-com/hyperframes/commit/f99fc4e5686239f5ef56d4eb6083bee796ceeddc)).
- **Grading controls in Studio.** The inspector exposes the new curve and wheel controls, with live previews. You can grade a clip on the canvas instead of hand-writing a `data-color-grading` payload ([20ef48abc](https://github.com/heygen-com/hyperframes/commit/20ef48abcb190e3e9ef132d1b1dd711201d3c58b)).
- **Agent-native grading from the CLI.** `hyperframes media-treatment` applies, previews, and clears a grading payload by selector. Use `--dry-run` before writing and `--clear` to remove. The media-use skill now reads the source and picks a treatment, instead of guessing a preset name ([6d5961b80](https://github.com/heygen-com/hyperframes/commit/6d5961b8024fe70f87879f271dd91149717d41a8), [4582881d0](https://github.com/heygen-com/hyperframes/commit/4582881d002c361afff59cc1c74ae072a50e17f7)).
- **Media treatments.** A media treatment is a source-aware plan. It composes existing color, effect, timeline, and registry primitives, rather than adding a second runtime schema. Core defines the capability catalog. The runtime renders treatments deterministically. Studio ships an inspector for them. The registry adds matching overlays ([70213c5a8](https://github.com/heygen-com/hyperframes/commit/70213c5a8526b12c8b26f01b8288d78bb9edc917), [944640c32](https://github.com/heygen-com/hyperframes/commit/944640c3283d604383fd437fcca8c63941d8e3d7), [39c2341c4](https://github.com/heygen-com/hyperframes/commit/39c2341c4d1edf52a00fb8db4a49553c739860cc), [b0d3164dd](https://github.com/heygen-com/hyperframes/commit/b0d3164ddb6177c6b31634f02a908fdae607850f)).
- **Keyframe ease editor.** Studio gets an ease curve editor. It has a preset library, editable ease parameters, and an SVG curve preview. A deterministic ease runtime backs it. An authored ease replays identically at render time ([c253dec23](https://github.com/heygen-com/hyperframes/commit/c253dec23b0a6f1ccfe49c17371fdc9823f73b4c), [5acbf240c](https://github.com/heygen-com/hyperframes/commit/5acbf240cbab38594d06b59b9ef7f2a4b37d31b7)).
- **Versioned distributed plan protocol.** Distributed plans now carry an explicit protocol descriptor. Plans written before the descriptor still load as v1. A partial, malformed, or unknown descriptor fails closed, before any layout-specific read. Plan hashes and rendered pixels are unchanged ([f9f00b0ef](https://github.com/heygen-com/hyperframes/commit/f9f00b0efc2d1006967d5b2e0009ea3c3f6ed2e6), [#2777](https://github.com/heygen-com/hyperframes/pull/2777)).
- **Plan v2 publishing straight to object storage.** A storage-neutral, manifest-last publisher lets cloud adapters write plan artifacts directly to S3 and GCS. No shared filesystem is needed. AWS Lambda now reads the v2 protocol. This is the groundwork for lifting the plan size limit on very large distributed renders ([09998789b](https://github.com/heygen-com/hyperframes/commit/09998789b5ff012adcd97e9fb33537e473f1cc52), [74d7bfde4](https://github.com/heygen-com/hyperframes/commit/74d7bfde4870bbc1c6c4471cfc004964807df3e7), [5bf61d6df](https://github.com/heygen-com/hyperframes/commit/5bf61d6df0694c3077ecc7e84cd9d72f2029a5e6), [#2789](https://github.com/heygen-com/hyperframes/pull/2789), [07f9a3de9](https://github.com/heygen-com/hyperframes/commit/07f9a3de954d663e61d0e7da233fe8d33a06a5f9), [#2792](https://github.com/heygen-com/hyperframes/pull/2792)).
- **Data-driven caption components.** caption-highlight, caption-weight-shift, caption-pill-karaoke, caption-emoji-pop, and caption-editorial-emphasis now build themselves from a shared caption-data runtime. That runtime brings automatic grouping, an emphasis heuristic, and a generic emoji lexicon. A transcript drives the animation, instead of hand-authored per-word markup ([7d4e71d10](https://github.com/heygen-com/hyperframes/commit/7d4e71d10b617b858fde2e764e15d530e9ffdf19), [c67e9dc1f](https://github.com/heygen-com/hyperframes/commit/c67e9dc1f843122225468ce63a67c57f70c2bba2), [08620b75d](https://github.com/heygen-com/hyperframes/commit/08620b75df5275739fd9b0b8f480527fdc1d9a98), [392a9d251](https://github.com/heygen-com/hyperframes/commit/392a9d251a4feadbf7703ba4f9157c1857c7a59b), [ed8973952](https://github.com/heygen-com/hyperframes/commit/ed8973952dda2aa43931e0ffa05b3fc6ba0cb1cb)).
- **New layout lint checks.** Lint adds `rotation_pivot_drift` and `off_pivot_rotation` for hub-referenced rotation. It also re-samples dense motion for `content_overlap`, so fast collisions between samples are no longer missed ([222aec45a](https://github.com/heygen-com/hyperframes/commit/222aec45ab0553c014dddccd563e273affd9b71a), [#2741](https://github.com/heygen-com/hyperframes/pull/2741), [e710a1686](https://github.com/heygen-com/hyperframes/commit/e710a1686f2442b460ea4973dca0be97c81ef184), [#2744](https://github.com/heygen-com/hyperframes/pull/2744), [72e2f08f1](https://github.com/heygen-com/hyperframes/commit/72e2f08f15ceec105eb2bca6e9e35b8020e040be), [#2746](https://github.com/heygen-com/hyperframes/pull/2746)).
- **Live map capture warning.** The engine detects a live map viewport at capture init. It names the map library it found and points at the basemap-baking path. Streaming tiles no longer silently produce a nondeterministic render ([30ca51c61](https://github.com/heygen-com/hyperframes/commit/30ca51c615fce20f5266278cdf5f4c97fd691c88)).
- **Leaner, larger skills corpus.** The composition skills gain seven blueprints and ten rules from a mining pass. The router now routes once and dispatches packet-scoped workers. Each run costs less context ([853256403](https://github.com/heygen-com/hyperframes/commit/853256403b3ffd3dc0b616785ab876b4c0f04a89), [#2680](https://github.com/heygen-com/hyperframes/pull/2680), [6ad738b58](https://github.com/heygen-com/hyperframes/commit/6ad738b580adf157393fde02351af64669c9fbc5), [#2618](https://github.com/heygen-com/hyperframes/pull/2618)).
## Fixes
- **Audio no longer runs past the picture.** Padded audio is normalized on a sample timeline, the mux stops at the shortest normalized stream, the final mux is capped to video duration, AAC packet padding is trimmed exactly, and M4A edit timing and priming survive normalization ([4b116b988](https://github.com/heygen-com/hyperframes/commit/4b116b98802c05f0eeb541878806692eea975541), [532461599](https://github.com/heygen-com/hyperframes/commit/532461599b7518f4609002cdb314ff2ce9f3a70f), [63bc525ca](https://github.com/heygen-com/hyperframes/commit/63bc525ca90b08356d50ebb41d4e15581c9d3470), [19258ea5b](https://github.com/heygen-com/hyperframes/commit/19258ea5ba47067e9d5756b122051c467ece0b04), [afc4e96bb](https://github.com/heygen-com/hyperframes/commit/afc4e96bbed21dbcd29544a6be95d35db7dd4eed), [9289551e9](https://github.com/heygen-com/hyperframes/commit/9289551e98958f006ec14af8ae9058096a4932fe), [59c56d325](https://github.com/heygen-com/hyperframes/commit/59c56d325723bbcb7c54315023aedeb72b376cd8), [113a4985b](https://github.com/heygen-com/hyperframes/commit/113a4985b51bc1d77babe7113d6c96bbe46d41bf)).
- **Portable audio padding.** The audio filter chain drops an FFmpeg option the bundled Windows build rejects, so compositions that rendered video but failed to mux audio now complete. Typed audio failure causes are preserved through the engine, the in-process producer, and distributed planning ([3b9552ef9](https://github.com/heygen-com/hyperframes/commit/3b9552ef9db36b133485e4ce805892346e0b9006), [37b88688e](https://github.com/heygen-com/hyperframes/commit/37b88688e7ae854e377c47fe3d9560ab0c930161), [#2769](https://github.com/heygen-com/hyperframes/pull/2769)).
- **Probe failures say why.** ffprobe keeps a bounded stderr tail instead of running quiet, so a failed probe reports the actual error rather than a blank diagnostic ([8c5077068](https://github.com/heygen-com/hyperframes/commit/8c50770684bc87ca67592c45d4101e3c029193d2), [#2772](https://github.com/heygen-com/hyperframes/pull/2772)).
- **Hardened media downloads.** Video downloads are atomic and retry transient failures, reserved and future-use IPv4 ranges are blocked, downloader trust-boundary gaps are closed, and network error shapes are narrowed honestly ([c01f6b446](https://github.com/heygen-com/hyperframes/commit/c01f6b446829f15c2c18dfd43da7013b412b2bf7), [2e84faeb2](https://github.com/heygen-com/hyperframes/commit/2e84faeb28ff8689da212a5b7db805f3b454d4d9), [4b81f7858](https://github.com/heygen-com/hyperframes/commit/4b81f785868362fbb5c4bd7f1c5be24b2ccb7f94), [5ce2eb879](https://github.com/heygen-com/hyperframes/commit/5ce2eb879db1bb2b3dd740bcd7e9abf8adaa3230)).
- **Distributed render reliability.** Sparse video directories are materialized, distributed capture falls back safely, plan scratch state resets between runs, oversized plans are attributed and stopped early, partial color metadata is accepted in plan v2, and Cloud Run enforces effective BeginFrame capture ([ddb59d356](https://github.com/heygen-com/hyperframes/commit/ddb59d3567fc70d808f86ef8b5b531078e3df2d6), [96cafb47c](https://github.com/heygen-com/hyperframes/commit/96cafb47c6c9850939c85e2cc76e578d3b6dbd1b), [ebb02cafe](https://github.com/heygen-com/hyperframes/commit/ebb02cafe7e1d1e067bc37c00fd3613e20230ee5), [d699cbf01](https://github.com/heygen-com/hyperframes/commit/d699cbf014ac2232e3d2cec5c06c9d74103fa61f), [58869f087](https://github.com/heygen-com/hyperframes/commit/58869f0878e304fd39d564b93cc4a8b18e885b4e), [#2814](https://github.com/heygen-com/hyperframes/pull/2814), [2a284a8e3](https://github.com/heygen-com/hyperframes/commit/2a284a8e3aca62100ec037c23c98706e7146aa14)).
- **Honest extraction errors.** Frame-extraction launch failures aggregate into one typed error with narrowed shapes instead of surfacing as an opaque crash ([33ca1de06](https://github.com/heygen-com/hyperframes/commit/33ca1de0631be66bfa5c591a231256c69667226e), [c01e1a5f9](https://github.com/heygen-com/hyperframes/commit/c01e1a5f96839e1a2650516ceb3745ed7b28517f), [9b63646c8](https://github.com/heygen-com/hyperframes/commit/9b63646c8aa036b786513137f2efcdf637ad432c)).
- **Capture self-verification.** Parallel and sequential disk drawElement samples self-verify, screenshot retry recovers disk-path verify failures, a verify failure rethrows past the completeness check, orphaned probe sessions close before retries, and frame stride carries onto worker results so interleaved workers stop reporting false positives ([060b6f8ae](https://github.com/heygen-com/hyperframes/commit/060b6f8ae53e8ae243d8e2c2c3afd690dbe7f99c), [9fc1c2f15](https://github.com/heygen-com/hyperframes/commit/9fc1c2f15990fc44f50533362bec2f075aa1d53d), [ec791e91d](https://github.com/heygen-com/hyperframes/commit/ec791e91d97c0070502c4ad208923d4f98005846), [c85cfae8f](https://github.com/heygen-com/hyperframes/commit/c85cfae8fa97229e9c4f4d7217cf08b4bc859f3d), [b8e101547](https://github.com/heygen-com/hyperframes/commit/b8e10154762f5a0fe71fb4a6a9f3d472e9bf99ec), [4f53dd4f2](https://github.com/heygen-com/hyperframes/commit/4f53dd4f2cd607de17b2d2329746fa5da0cadeaa)).
- **Studio editing.** Preview audio plays at speeds above 1x, flat keyframe retiming is hardened, tween keyframe diamonds retime correctly, composed media treatments survive a round trip, stale color scopes clear, and the grading contracts line up across panels ([07965e9fe](https://github.com/heygen-com/hyperframes/commit/07965e9fe93fc7b53dfe7c933f8abd0c4b10c86d), [#2691](https://github.com/heygen-com/hyperframes/pull/2691), [270179d94](https://github.com/heygen-com/hyperframes/commit/270179d94b3ca3b82f61655b354855187f0d210c), [f25a13692](https://github.com/heygen-com/hyperframes/commit/f25a1369279c99081c56966b14991649138bc035), [d5c7d3ee1](https://github.com/heygen-com/hyperframes/commit/d5c7d3ee16c2db53b91c66353d4f6387fe23e920), [c1fcf7534](https://github.com/heygen-com/hyperframes/commit/c1fcf7534f730f5677b0d5201e6af6d17bd19cb0), [794930a07](https://github.com/heygen-com/hyperframes/commit/794930a07568deddd55ba0d891b68bec739641fa)).
- **Authoring fidelity.** Position edits apply to SVG elements and not just HTML, nested-rule selectors survive composition CSS scoping, `setText` keeps `<br>` line breaks editable, and duration-authored keyframe timing and intent are preserved ([63539a0cd](https://github.com/heygen-com/hyperframes/commit/63539a0cdef75597d2e301736740cf3bdd905596), [#2724](https://github.com/heygen-com/hyperframes/pull/2724), [1e2c7d673](https://github.com/heygen-com/hyperframes/commit/1e2c7d673fc0fe8da9a822bb7a8744f84f44d9d4), [#2733](https://github.com/heygen-com/hyperframes/pull/2733), [dd7378bbd](https://github.com/heygen-com/hyperframes/commit/dd7378bbd934ecc0da85c1edfae7946ac7e2271a), [#2742](https://github.com/heygen-com/hyperframes/pull/2742), [4bfbd89d6](https://github.com/heygen-com/hyperframes/commit/4bfbd89d633d5fd227023643db62d2a566984edb), [d84e999f7](https://github.com/heygen-com/hyperframes/commit/d84e999f728e25cee15a71995805a52d0a7907c4)).
- **Fewer false lint failures.** The `media_in_subcomposition` rule is dropped, bounded GSAP infinite repeats are allowed, compiler-derived `data-end` is recognized as legitimate, and the pivot-drift and `connector_detached` checks are tightened against counterfactuals ([e7f9918d2](https://github.com/heygen-com/hyperframes/commit/e7f9918d21f9fa57f1799c7b3ba38963cfcb52f1), [#2765](https://github.com/heygen-com/hyperframes/pull/2765), [adb149b86](https://github.com/heygen-com/hyperframes/commit/adb149b86939e61bb3fce91cb8d0e5530f7bd29c), [#2763](https://github.com/heygen-com/hyperframes/pull/2763), [ac9f46310](https://github.com/heygen-com/hyperframes/commit/ac9f463108531d28eee496bd837aab542eb9e409), [75ed99e1d](https://github.com/heygen-com/hyperframes/commit/75ed99e1d4f45015812575ff26c07efb0b253f21), [#2819](https://github.com/heygen-com/hyperframes/pull/2819), [7a294f195](https://github.com/heygen-com/hyperframes/commit/7a294f19562928036dae20d5e73c2637d1e19060), [#2739](https://github.com/heygen-com/hyperframes/pull/2739)).
- **Preview and coverage.** The preview server serves external symlink assets, looping short videos are credited in the coverage gate, frame coverage aligns with extraction rounding, and invalid render durations are bounded ([7778c093b](https://github.com/heygen-com/hyperframes/commit/7778c093b6288756cf5e27311828e6049e7c85c3), [#2764](https://github.com/heygen-com/hyperframes/pull/2764), [a637f394e](https://github.com/heygen-com/hyperframes/commit/a637f394ee900c64c8f2e1ee78cf1e0ce17b8739), [#2732](https://github.com/heygen-com/hyperframes/pull/2732), [f0c2c7d23](https://github.com/heygen-com/hyperframes/commit/f0c2c7d23384de589f54c11ff093f128c9a39e56), [344d9c0a8](https://github.com/heygen-com/hyperframes/commit/344d9c0a87aeba01beca618e21d2469921506cdf)).
- **Runtime audio variables in distributed plans.** Audio variables resolved at runtime are carried into distributed plans instead of being dropped when a render fans out ([465c9e764](https://github.com/heygen-com/hyperframes/commit/465c9e764138b94faa48badbee468eb42bd1a39d), [#2725](https://github.com/heygen-com/hyperframes/pull/2725)).
- **CLI process lifecycle.** Command failures report once, error telemetry is awaited before finalization, the post-render exit reset stays root-owned, and the lifecycle migration is complete ([73d3b4e1f](https://github.com/heygen-com/hyperframes/commit/73d3b4e1f491e5211a960bac86fbb674909be7ad), [a9338a4e0](https://github.com/heygen-com/hyperframes/commit/a9338a4e0f91b8482fe32bef50018209c29ce031), [e0bda7a17](https://github.com/heygen-com/hyperframes/commit/e0bda7a17111753f76b7e073d654be802c06c129), [619406a23](https://github.com/heygen-com/hyperframes/commit/619406a23b061bf659d107f83beec4eada6ff086)).
- **Caption template hygiene.** Caption runtimes are wrapped in IIFEs, non-numeric caption-data versions are rejected, a boot fetch never clobbers a manual attach, brand custom properties clear on unbranded re-attach, GSAP renders at attach, and a quadratic hide-all-others loop is gone ([e2846eb7c](https://github.com/heygen-com/hyperframes/commit/e2846eb7cc81821f7fc21a9c4dccdd85c7ef3429), [5c2981d06](https://github.com/heygen-com/hyperframes/commit/5c2981d066480000d623e4d023e9cf918b2772e3), [4f6994719](https://github.com/heygen-com/hyperframes/commit/4f6994719196e72dc01ebe8f60d869e064ec7685), [020c8986f](https://github.com/heygen-com/hyperframes/commit/020c8986f46f795757203900cd251ad551ccc620), [5af6203ae](https://github.com/heygen-com/hyperframes/commit/5af6203ae7fcd506511ca5683a9097d666c4a63e), [18de2b1f1](https://github.com/heygen-com/hyperframes/commit/18de2b1f1de8062a79ae7aa6fa4796e320fe6131), [8bf939043](https://github.com/heygen-com/hyperframes/commit/8bf939043fb44e05f6dd7caba81822ac5ef18334)).
- **Feedback telemetry.** CLI feedback is sent as plain events, and the repro guidance no longer embeds identifying detail ([597c14a88](https://github.com/heygen-com/hyperframes/commit/597c14a8874401fe252d238c1ed4a0b6e2812a2c), [78ab9bc88](https://github.com/heygen-com/hyperframes/commit/78ab9bc889908e412e806d80f4ffbd938efc7a78)).
- **Audio no longer runs past the picture.** Padded audio is normalized on a sample timeline. The mux stops at the shortest normalized stream. The final mux is capped to video duration. AAC packet padding is trimmed exactly. M4A edit timing and priming survive normalization ([4b116b988](https://github.com/heygen-com/hyperframes/commit/4b116b98802c05f0eeb541878806692eea975541), [532461599](https://github.com/heygen-com/hyperframes/commit/532461599b7518f4609002cdb314ff2ce9f3a70f), [63bc525ca](https://github.com/heygen-com/hyperframes/commit/63bc525ca90b08356d50ebb41d4e15581c9d3470), [19258ea5b](https://github.com/heygen-com/hyperframes/commit/19258ea5ba47067e9d5756b122051c467ece0b04), [afc4e96bb](https://github.com/heygen-com/hyperframes/commit/afc4e96bbed21dbcd29544a6be95d35db7dd4eed), [9289551e9](https://github.com/heygen-com/hyperframes/commit/9289551e98958f006ec14af8ae9058096a4932fe), [59c56d325](https://github.com/heygen-com/hyperframes/commit/59c56d325723bbcb7c54315023aedeb72b376cd8), [113a4985b](https://github.com/heygen-com/hyperframes/commit/113a4985b51bc1d77babe7113d6c96bbe46d41bf)).
- **Portable audio padding.** The audio filter chain drops an FFmpeg option the bundled Windows build rejects. Compositions that rendered video but failed to mux audio now complete. Typed audio failure causes are preserved through the engine, the in-process producer, and distributed planning ([3b9552ef9](https://github.com/heygen-com/hyperframes/commit/3b9552ef9db36b133485e4ce805892346e0b9006), [37b88688e](https://github.com/heygen-com/hyperframes/commit/37b88688e7ae854e377c47fe3d9560ab0c930161), [#2769](https://github.com/heygen-com/hyperframes/pull/2769)).
- **Probe failures say why.** ffprobe keeps a bounded stderr tail instead of running quiet. A failed probe now reports the actual error rather than a blank diagnostic ([8c5077068](https://github.com/heygen-com/hyperframes/commit/8c50770684bc87ca67592c45d4101e3c029193d2), [#2772](https://github.com/heygen-com/hyperframes/pull/2772)).
- **Hardened media downloads.** Video downloads are atomic and retry transient failures. Reserved and future-use IPv4 ranges are blocked. Downloader trust-boundary gaps are closed. Network error shapes are narrowed honestly ([c01f6b446](https://github.com/heygen-com/hyperframes/commit/c01f6b446829f15c2c18dfd43da7013b412b2bf7), [2e84faeb2](https://github.com/heygen-com/hyperframes/commit/2e84faeb28ff8689da212a5b7db805f3b454d4d9), [4b81f7858](https://github.com/heygen-com/hyperframes/commit/4b81f785868362fbb5c4bd7f1c5be24b2ccb7f94), [5ce2eb879](https://github.com/heygen-com/hyperframes/commit/5ce2eb879db1bb2b3dd740bcd7e9abf8adaa3230)).
- **Distributed render reliability.** Sparse video directories are materialized. Distributed capture falls back safely. Plan scratch state resets between runs. Oversized plans are attributed and stopped early. Partial color metadata is accepted in plan v2. Cloud Run enforces effective BeginFrame capture ([ddb59d356](https://github.com/heygen-com/hyperframes/commit/ddb59d3567fc70d808f86ef8b5b531078e3df2d6), [96cafb47c](https://github.com/heygen-com/hyperframes/commit/96cafb47c6c9850939c85e2cc76e578d3b6dbd1b), [ebb02cafe](https://github.com/heygen-com/hyperframes/commit/ebb02cafe7e1d1e067bc37c00fd3613e20230ee5), [d699cbf01](https://github.com/heygen-com/hyperframes/commit/d699cbf014ac2232e3d2cec5c06c9d74103fa61f), [58869f087](https://github.com/heygen-com/hyperframes/commit/58869f0878e304fd39d564b93cc4a8b18e885b4e), [#2814](https://github.com/heygen-com/hyperframes/pull/2814), [2a284a8e3](https://github.com/heygen-com/hyperframes/commit/2a284a8e3aca62100ec037c23c98706e7146aa14)).
- **Honest extraction errors.** Frame-extraction launch failures aggregate into one typed error with narrowed shapes. They no longer surface as an opaque crash ([33ca1de06](https://github.com/heygen-com/hyperframes/commit/33ca1de0631be66bfa5c591a231256c69667226e), [c01e1a5f9](https://github.com/heygen-com/hyperframes/commit/c01e1a5f96839e1a2650516ceb3745ed7b28517f), [9b63646c8](https://github.com/heygen-com/hyperframes/commit/9b63646c8aa036b786513137f2efcdf637ad432c)).
- **Capture self-verification.** Parallel and sequential disk drawElement samples self-verify. Screenshot retry recovers disk-path verify failures. A verify failure rethrows past the completeness check. Orphaned probe sessions close before retries. Frame stride carries onto worker results, so interleaved workers stop reporting false positives ([060b6f8ae](https://github.com/heygen-com/hyperframes/commit/060b6f8ae53e8ae243d8e2c2c3afd690dbe7f99c), [9fc1c2f15](https://github.com/heygen-com/hyperframes/commit/9fc1c2f15990fc44f50533362bec2f075aa1d53d), [ec791e91d](https://github.com/heygen-com/hyperframes/commit/ec791e91d97c0070502c4ad208923d4f98005846), [c85cfae8f](https://github.com/heygen-com/hyperframes/commit/c85cfae8fa97229e9c4f4d7217cf08b4bc859f3d), [b8e101547](https://github.com/heygen-com/hyperframes/commit/b8e10154762f5a0fe71fb4a6a9f3d472e9bf99ec), [4f53dd4f2](https://github.com/heygen-com/hyperframes/commit/4f53dd4f2cd607de17b2d2329746fa5da0cadeaa)).
- **Studio editing.** Preview audio plays at speeds above 1x. Flat keyframe retiming is hardened. Tween keyframe diamonds retime correctly. Composed media treatments survive a round trip. Stale color scopes clear. The grading contracts line up across panels ([07965e9fe](https://github.com/heygen-com/hyperframes/commit/07965e9fe93fc7b53dfe7c933f8abd0c4b10c86d), [#2691](https://github.com/heygen-com/hyperframes/pull/2691), [270179d94](https://github.com/heygen-com/hyperframes/commit/270179d94b3ca3b82f61655b354855187f0d210c), [f25a13692](https://github.com/heygen-com/hyperframes/commit/f25a1369279c99081c56966b14991649138bc035), [d5c7d3ee1](https://github.com/heygen-com/hyperframes/commit/d5c7d3ee16c2db53b91c66353d4f6387fe23e920), [c1fcf7534](https://github.com/heygen-com/hyperframes/commit/c1fcf7534f730f5677b0d5201e6af6d17bd19cb0), [794930a07](https://github.com/heygen-com/hyperframes/commit/794930a07568deddd55ba0d891b68bec739641fa)).
- **Authoring fidelity.** Position edits apply to SVG elements, not just HTML. Nested-rule selectors survive composition CSS scoping. `setText` keeps `<br>` line breaks editable. Duration-authored keyframe timing and intent are preserved ([63539a0cd](https://github.com/heygen-com/hyperframes/commit/63539a0cdef75597d2e301736740cf3bdd905596), [#2724](https://github.com/heygen-com/hyperframes/pull/2724), [1e2c7d673](https://github.com/heygen-com/hyperframes/commit/1e2c7d673fc0fe8da9a822bb7a8744f84f44d9d4), [#2733](https://github.com/heygen-com/hyperframes/pull/2733), [dd7378bbd](https://github.com/heygen-com/hyperframes/commit/dd7378bbd934ecc0da85c1edfae7946ac7e2271a), [#2742](https://github.com/heygen-com/hyperframes/pull/2742), [4bfbd89d6](https://github.com/heygen-com/hyperframes/commit/4bfbd89d633d5fd227023643db62d2a566984edb), [d84e999f7](https://github.com/heygen-com/hyperframes/commit/d84e999f728e25cee15a71995805a52d0a7907c4)).
- **Fewer false lint failures.** The `media_in_subcomposition` rule is dropped. Bounded GSAP infinite repeats are allowed. Compiler-derived `data-end` is recognized as legitimate. The pivot-drift and `connector_detached` checks are tightened against counterfactuals ([e7f9918d2](https://github.com/heygen-com/hyperframes/commit/e7f9918d21f9fa57f1799c7b3ba38963cfcb52f1), [#2765](https://github.com/heygen-com/hyperframes/pull/2765), [adb149b86](https://github.com/heygen-com/hyperframes/commit/adb149b86939e61bb3fce91cb8d0e5530f7bd29c), [#2763](https://github.com/heygen-com/hyperframes/pull/2763), [ac9f46310](https://github.com/heygen-com/hyperframes/commit/ac9f463108531d28eee496bd837aab542eb9e409), [75ed99e1d](https://github.com/heygen-com/hyperframes/commit/75ed99e1d4f45015812575ff26c07efb0b253f21), [#2819](https://github.com/heygen-com/hyperframes/pull/2819), [7a294f195](https://github.com/heygen-com/hyperframes/commit/7a294f19562928036dae20d5e73c2637d1e19060), [#2739](https://github.com/heygen-com/hyperframes/pull/2739)).
- **Preview and coverage.** The preview server serves external symlink assets. Looping short videos are credited in the coverage gate. Frame coverage aligns with extraction rounding. Invalid render durations are bounded ([7778c093b](https://github.com/heygen-com/hyperframes/commit/7778c093b6288756cf5e27311828e6049e7c85c3), [#2764](https://github.com/heygen-com/hyperframes/pull/2764), [a637f394e](https://github.com/heygen-com/hyperframes/commit/a637f394ee900c64c8f2e1ee78cf1e0ce17b8739), [#2732](https://github.com/heygen-com/hyperframes/pull/2732), [f0c2c7d23](https://github.com/heygen-com/hyperframes/commit/f0c2c7d23384de589f54c11ff093f128c9a39e56), [344d9c0a8](https://github.com/heygen-com/hyperframes/commit/344d9c0a87aeba01beca618e21d2469921506cdf)).
- **Runtime audio variables in distributed plans.** Audio variables resolved at runtime are carried into distributed plans. They are no longer dropped when a render fans out ([465c9e764](https://github.com/heygen-com/hyperframes/commit/465c9e764138b94faa48badbee468eb42bd1a39d), [#2725](https://github.com/heygen-com/hyperframes/pull/2725)).
- **CLI process lifecycle.** Command failures report once. Error telemetry is awaited before finalization. The post-render exit reset stays root-owned. The lifecycle migration is complete ([73d3b4e1f](https://github.com/heygen-com/hyperframes/commit/73d3b4e1f491e5211a960bac86fbb674909be7ad), [a9338a4e0](https://github.com/heygen-com/hyperframes/commit/a9338a4e0f91b8482fe32bef50018209c29ce031), [e0bda7a17](https://github.com/heygen-com/hyperframes/commit/e0bda7a17111753f76b7e073d654be802c06c129), [619406a23](https://github.com/heygen-com/hyperframes/commit/619406a23b061bf659d107f83beec4eada6ff086)).
- **Caption template hygiene.** Caption runtimes are wrapped in IIFEs. Non-numeric caption-data versions are rejected. A boot fetch never clobbers a manual attach. Brand custom properties clear on unbranded re-attach. GSAP renders at attach. A quadratic hide-all-others loop is gone ([e2846eb7c](https://github.com/heygen-com/hyperframes/commit/e2846eb7cc81821f7fc21a9c4dccdd85c7ef3429), [5c2981d06](https://github.com/heygen-com/hyperframes/commit/5c2981d066480000d623e4d023e9cf918b2772e3), [4f6994719](https://github.com/heygen-com/hyperframes/commit/4f6994719196e72dc01ebe8f60d869e064ec7685), [020c8986f](https://github.com/heygen-com/hyperframes/commit/020c8986f46f795757203900cd251ad551ccc620), [5af6203ae](https://github.com/heygen-com/hyperframes/commit/5af6203ae7fcd506511ca5683a9097d666c4a63e), [18de2b1f1](https://github.com/heygen-com/hyperframes/commit/18de2b1f1de8062a79ae7aa6fa4796e320fe6131), [8bf939043](https://github.com/heygen-com/hyperframes/commit/8bf939043fb44e05f6dd7caba81822ac5ef18334)).
- **Feedback telemetry.** CLI feedback is sent as plain events. The repro guidance no longer embeds identifying detail ([597c14a88](https://github.com/heygen-com/hyperframes/commit/597c14a8874401fe252d238c1ed4a0b6e2812a2c), [78ab9bc88](https://github.com/heygen-com/hyperframes/commit/78ab9bc889908e412e806d80f4ffbd938efc7a78)).
## Docs
- **Send-to guide is discoverable.** The Send-to guide is published in the nav and in `llms.txt`, so agents can find it without being handed the path ([911b332bb](https://github.com/heygen-com/hyperframes/commit/911b332bb2131f1b3fc4abbea563bf0c70d165c4), [#2667](https://github.com/heygen-com/hyperframes/pull/2667)).
- **Changelog video skill.** Captions are non-optional in the changelog-video skill, and a pre-build gate stops a run before it produces an unbuildable composition ([807078c7c](https://github.com/heygen-com/hyperframes/commit/807078c7cde9d5c8403588722d1cd9397c513a0d), [#2729](https://github.com/heygen-com/hyperframes/pull/2729), [7d312bd17](https://github.com/heygen-com/hyperframes/commit/7d312bd170baa6fb1d2c247e14fef7c4d1022279)).
- **Send-to guide is discoverable.** The Send-to guide is published in the nav and in `llms.txt`. Agents can find it without being handed the path ([911b332bb](https://github.com/heygen-com/hyperframes/commit/911b332bb2131f1b3fc4abbea563bf0c70d165c4), [#2667](https://github.com/heygen-com/hyperframes/pull/2667)).
- **Changelog video skill.** Captions are non-optional in the changelog-video skill. A pre-build gate stops a run before it produces an unbuildable composition ([807078c7c](https://github.com/heygen-com/hyperframes/commit/807078c7cde9d5c8403588722d1cd9397c513a0d), [#2729](https://github.com/heygen-com/hyperframes/pull/2729), [7d312bd17](https://github.com/heygen-com/hyperframes/commit/7d312bd170baa6fb1d2c247e14fef7c4d1022279)).
- **Codex plugin packaging.** The skills bundle now packages a Codex plugin upload alongside the existing surfaces ([696cbdbbd](https://github.com/heygen-com/hyperframes/commit/696cbdbbd0e5c83faf72c767126d4a153110f130), [#2668](https://github.com/heygen-com/hyperframes/pull/2668)).
For exact versioned release notes, see the [Changelog](/changelog).
@@ -138,40 +138,40 @@ For exact versioned release notes, see the [Changelog](/changelog).
<Frame>
<DocsVideo
title="HyperFrames video: Weekly Changelog Jul13 20"
src="https://static.heygen.ai/hyperframes/changelog-videos/weekly-changelog-jul13-20-v2.mp4"
src="https://static.heygen.ai/hyperframes/changelog-videos/weekly-changelog-jul13-20.mp4"
/>
</Frame>
Automatic media proxying is the headline. Any video codec your FFmpeg can decode now plays on every live surface, from preview and Studio to play and published pages, while render keeps using the originals. Media Use gains a video generator, Studio's flat inspector ships on by default, and the engine's timeout errors now name the fix. A large batch of render, lint, and CLI reliability fixes lands alongside.
Automatic media proxying is the headline. Any video codec your FFmpeg can decode now plays on every live surface. That covers preview, Studio, play, and published pages. Render keeps using the originals. Media Use gains a video generator. Studio's flat inspector ships on by default. The engine's timeout errors now name the fix. A large batch of render, lint, and CLI reliability fixes lands alongside.
## Features
- **Automatic media proxying.** Studio-server probes each source's codec facts, transcodes a bounded H.264 proxy on demand, and the runtime swaps an undecodable source to its proxy so browser-hostile footage plays instead of showing a black frame. Render always uses the originals ([9ca1e1710](https://github.com/heygen-com/hyperframes/commit/9ca1e171013c0d74a868f5eda3ebdf93c9566632), [#2587](https://github.com/heygen-com/hyperframes/pull/2587), [9d148d288](https://github.com/heygen-com/hyperframes/commit/9d148d288aa1ea1ad4ea687fc21d92f8b5008286), [#2589](https://github.com/heygen-com/hyperframes/pull/2589), [39b588cbd](https://github.com/heygen-com/hyperframes/commit/39b588cbd0e196d1d1db14e959f837dc90cf7788), [#2592](https://github.com/heygen-com/hyperframes/pull/2592)).
- **Proxies across the authoring surfaces.** Proxies serve from the preview route, play, and the static project server, and bake into published archives. Projects can opt out with `media.autoProxy` or `--no-proxy` ([67eab59f4](https://github.com/heygen-com/hyperframes/commit/67eab59f44c0609c7299dc7127d864b37d2d1715), [#2590](https://github.com/heygen-com/hyperframes/pull/2590), [74b4f1e8c](https://github.com/heygen-com/hyperframes/commit/74b4f1e8c3cb0058583c6d9048708519a6d94417), [#2593](https://github.com/heygen-com/hyperframes/pull/2593), [35eff5038](https://github.com/heygen-com/hyperframes/commit/35eff5038b5f8e825b416b05bd7ea5baa38684a0), [#2595](https://github.com/heygen-com/hyperframes/pull/2595), [645880706](https://github.com/heygen-com/hyperframes/commit/6458807066bd4e0c1a6f573423879e97b91ad1c3), [#2591](https://github.com/heygen-com/hyperframes/pull/2591)).
- **Alpha-capable proxies.** Alpha sources get a VP9 and yuva420p WebM proxy instead of a refusal, so a ProRes 4444 file previews rather than going black ([e8371a7ac](https://github.com/heygen-com/hyperframes/commit/e8371a7accfb1ccd88c792898b65910aec60b0bd), [#2598](https://github.com/heygen-com/hyperframes/pull/2598)).
- **Media Use video generation.** `resolve --type video` generates a HeyGen avatar video, free for new API users, and falls back to local LTX-2 when HeyGen is unavailable or you pass `--local-only` ([0a66671fc](https://github.com/heygen-com/hyperframes/commit/0a66671fc576b6b7d4a1b433ff97467dcba20b17), [#2614](https://github.com/heygen-com/hyperframes/pull/2614)).
- **Automatic media proxying.** Studio-server probes each source's codec facts and transcodes a bounded H.264 proxy on demand. The runtime swaps an undecodable source to its proxy. Browser-hostile footage plays instead of showing a black frame. Render always uses the originals ([9ca1e1710](https://github.com/heygen-com/hyperframes/commit/9ca1e171013c0d74a868f5eda3ebdf93c9566632), [#2587](https://github.com/heygen-com/hyperframes/pull/2587), [9d148d288](https://github.com/heygen-com/hyperframes/commit/9d148d288aa1ea1ad4ea687fc21d92f8b5008286), [#2589](https://github.com/heygen-com/hyperframes/pull/2589), [39b588cbd](https://github.com/heygen-com/hyperframes/commit/39b588cbd0e196d1d1db14e959f837dc90cf7788), [#2592](https://github.com/heygen-com/hyperframes/pull/2592)).
- **Proxies across the authoring surfaces.** Proxies serve from the preview route, play, and the static project server. They also bake into published archives. Projects can opt out with `media.autoProxy` or `--no-proxy` ([67eab59f4](https://github.com/heygen-com/hyperframes/commit/67eab59f44c0609c7299dc7127d864b37d2d1715), [#2590](https://github.com/heygen-com/hyperframes/pull/2590), [74b4f1e8c](https://github.com/heygen-com/hyperframes/commit/74b4f1e8c3cb0058583c6d9048708519a6d94417), [#2593](https://github.com/heygen-com/hyperframes/pull/2593), [35eff5038](https://github.com/heygen-com/hyperframes/commit/35eff5038b5f8e825b416b05bd7ea5baa38684a0), [#2595](https://github.com/heygen-com/hyperframes/pull/2595), [645880706](https://github.com/heygen-com/hyperframes/commit/6458807066bd4e0c1a6f573423879e97b91ad1c3), [#2591](https://github.com/heygen-com/hyperframes/pull/2591)).
- **Alpha-capable proxies.** Alpha sources get a VP9 and yuva420p WebM proxy instead of a refusal. A ProRes 4444 file previews rather than going black ([e8371a7ac](https://github.com/heygen-com/hyperframes/commit/e8371a7accfb1ccd88c792898b65910aec60b0bd), [#2598](https://github.com/heygen-com/hyperframes/pull/2598)).
- **Media Use video generation.** `resolve --type video` generates a HeyGen avatar video, free for new API users. It falls back to local LTX-2 when HeyGen is unavailable, or when you pass `--local-only` ([0a66671fc](https://github.com/heygen-com/hyperframes/commit/0a66671fc576b6b7d4a1b433ff97467dcba20b17), [#2614](https://github.com/heygen-com/hyperframes/pull/2614)).
- **Flat inspector on by default.** Studio's flat inspector is now the default panel after this cycle's fixes. Set `VITE_STUDIO_FLAT_INSPECTOR_ENABLED=false` to return to the legacy panel ([a4167ede0](https://github.com/heygen-com/hyperframes/commit/a4167ede074cc4a3e86bc14571ff6a406d664271)).
- **Size-aware cloud archives.** Cloud render and publish honor `.hyperframesignore`, drop root render and snapshot output by default, and add `cloud render --dry-run` diagnostics so projects stay under the 200MB upload limit ([e73304fb0](https://github.com/heygen-com/hyperframes/commit/e73304fb0e94d2272839e55a0bcc4dd00210db34)).
- **Clearer engine timeout errors.** Puppeteer and page-navigation timeouts now name the env vars and escape hatches that fix them, and streaming-encode auto-disables on Windows software-GPU setups ([6944a1c2d](https://github.com/heygen-com/hyperframes/commit/6944a1c2d0430c8d42c6c5e1408d640b9674a2c8), [58cff5f6d](https://github.com/heygen-com/hyperframes/commit/58cff5f6d5dc8a136617df1a1b1712b142ec0986), [cbf2a2ec6](https://github.com/heygen-com/hyperframes/commit/cbf2a2ec69f12f4b5aad384d538d354321ce64cf)).
- **New GSAP lint rules.** Lint flags seek-order and SVG draw-on hazards, relative-value second writers, `tl.set` initial hides, and cold-seek opacity reveals that break at render time ([f3d210066](https://github.com/heygen-com/hyperframes/commit/f3d21006633014fcb29b7a51571cd50ce832fed3), [#2611](https://github.com/heygen-com/hyperframes/pull/2611), [4ad582606](https://github.com/heygen-com/hyperframes/commit/4ad582606bd2c0da9e20c83faa1acb3b79fe6e47), [#2612](https://github.com/heygen-com/hyperframes/pull/2612), [55ee559e4](https://github.com/heygen-com/hyperframes/commit/55ee559e40e2e84e11fe5e09e8bf57b755ea03cb), [#2503](https://github.com/heygen-com/hyperframes/pull/2503)).
- **CLI quality-of-life.** The transcribe timeout is configurable with a duration-scaled default, `--resolution` accepts portrait aspects, and `doctor` surfaces the extract-cache directory alongside a new `--frames-cache-dir` flag ([f8210d96d](https://github.com/heygen-com/hyperframes/commit/f8210d96daf7fd081e9304f26a288a0a1420db66), [46e9ecf3f](https://github.com/heygen-com/hyperframes/commit/46e9ecf3f2f66f7a8b145fc87a36184541b3ad13), [ca3522750](https://github.com/heygen-com/hyperframes/commit/ca352275062574b8b96aaef2ac06f8bae0a1ccc1)).
- **SDK base variable reads.** `getVariableValue({ base: true })` reads the declared default before overrides, and `attachSync` re-syncs the override snapshot on iframe load ([db5e06221](https://github.com/heygen-com/hyperframes/commit/db5e062211fbad324b67bcc672d4e92cc4e2d351), [#2499](https://github.com/heygen-com/hyperframes/pull/2499), [4682da14f](https://github.com/heygen-com/hyperframes/commit/4682da14f19061aeac25b723e1cbb6a98f5d86ad)).
- **Size-aware cloud archives.** Cloud render and publish honor `.hyperframesignore`. They drop root render and snapshot output by default. They add `cloud render --dry-run` diagnostics, so projects stay under the 200MB upload limit ([e73304fb0](https://github.com/heygen-com/hyperframes/commit/e73304fb0e94d2272839e55a0bcc4dd00210db34)).
- **Clearer engine timeout errors.** Puppeteer and page-navigation timeouts now name the env vars and escape hatches that fix them. Streaming-encode auto-disables on Windows software-GPU setups ([6944a1c2d](https://github.com/heygen-com/hyperframes/commit/6944a1c2d0430c8d42c6c5e1408d640b9674a2c8), [58cff5f6d](https://github.com/heygen-com/hyperframes/commit/58cff5f6d5dc8a136617df1a1b1712b142ec0986), [cbf2a2ec6](https://github.com/heygen-com/hyperframes/commit/cbf2a2ec69f12f4b5aad384d538d354321ce64cf)).
- **New GSAP lint rules.** Lint flags seek-order and SVG draw-on hazards. It flags relative-value second writers and `tl.set` initial hides. It flags cold-seek opacity reveals that break at render time ([f3d210066](https://github.com/heygen-com/hyperframes/commit/f3d21006633014fcb29b7a51571cd50ce832fed3), [#2611](https://github.com/heygen-com/hyperframes/pull/2611), [4ad582606](https://github.com/heygen-com/hyperframes/commit/4ad582606bd2c0da9e20c83faa1acb3b79fe6e47), [#2612](https://github.com/heygen-com/hyperframes/pull/2612), [55ee559e4](https://github.com/heygen-com/hyperframes/commit/55ee559e40e2e84e11fe5e09e8bf57b755ea03cb), [#2503](https://github.com/heygen-com/hyperframes/pull/2503)).
- **CLI quality-of-life.** The transcribe timeout is configurable, with a duration-scaled default. `--resolution` accepts portrait aspects. `doctor` surfaces the extract-cache directory, alongside a new `--frames-cache-dir` flag ([f8210d96d](https://github.com/heygen-com/hyperframes/commit/f8210d96daf7fd081e9304f26a288a0a1420db66), [46e9ecf3f](https://github.com/heygen-com/hyperframes/commit/46e9ecf3f2f66f7a8b145fc87a36184541b3ad13), [ca3522750](https://github.com/heygen-com/hyperframes/commit/ca352275062574b8b96aaef2ac06f8bae0a1ccc1)).
- **SDK base variable reads.** `getVariableValue({ base: true })` reads the declared default before overrides. `attachSync` re-syncs the override snapshot on iframe load ([db5e06221](https://github.com/heygen-com/hyperframes/commit/db5e062211fbad324b67bcc672d4e92cc4e2d351), [#2499](https://github.com/heygen-com/hyperframes/pull/2499), [4682da14f](https://github.com/heygen-com/hyperframes/commit/4682da14f19061aeac25b723e1cbb6a98f5d86ad)).
## Fixes
- **Deep sub-composition nesting.** Recursive sub-composition inlining now handles depth-3 and deeper nesting ([d21883fe0](https://github.com/heygen-com/hyperframes/commit/d21883fe05a5819e910d4747683dc008a0ab5147), [#2660](https://github.com/heygen-com/hyperframes/pull/2660)).
- **Final frame holds.** Video holds its final frame through the rest of the composition instead of dropping to blank ([2e8f871bc](https://github.com/heygen-com/hyperframes/commit/2e8f871bc86d29ec3369f0eb11f0183b2001a07a)).
- **No phantom capture duplicates.** Capture stops compositing phantom duplicates when captureBeyondViewport is on ([2be8a62c0](https://github.com/heygen-com/hyperframes/commit/2be8a62c0009e61aeae7f713773013afd9b6f173), [#2607](https://github.com/heygen-com/hyperframes/pull/2607)).
- **Clean CLI output.** Diagnostics and the SystemMemory cgroup notice now go to stderr, keeping `--json` output and stdout parsers clean ([b179c9536](https://github.com/heygen-com/hyperframes/commit/b179c95362645c3ca06fa869d698429e2e3d1e61), [#2520](https://github.com/heygen-com/hyperframes/pull/2520), [d92d1d4f5](https://github.com/heygen-com/hyperframes/commit/d92d1d4f51e2737d19db8a67073da8ae04a16789), [#2522](https://github.com/heygen-com/hyperframes/pull/2522)).
- **Studio reliability.** Composition timelines are hardened, stale failed sidecars are ignored for existing renders, and stale SwiftShader layers are prevented ([2b65b4efc](https://github.com/heygen-com/hyperframes/commit/2b65b4efcef9f69ab294aa608b54a89a600ab76f), [#2615](https://github.com/heygen-com/hyperframes/pull/2615), [2577aaffe](https://github.com/heygen-com/hyperframes/commit/2577aaffeb9703be3d9dd17c0c5fb3c45b6e32e7), [#2621](https://github.com/heygen-com/hyperframes/pull/2621), [54a3ef200](https://github.com/heygen-com/hyperframes/commit/54a3ef2000da635b93c03a41c129a78f0276bf38)).
- **Platform fixes.** Intel macOS background removal is restored, Windows work dirs avoid the output path limit, and a dyld crash on older macOS now points at `HYPERFRAMES_BROWSER_PATH` ([04954ead8](https://github.com/heygen-com/hyperframes/commit/04954ead818d5db91efbdd7bbb2bd1e5f07a2f60), [#2480](https://github.com/heygen-com/hyperframes/pull/2480), [882c20324](https://github.com/heygen-com/hyperframes/commit/882c203241b43e6a1515c2672a4285d0d4f5425c), [#2479](https://github.com/heygen-com/hyperframes/pull/2479), [0d16f19b0](https://github.com/heygen-com/hyperframes/commit/0d16f19b07b1c7f60d54cac4f05f93ea3abacbcd)).
- **Clean CLI output.** Diagnostics and the SystemMemory cgroup notice now go to stderr. That keeps `--json` output and stdout parsers clean ([b179c9536](https://github.com/heygen-com/hyperframes/commit/b179c95362645c3ca06fa869d698429e2e3d1e61), [#2520](https://github.com/heygen-com/hyperframes/pull/2520), [d92d1d4f5](https://github.com/heygen-com/hyperframes/commit/d92d1d4f51e2737d19db8a67073da8ae04a16789), [#2522](https://github.com/heygen-com/hyperframes/pull/2522)).
- **Studio reliability.** Composition timelines are hardened. Stale failed sidecars are ignored for existing renders. Stale SwiftShader layers are prevented ([2b65b4efc](https://github.com/heygen-com/hyperframes/commit/2b65b4efcef9f69ab294aa608b54a89a600ab76f), [#2615](https://github.com/heygen-com/hyperframes/pull/2615), [2577aaffe](https://github.com/heygen-com/hyperframes/commit/2577aaffeb9703be3d9dd17c0c5fb3c45b6e32e7), [#2621](https://github.com/heygen-com/hyperframes/pull/2621), [54a3ef200](https://github.com/heygen-com/hyperframes/commit/54a3ef2000da635b93c03a41c129a78f0276bf38)).
- **Platform fixes.** Intel macOS background removal is restored. Windows work dirs avoid the output path limit. A dyld crash on older macOS now points at `HYPERFRAMES_BROWSER_PATH` ([04954ead8](https://github.com/heygen-com/hyperframes/commit/04954ead818d5db91efbdd7bbb2bd1e5f07a2f60), [#2480](https://github.com/heygen-com/hyperframes/pull/2480), [882c20324](https://github.com/heygen-com/hyperframes/commit/882c203241b43e6a1515c2672a4285d0d4f5425c), [#2479](https://github.com/heygen-com/hyperframes/pull/2479), [0d16f19b0](https://github.com/heygen-com/hyperframes/commit/0d16f19b07b1c7f60d54cac4f05f93ea3abacbcd)).
- **Media Use asset cleanup.** Failed asset reservations are cleaned up instead of leaking zero-byte placeholders ([49113eb08](https://github.com/heygen-com/hyperframes/commit/49113eb08487b2c53b7404bc26c5e419244ae97f), [#2627](https://github.com/heygen-com/hyperframes/pull/2627)).
- **Producer coverage gate.** Held video tails are credited in the coverage gate ([209784ab2](https://github.com/heygen-com/hyperframes/commit/209784ab27801b70c9d8e636e4eaf2e3dc796d2b), [#2606](https://github.com/heygen-com/hyperframes/pull/2606)).
## Docs & Examples
- **Automatic proxying guide.** New docs cover the proxy cache, published-proxy baking, the render-original invariant, FFmpeg requirements, and both opt-out forms ([8c1b6c515](https://github.com/heygen-com/hyperframes/commit/8c1b6c515401a03e1a8394cff60b4e7410f14f0d), [#2596](https://github.com/heygen-com/hyperframes/pull/2596)).
- **Send-to guides consolidated.** The Send-to import guidance now lives in one guide, resolving an earlier fidelity contradiction ([8bfc67688](https://github.com/heygen-com/hyperframes/commit/8bfc676881c07c3c8ee1f0b6b247cc5f207cd3fd), [#2619](https://github.com/heygen-com/hyperframes/pull/2619), [7acabbcde](https://github.com/heygen-com/hyperframes/commit/7acabbcdeb9b55ce9f75c7d35d7f281273a99f29), [#2620](https://github.com/heygen-com/hyperframes/pull/2620)).
- **Automatic proxying guide.** New docs cover the proxy cache, published-proxy baking, and the render-original invariant. They also cover FFmpeg requirements and both opt-out forms ([8c1b6c515](https://github.com/heygen-com/hyperframes/commit/8c1b6c515401a03e1a8394cff60b4e7410f14f0d), [#2596](https://github.com/heygen-com/hyperframes/pull/2596)).
- **Send-to guides consolidated.** The Send-to import guidance now lives in one guide. That resolves an earlier fidelity contradiction ([8bfc67688](https://github.com/heygen-com/hyperframes/commit/8bfc676881c07c3c8ee1f0b6b247cc5f207cd3fd), [#2619](https://github.com/heygen-com/hyperframes/pull/2619), [7acabbcde](https://github.com/heygen-com/hyperframes/commit/7acabbcdeb9b55ce9f75c7d35d7f281273a99f29), [#2620](https://github.com/heygen-com/hyperframes/pull/2620)).
- **Core skills install by default.** The core skill set now installs by default on every surface ([3bb26b0f0](https://github.com/heygen-com/hyperframes/commit/3bb26b0f08142e95126b4934511a09a1e68c143d), [#2554](https://github.com/heygen-com/hyperframes/pull/2554)).
- **TTS docs aligned.** The skill's text-to-speech docs now match the CLI contract ([428e57191](https://github.com/heygen-com/hyperframes/commit/428e571914ee979c815097fbbd26d56757a11056), [#2483](https://github.com/heygen-com/hyperframes/pull/2483)).