chore: import upstream snapshot with attribution
regression / regression-shards (style-16-prod style-9-prod style-17-prod iframe-render-compat variables-prod mp4-h265-sdr, shard-4) (push) Has been cancelled
regression / regression-shards (style-4-prod style-11-prod style-2-prod animejs-adapter typegpu-adapter parallel-capture-regression, shard-5) (push) Has been cancelled
regression / regression-shards (style-7-prod style-8-prod style-10-prod css-spinner-render-compat webm-transparency mp4-h264-sdr webm-vp9, shard-3) (push) Has been cancelled
regression / regression-shards (sub-composition-video style-18-prod raf-ball-render-compat font-variant-numeric sub-comp-t0 sub-comp-id-selector, shard-7) (push) Has been cancelled
Windows render verification / Detect changes (push) Has been cancelled
Windows render verification / Preflight (lint + format) (push) Has been cancelled
Windows render verification / Render on windows-latest (push) Has been cancelled
Windows render verification / Tests on windows-latest (push) Has been cancelled
CI / Detect changes (push) Has been cancelled
CI / Build (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Fallow audit (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Test (push) Has been cancelled
CI / Producer: integration tests (push) Has been cancelled
CI / Producer: unit tests (push) Has been cancelled
CI / File size check (push) Has been cancelled
CI / Test: skills (push) Has been cancelled
CI / Skills: manifest in sync (push) Has been cancelled
CI / CLI: npx shim (macos-latest) (push) Has been cancelled
CI / CLI: npx shim (ubuntu-latest) (push) Has been cancelled
CI / CLI: npx shim (windows-latest) (push) Has been cancelled
CI / SDK: unit + contract + smoke (push) Has been cancelled
CI / Test: runtime contract (push) Has been cancelled
CI / Studio: load smoke (push) Has been cancelled
CI / Smoke: global install (push) Has been cancelled
CI / CLI smoke (required) (push) Has been cancelled
CI / Semantic PR title (push) Has been cancelled
Player perf / Detect changes (push) Has been cancelled
Player perf / Preflight (lint + format) (push) Has been cancelled
Player perf / player-perf (push) Has been cancelled
Player perf / Perf: drift (push) Has been cancelled
Player perf / Perf: fps (push) Has been cancelled
Player perf / Perf: parity (push) Has been cancelled
Player perf / Perf: scrub (push) Has been cancelled
Player perf / Perf: load (push) Has been cancelled
preview-regression / Detect changes (push) Has been cancelled
preview-regression / Preflight (lint + format) (push) Has been cancelled
preview-regression / Preview parity (push) Has been cancelled
preview-regression / preview-regression (push) Has been cancelled
regression / regression (push) Has been cancelled
regression / Detect changes (push) Has been cancelled
regression / Preflight (lint + format) (push) Has been cancelled
regression / regression-shards (hdr-regression style-5-prod style-3-prod mov-prores, shard-1) (push) Has been cancelled
regression / regression-shards (overlay-montage-prod style-12-prod chat missing-host-comp-id png-sequence portrait-edge-bleed, shard-6) (push) Has been cancelled
regression / regression-shards (style-13-prod style-6-prod vignelli-stacking gsap-letters-render-compat audio-mux-parity, shard-8) (push) Has been cancelled
regression / regression-shards (style-15-prod hdr-hlg-regression style-1-prod many-cuts vfr-screen-recording render-symlinked-assets, shard-2) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Docs / Validate docs (push) Has been cancelled
Sync skills to ClawHub / Publish changed skills (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:58:35 +08:00
commit 85453da49f
4031 changed files with 710987 additions and 0 deletions
+167
View File
@@ -0,0 +1,167 @@
---
title: 4K Rendering
description: "Render any composition to 4K (3840×2160) without rewriting it — the CLI supersamples a 1080p composition via Chrome's device scale factor."
---
Hyperframes renders to 4K (3840×2160) two ways. Both produce a true 4K MP4; pick the one that matches your project.
<CardGroup cols={2}>
<Card title="Author at 4K" icon="ruler">
Scaffold the project at 4K so the composition is laid out at 4K natively. Best when you want crisp 4K-native typography and assets.
```bash
npx hyperframes init my-video --resolution 4k
```
</Card>
<Card title="Supersample at render" icon="up-right-and-down-left-from-center">
Keep your existing 1080p composition. Pass `--resolution 4k` at render time and Chrome renders at 2× DPR so the screenshot lands at 4K.
```bash
npx hyperframes render --resolution 4k --output 4k.mp4
```
</Card>
</CardGroup>
## Quickstart
<Steps>
<Step title="Render an existing project at 4K">
```bash Terminal
npx hyperframes render --resolution 4k --output my-video-4k.mp4
```
The composition's `data-width` / `data-height` are unchanged. Chrome's `deviceScaleFactor` is set to `2`, so the captured screenshot for each frame is 3840×2160. ffmpeg auto-detects the dimensions from the screenshot stream and encodes at 4K.
</Step>
<Step title="Or scaffold a new project at 4K">
```bash Terminal
npx hyperframes init my-video --resolution 4k
```
Every scaffolded HTML file is patched in place: `data-width="3840"`, `data-height="2160"`, `data-resolution="landscape-4k"`, `#stage` CSS dimensions, and the `<meta viewport>` tag.
</Step>
<Step title="Verify the output is 4K">
```bash Terminal
ffprobe -v error -select_streams v:0 -show_entries stream=width,height my-video-4k.mp4
```
Expected:
```
width=3840
height=2160
```
</Step>
</Steps>
## Resolution presets
`--resolution` accepts these values on both `init` and `render`:
| Preset | Dimensions | Aliases |
|--------|-----------|---------|
| `landscape` | 1920×1080 | `1080p`, `hd` |
| `portrait` | 1080×1920 | `1080p-portrait` |
| `square` | 1080×1080 | `1080p-square`, `square-1080p` |
| `landscape-4k` | 3840×2160 | `4k`, `uhd` |
| `portrait-4k` | 2160×3840 | `4k-portrait` |
| `square-4k` | 2160×2160 | `4k-square` |
Examples:
```bash Terminal
npx hyperframes render --resolution 4k # landscape 4K
npx hyperframes render --resolution portrait-4k # vertical 4K (TikTok / Reels at max quality)
npx hyperframes render --resolution 1080p # explicit 1080p (no-op on 1080p compositions)
```
## How `--resolution` works (supersampling)
The composition stays at its authored dimensions. Hyperframes computes a `deviceScaleFactor` from the ratio of output to composition dimensions and passes it to Chrome:
| Composition | `--resolution` | `deviceScaleFactor` | Output |
|-------------|---------------|--------------------|--------|
| 1920×1080 | `4k` | 2 | 3840×2160 |
| 1080×1920 | `portrait-4k` | 2 | 2160×3840 |
| 3840×2160 | `4k` | 1 (no-op) | 3840×2160 |
Chrome then renders the page at the higher DPR — effectively rendering each CSS pixel as 2×2 device pixels — so the captured screenshot is at the requested resolution.
<Tip>
This approach is intentionally simple — no composition edits, no second authoring pass. The tradeoff: 4K renders take roughly 4× as long per frame because there are 4× the pixels to capture and encode.
</Tip>
## What scales, what doesn't
Supersampling re-renders the page at higher DPR. That genuinely helps anything the browser rasterizes from a vector or high-resolution source, and does nothing for content already locked to a fixed pixel grid. Knowing which is which sets correct expectations before a 4K render:
| Asset type | Behavior at `--resolution 4k` |
|------------|------------------------------|
| Text (HTML, SVG `<text>`, web fonts) | ✅ **Re-rasterized at 4K.** Glyphs are vector and the browser shapes/rasterizes them at the new DPR. Crisp at any scale. |
| SVG / vector graphics | ✅ **Re-rasterized at 4K.** Same story as text — paths are vector. |
| CSS shapes, gradients, borders, shadows | ✅ **Re-rasterized at 4K.** Browser-generated raster. |
| Images with intrinsic dimensions ≥ 4K | ✅ **Full benefit.** A 3840×2160 source serves all the detail. |
| Images smaller than 4K (e.g. a 1920×1080 PNG) | ⚠️ **No new detail.** Browser upscales the source bitmap; output is no sharper than rendering at 1080p and upscaling externally — but no worse either. |
| `<video>` elements | ❌ **Locked to source resolution.** A 1080p MP4 stays 1080p; the supersample only helps the surrounding DOM. Encode source video at the target resolution if you need 4K throughout. |
| `<canvas>` (2D and WebGL) | ❌ **Locked to canvas's intrinsic dimensions.** `<canvas width="1920" height="1080">` is a 1080p bitmap regardless of DPR. To render canvas content at 4K, multiply `canvas.width` / `canvas.height` by your target DPR and scale the drawing context (`ctx.scale(2, 2)` for a 2× canvas with the same logical layout). |
| Pre-rendered video frames injected by the engine | ❌ **Locked to extraction resolution.** When the producer pre-extracts `<video>` frames via ffmpeg, they're decoded at the source video's dimensions. |
**Rule of thumb**: if the asset is *vector or generated by the browser*, supersampling helps. If it's a *bitmap with fixed pixel dimensions* (video, canvas, low-res PNG), it doesn't — author it at the target resolution instead.
## Constraints
`--resolution` enforces three guards before any frames are captured. If any fail, the render exits before doing work.
### Aspect ratio must match
```bash
# OK — both landscape
hyperframes render --resolution 4k # composition is 1920×1080
# Error — composition is landscape, target is portrait
hyperframes render --resolution portrait-4k # composition is 1920×1080
# → outputResolution portrait-4k (2160×3840) does not match the aspect ratio
# of the composition (1920×1080). Pick a preset whose orientation matches.
```
### The scale must be an integer
The width ratio (output ÷ composition) must be a positive integer. 1080p → 4K is exactly `2×`. 720p → 4K would be `3×` and works. Non-integer scales like 900p → 4K (`2.4×`) introduce aliasing on subpixel-positioned text — Hyperframes refuses rather than producing a blurry render.
### Downsampling is not supported
`--resolution` only supersamples. A 4K composition cannot be downsampled to 1080p with this flag — render at the composition's native resolution and downscale separately with ffmpeg if needed.
### Not yet supported with `--hdr`
The HDR layered compositor processes pixel buffers at composition dimensions; supersample + HDR would need parallel scaling for those buffers. The combination is rejected with a clear error message. Render in two passes if you need both: HDR at composition resolution, then upscale separately.
## Performance
A 1080p → 4K supersample is roughly 4× more pixels to capture, encode, and write. Expect:
- **Per-frame capture**: 34× slower (Chrome paints 4× the pixels and the screenshot transfer is 4× larger)
- **Encoding**: 23× slower (depends on codec; H.264 scales sublinearly with resolution)
- **Memory**: bounded — the engine's frame data-URI cache is byte-budgeted (default 1500 MB per worker, configurable via `PRODUCER_FRAME_DATA_URI_CACHE_BYTES_MB`)
- **Output file size**: at the default CRF, expect 35× the file size of the 1080p render. Pass `--video-bitrate 25M` (or higher) for predictable file sizes.
For a 4K render of a 30-second composition, plan on a few minutes of wall time on a modern laptop. Add `--workers 4` (or more) on a render box for parallel capture.
## Studio support
The Renders panel in Studio includes a resolution dropdown next to the format and quality selectors. Pick `4K` (or `4K ↕` for portrait) and hit **Export** — the same supersampling path runs as the CLI flag, no composition edits required.
The dropdown defaults to `Auto` (render at the composition's authored size). Available presets:
- **Auto** — composition's native dimensions
- **1080p ↔** / **1080p ↕** — 1920×1080 / 1080×1920
- **4K ↔** / **4K ↕** — 3840×2160 / 2160×3840
The resolution applies per render, not per project — your composition files are unchanged. The same [constraints](#constraints) apply; when the producer rejects a combination, the failure surfaces in the Studio render queue.
You can also drive resolution from the CLI:
- **New project**: `hyperframes init my-video --resolution 4k`
- **Existing project**: `hyperframes render --resolution 4k --output 4k.mp4`
## See also
- [`render` CLI reference](/packages/cli#render) — every render flag including `--video-bitrate` and `--crf`
- [`init` CLI reference](/packages/cli#init) — the `--resolution` flag at scaffold time
- [HDR Rendering](/guides/hdr) — color pipeline guide; HDR + 4K is not yet a supported combination
+150
View File
@@ -0,0 +1,150 @@
---
title: Google Antigravity
description: "Set up HyperFrames in Google Antigravity — install skills, author compositions, and render video from the agent-first IDE."
---
Google Antigravity is Google's agent-first IDE built on VS Code. Its agent discovers and loads HyperFrames skills automatically based on your prompts, so you get correct compositions without memorizing framework internals.
## Install skills
Install the HyperFrames skill package into your project:
```bash
npx skills add heygen-com/hyperframes
```
This places skill directories inside `.agents/skills/` at your workspace root — the location Antigravity scans for workspace-scoped skills.
<Tip>
If you want the skills available across all your Antigravity projects, install them to the global scope instead:
```bash
npx skills add heygen-com/hyperframes --agent antigravity --global
```
Global skills live in `~/.gemini/antigravity/skills/` and load in every workspace.
</Tip>
Verify the skills are installed:
```bash
ls .agents/skills/
```
You should see directories like `hyperframes/` (the entry skill), `hyperframes-core/`, `hyperframes-animation/`, `hyperframes-cli/`, and others — each containing a `SKILL.md` that the agent reads on demand.
## How skills work in Antigravity
Antigravity uses **semantic matching** — when you type a prompt, the agent compares it against the `description` field of every available skill and loads the ones that are relevant. You don't need to invoke skills with a slash command (though you can reference them by name for precision).
| You say | Agent loads |
|---|---|
| "Create a 10-second product intro with captions" | `hyperframes`, `hyperframes-core` |
| "Add a GSAP scale-pop to the title" | `hyperframes-animation` |
| "Use Tailwind for styling" | `hyperframes-core` |
| "Transcribe this audio and add captions" | `media-use` |
| "Add a shimmer sweep transition" | `hyperframes-registry` |
The skill is only injected into the agent's context window when it matches — this keeps the context clean and focused.
<Note>
For the best results, mention "HyperFrames" or "composition" in your prompt so the agent matches the right skills immediately instead of guessing at generic web-video conventions.
</Note>
## Create and preview a video
<Steps>
<Step title="Scaffold a project">
Open Antigravity's integrated terminal and run:
```bash
npx hyperframes init my-video
cd my-video
```
The wizard walks you through example selection and media import. Skills are installed automatically inside the new project.
</Step>
<Step title="Start the preview server">
```bash
npx hyperframes preview
```
This launches the HyperFrames Studio in your browser with hot reload — edits to `index.html` appear instantly.
</Step>
<Step title="Prompt the agent">
In Antigravity's agent sidebar, describe the video you want:
> Create a 15-second dark-themed product intro for my SaaS app with a fade-in title, hype-style captions, and a flash transition to the CTA.
The agent reads the HyperFrames skill, writes valid HTML with `data-*` attributes and GSAP timelines, and saves it to your project files. The preview updates automatically.
</Step>
<Step title="Iterate">
Talk to the agent like a video editor — don't re-prompt from scratch:
> Make the title 2x bigger and swap the transition to a whip pan.
> Add a lower third at 0:03 with my name.
> Replace the background with assets/hero.mp4.
</Step>
<Step title="Render">
```bash
npx hyperframes render --output output.mp4
```
</Step>
</Steps>
## Using multiple agents in Manager view
Antigravity's [Manager view](https://antigravity.google/docs/agent) lets you orchestrate multiple agents in parallel. For complex multi-scene videos, you can split the work:
- **Agent 1:** Author the intro scene and transitions
- **Agent 2:** Generate TTS narration and caption timing
- **Agent 3:** Build the data visualization scene
Each agent picks up the relevant HyperFrames skills independently. Merge the output into a single `index.html` with `data-composition-src` references when all agents finish.
## MCP alternative
If you prefer zero-install cloud authoring, Antigravity supports remote MCP servers. Add the HyperFrames MCP connector to author and render compositions without the CLI — see [Antigravity's MCP documentation](https://antigravity.google/docs/mcp) for the exact setup steps in your version, then use the server URL:
```
https://mcp.heygen.com/mcp/hyperframes
```
Authorize via OAuth with your HeyGen account when prompted. See the [HyperFrames MCP guide](/guides/mcp) for full details on tools and prompting. The MCP handles rendering in the cloud; the CLI gives you local control.
## Agent instruction files
HyperFrames projects created with `npx hyperframes init` scaffold both `CLAUDE.md` and `AGENTS.md` automatically — these give the agent additional composition context beyond what the skills provide.
- **`AGENTS.md`** — the cross-agent convention. Antigravity reads this regardless of which model you've selected (Gemini, Claude, or others).
- **`CLAUDE.md`** — read when Antigravity's agent uses a Claude model (Sonnet or Opus), providing Claude-specific project instructions.
## Tips
- **Mention the skill by name when precision matters.** "Using the hyperframes skill, add a grain overlay" is more reliable than "add a grain overlay" when the agent has many skills loaded.
- **Use `npx hyperframes lint` before rendering.** The linter catches structural issues (missing `class="clip"`, unregistered timelines, muted video violations) that the agent might miss on complex edits.
- **Install registry blocks for advanced effects.** Run `npx hyperframes add shimmer-sweep` to install pre-built blocks, then ask the agent to wire them into your composition.
- **Keep the preview server running.** Antigravity's file watcher + HyperFrames' hot reload means you see every agent edit in real time.
## Next steps
<CardGroup cols={2}>
<Card title="Prompting guide" icon="message" href="/guides/prompting">
Vocabulary and patterns that produce better compositions.
</Card>
<Card title="Catalog" icon="grid-2" href="/catalog/blocks/data-chart">
50+ ready-to-use blocks the agent can install and wire.
</Card>
<Card title="GSAP Animation" icon="wand-magic-sparkles" href="/guides/gsap-animation">
Motion principles and timeline authoring.
</Card>
<Card title="Pipeline guide" icon="list-check" href="/guides/pipeline">
The 7-step structure agents follow for multi-beat videos.
</Card>
</CardGroup>
+88
View File
@@ -0,0 +1,88 @@
---
title: Authentication & API keys
description: "Sign in to HeyGen, and how the keys for voice, music, and capture resolve across the CLI and skills — including the priority order and the fully local fallback."
---
HyperFrames uses a HeyGen credential for premium voiceover (TTS) and the music / sound-effects library. Other providers are optional, and **everything runs without any key** — voice and music fall back to fully local engines. This page covers signing in, the keys each capability uses, and the order they resolve.
## Sign in
Signing in is the same OAuth step as creating an account — new users land on the sign-up screen.
<Steps>
<Step title="Sign in">
The default flow opens your browser for OAuth and captures the token on a loopback port:
```bash
npx hyperframes auth login
# ✓ Signed in.
```
For CI or headless machines, save a long-lived API key instead:
```bash
npx hyperframes auth login --api-key # hidden-input prompt
echo "$HEYGEN_API_KEY" | npx hyperframes auth login --api-key # from stdin
```
</Step>
<Step title="Confirm what's configured">
```bash
npx hyperframes auth status
```
Shows the active credential's source and verified identity, and — when you're signed out — which local engines voice and music will use. Add `--json` for `{ configured, recommended_action, offline_engines }` in scripts.
</Step>
</Steps>
The credential lives in `~/.heygen/credentials` (mode `0600`) — no per-repo `.env` to manage. Browser OAuth is a `hyperframes auth login` feature. The separate [`heygen` CLI](https://github.com/heygen-com/heygen-cli) (its own install — there's no `npx heygen`) is API-key-only, so `heygen auth login` just stores a key you paste. Both read the same `~/.heygen/credentials`, so signing in with one carries to the other.
<Tip>
No account needed to try HyperFrames. With no credential, voice uses **Kokoro** and music uses **MusicGen**, both fully local and offline — see [Working offline](#working-offline).
</Tip>
## How credentials resolve
The HeyGen credential drives TTS and music / SFX **retrieval**. It resolves first-match-wins:
1. `HEYGEN_API_KEY` — environment variable
2. `HYPERFRAMES_API_KEY` — alias, for parity with other tools
3. `~/.heygen/credentials` — written by `hyperframes auth login` (or `heygen auth login`)
Point at a different config directory with `HEYGEN_CONFIG_DIR`, or a different backend with `HEYGEN_API_URL`.
## Keys by capability
Each capability picks the **first available provider** in order; the last is always a local engine that needs no key. Cloud providers below the HeyGen line need their own key *and* a local Python dependency.
| Capability | Provider order | Key(s) — first match wins | Local dependency |
|------------|----------------|---------------------------|------------------|
| **Voice (TTS)** | HeyGen → ElevenLabs → Kokoro | `HEYGEN_API_KEY` → `HYPERFRAMES_API_KEY` → `~/.heygen` · then `ELEVENLABS_API_KEY` | Kokoro: `pip install kokoro-onnx soundfile` |
| **Music (BGM)** | HeyGen library → Lyria → MusicGen | HeyGen credential (above) · then `GEMINI_API_KEY` → `GOOGLE_API_KEY` | MusicGen: `pip install transformers torch soundfile numpy` |
| **Sound effects** | HeyGen library → bundled library | HeyGen credential (above) | bundled — no deps |
| **Capture descriptions** | OpenRouter → Gemini | `OPENROUTER_API_KEY` → `GEMINI_API_KEY` | — (optional; for [website-to-video](/guides/website-to-video)) |
Run `npx hyperframes doctor` to check which local dependencies are installed. The media skills also run `hyperframes auth status` as a preflight before generating, so you always know whether a run will use HeyGen or a local engine before it starts.
## Working offline
No key configured is a normal state, not an error. The workflow runs entirely on local models:
- **Voice** — Kokoro-82M (54 voices), with Whisper for word-level caption alignment.
- **Music** — MusicGen (`facebook/musicgen-small`).
- **Sound effects** — a bundled library.
Local engines are free and offline; HeyGen gives higher-quality voices and a professionally produced music library. Sign in any time to switch a project from local to HeyGen.
## Environment variables
| Variable | Used for |
|----------|----------|
| `HEYGEN_API_KEY` | HeyGen credential — voice + music/SFX retrieval. Highest priority. |
| `HYPERFRAMES_API_KEY` | Alias for `HEYGEN_API_KEY`. |
| `HEYGEN_API_URL` | API base URL (default `https://api.heygen.com`). |
| `HEYGEN_CONFIG_DIR` | Credentials directory (default `~/.heygen`). |
| `ELEVENLABS_API_KEY` | ElevenLabs TTS, used when no HeyGen credential is present. |
| `GEMINI_API_KEY` / `GOOGLE_API_KEY` | Lyria music generation (and capture descriptions). |
| `OPENROUTER_API_KEY` | Capture descriptions; takes priority over Gemini for that step. |
See the [`hyperframes auth`](/packages/cli#hyperframes-auth) command reference for subcommand details, and [Cloud rendering](/deploy/cloud) for using the same credential to render in HeyGen's cloud.
File diff suppressed because it is too large Load Diff
+148
View File
@@ -0,0 +1,148 @@
---
title: Claude Design
description: "Create HyperFrames video drafts in Claude Design, then refine in any AI coding agent."
---
Claude Design produces a **valid first draft** of a HyperFrames video — brand identity, scene content, layout, animations, and transitions. You then download the ZIP and refine in any AI coding agent (Claude Code, Cursor, Codex, Windsurf, etc.) with linting and live preview.
## Get started
<Steps>
<Step title="Download the instruction file">
Open [`claude-design-hyperframes.md`](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/claude-design-hyperframes.md) on GitHub and click the download button (↓) to save it.
</Step>
<Step title="Open Claude Design">
Start a new chat at [claude.ai/design](https://claude.ai/design).
</Step>
<Step title="Attach the file + describe your video">
Drag the `claude-design-hyperframes.md` file into the chat. Describe what you want — include screenshots, brand assets, or a palette if you have them.
</Step>
<Step title="Download the ZIP">
Claude Design produces `index.html`, `preview.html`, `README.md`, and `DESIGN.md`. Download the ZIP.
</Step>
<Step title="Refine in any AI coding agent">
Open the project in Claude Code, Cursor, Codex, or any agent with terminal access for animation polish, timing, and production QA.
```bash
npx skills add heygen-com/hyperframes # install skills (one-time)
npx hyperframes lint # should pass with zero errors
npx hyperframes preview # open the studio
```
</Step>
</Steps>
<Tip>
**Attach the file, don't paste the URL.** Claude Design reads file attachments natively with detail preserved. URL-driven runs produce usable output but consistently miss more rules.
</Tip>
## Which setup to use
| Surface | Recommended setup |
| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| Claude Design | Download [`claude-design-hyperframes.md`](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/claude-design-hyperframes.md) from GitHub and attach to your chat |
| Claude Code | `npx skills add heygen-com/hyperframes`, then use `/hyperframes` |
| Cursor / Codex / Gemini CLI | `npx skills add heygen-com/hyperframes` |
## How it works
The instruction file gives Claude Design **pre-valid HTML skeletons** — the structural rules (data attributes, timeline registration, scene visibility, preview token forwarding) are already embedded. Claude Design fills in the creative work:
1. **Palette + typography** — CSS custom properties on `:root`
2. **Scene content** — text, images, layout inside `.scene-content` wrappers
3. **Animations** — GSAP entrance tweens and mid-scene activity
4. **Transitions** — hard cuts for most scenes, shader transitions at 2-3 key moments
This template-first approach means the output passes `npx hyperframes lint` with zero errors on first download — your coding agent can start refining immediately without structural fixes.
## Example prompts
<CardGroup cols={1}>
<Card title="Feature announcement">
```text
Use the attached file. I just shipped dark mode for my app. Make me a
15-second Instagram reel announcing it.
- App name: Taskflow
- Primary color: #6C5CE7
- The vibe is clean, minimal, dark
- Key stat: "47% of users requested this"
```
</Card>
<Card title="Founder pitch">
```text
Use the attached file. 25-second LinkedIn video for my startup.
Problem: Sales teams waste 3 hours/day on manual CRM updates.
Solution: AutoCRM — AI that logs every call, email, and meeting.
Traction: 200+ teams, $1.2M ARR, 18% MoM growth.
CTA: autocrmhq.com
Professional but not corporate. Think Linear or Vercel energy.
```
</Card>
<Card title="Stat highlight">
```text
Use the attached file. 10-second reel. Just one big number:
"$4.2 billion processed in Q1 2026"
Dark background, the number should animate up from zero. Subtle,
confident. End with logo placeholder and "stripe.com"
```
</Card>
<Card title="Sparse brief (let it ask)">
```text
Use the attached file. Make a 30-second launch video for Orbit.
```
The instructions tell Claude Design to ask ONE short clarifying question before generating.
</Card>
</CardGroup>
## What to include in your prompt
Claude Design reads inputs in this order of reliability: **attachments > pasted content > web research > URLs**.
| Input type | What it gives Claude Design |
| --- | --- |
| Screenshots / PDFs / brand guides | Palette, typography, UI patterns, tone — strongest source |
| Pasted hex codes, typefaces, copy | Authoritative for what it covers |
| Brand name (well-known) | Claude Design can research blogs, press, Wikipedia |
| SPA URL (React/Vue homepage) | Returns near-empty shell — pivot to blog/press instead |
The more specific your prompt, the better the output. Include palette, fonts, duration, and scene ideas when you have them.
## Known limitations
- **In-pane preview** — scrubbing is unreliable in Claude Design's iframe sandbox. Download and use `npx hyperframes preview` locally for reliable playback.
- **No linting** — Claude Design can't run `npx hyperframes lint`. The template-first skeletons handle structural validity, but the self-review checklist is the only QA before download.
- **Shaders work at any aspect ratio** — vertical (1080x1920), landscape (1920x1080), and square (1080x1080) all supported.
- **3 fetch limit** — Claude Design limits web fetches per turn. All critical rules are inlined; external references are for edge cases only.
- **Seeking backwards** — scrubbing backwards in the in-pane preview can show blank frames. Forward seeking usually works.
## The handoff to your coding agent
Claude Design's output is a valid first draft. Open it in Claude Code, Cursor, Codex, or any AI coding agent with terminal access:
```bash
npx skills add heygen-com/hyperframes # one-time setup
npx hyperframes lint # verify structure
npx hyperframes preview # open the studio
```
Then iterate:
- "Make scene 3's entrance snappier"
- "Add a counter animation to the stat in scene 5"
- "Tighten the pacing — scenes 4 and 6 feel too long"
- "Change the shader on transition 2 to glitch"
## Next steps
<CardGroup cols={2}>
<Card title="Prompt Guide" icon="message" href="/guides/prompting">
More prompt patterns for HyperFrames across Claude Code, Claude Design, and other agents.
</Card>
<Card title="@hyperframes/player" icon="play" href="/packages/player">
Embed compositions with the official player component.
</Card>
</CardGroup>
+224
View File
@@ -0,0 +1,224 @@
---
title: Color Grading
description: "Apply real-time color grading, presets, LUTs, vignette, grain, blur, and pixelate to video and image media in Studio and final renders."
---
HyperFrames Studio can color grade project-local `<video>` and `<img>` media directly in the preview. The same `data-color-grading` settings are used by the render pipeline, so the exported video should match the look you preview.
This is a lightweight media color tool for generated videos, uploaded footage, social variants, and agent-authored compositions. It is not a DaVinci Resolve, Premiere, ACES, or OCIO finishing pipeline.
## What Is New
| Capability | Status | Notes |
| --- | --- | --- |
| Studio Color Grading panel | Supported | Appears on selected `<video>` and `<img>` elements. |
| Manual controls | Supported | Exposure, contrast, highlights, shadows, white point, black point, warmth, tint, vibrance, saturation. |
| Presets | Supported | Named HyperFrames presets backed by shader settings, not bundled third-party LUT packs. |
| Custom LUT upload | Supported | Project-local 3D `.cube` LUT files with strength control. |
| Finishing | Supported | Vignette and grain, with advanced settings behind the settings icon. |
| Effects | Supported | Blur and pixelate on the selected media surface. |
| Before preview | Supported | Hold the compare button to temporarily show the ungraded media. |
| Render parity | Supported | The render pipeline redraws the color-grading shader after video-frame injection. |
Studio labels `whites`, `blacks`, and `temperature` as White Point, Black Point, and Warmth. Use the JSON keys shown in the data shape when authoring `data-color-grading` by hand or through an agent.
## Support Matrix
| Source / workflow | Supported? | What to expect |
| --- | --- | --- |
| 1080p SDR video | Yes | Best default path. Good for most uploaded/generated MP4/WebM/MOV media that browsers can decode. |
| 4K SDR video | Yes | Works when the browser and machine can decode it. Preview/render cost is higher. A 4K source only produces 4K output when the composition/render is also 4K. |
| 1080p / 4K images | Yes | Works on normal project-local images. Output resolution follows the element/composition render size, not hidden extra detail beyond that size. |
| iPhone SDR video | Yes | Treat as normal SDR media when it is tagged/decoded as SDR. |
| iPhone HDR / HLG / Dolby Vision-style uploads | Partial | The media can be loaded if browser/FFmpeg support the file, and Studio warns when HDR metadata is detected. The live Color Grading shader is still an SDR preview path, not true HDR grading. Use the existing [HDR Rendering](/guides/hdr) pipeline for HDR delivery and verify the output. |
| HDR render output | Related, not new | HyperFrames already has HDR render support. Color Grading does not yet provide HDR-aware grading controls. |
| LOG camera footage | Partial | Sliders and LUTs can be applied, but HyperFrames does not auto-detect camera LOG profiles or apply ACES/OCIO input transforms. Use a matching conversion/look LUT if you know the source profile. |
| Rec.709 creative LUTs | Yes | Best LUT path today. Use project-local 3D `.cube` files. |
| Camera conversion LUTs | Partial | Technically accepted if they are supported 3D `.cube` files, but correctness depends on the source footage matching the LUT's expected input color space. |
| Full-scene grading including text/DOM | Not yet | Color Grading is media-only. Captions, text, SVG, and regular DOM overlays stay unchanged. |
| Remote media URLs | Partial | WebGL pixel processing requires compatible CORS headers. Project-local assets are the reliable path. |
| Professional ACES/OCIO/HDR finishing | Not yet | Future render/color-management work, not this Studio shader path. |
## How It Works
Color grading is stored on media elements as `data-color-grading`:
```html index.html
<video
id="hero-video"
src="assets/hero.mp4"
data-start="0"
data-duration="6"
muted
playsinline
data-color-grading='{
"preset":"clean-studio",
"intensity":0.85,
"adjust":{
"exposure":0.05,
"contrast":0.08,
"highlights":-0.08,
"shadows":0.06,
"vibrance":0.04,
"saturation":0.04
},
"details":{
"vignette":0.08,
"vignetteFeather":0.72,
"grain":0.12,
"grainSize":0.25,
"grainRoughness":0.55
},
"effects":{
"blur":0.08
},
"colorSpace":"rec709"
}'
></video>
```
The runtime creates a sibling WebGL canvas for the media element, samples the current video or image frame, applies shader uniforms, then hides the native media only after a shader frame is ready.
<Note>
Color Grading is intentionally **media-only**. It applies to `<video>` and `<img>` sources. Captions, text, divs, SVG, and UI graphics remain ungraded unless you render them into media first.
</Note>
<Note>
Project-local media is the safest path. Remote media must be served with compatible CORS headers and should use `crossorigin="anonymous"` when pixel processing is needed.
</Note>
## Data Shape
```json
{
"preset": "natural-lift",
"intensity": 1,
"adjust": {
"exposure": 0,
"contrast": 0,
"highlights": 0,
"shadows": 0,
"whites": 0,
"blacks": 0,
"temperature": 0,
"tint": 0,
"vibrance": 0,
"saturation": 0
},
"details": {
"vignette": 0,
"vignetteMidpoint": 0.5,
"vignetteRoundness": 0,
"vignetteFeather": 0.65,
"grain": 0,
"grainSize": 0.25,
"grainRoughness": 0.5
},
"effects": {
"blur": 0,
"pixelate": 0
},
"lut": {
"src": "assets/luts/look.cube",
"intensity": 0.75
},
"colorSpace": "rec709"
}
```
Omit `enabled`; the presence of `data-color-grading` implies that grading is active. All numeric controls are clamped by the runtime. The current color grading path is Rec.709/sRGB-oriented and assumes browser-decoded media frames.
## Custom LUTs
HyperFrames supports project-local 3D `.cube` LUT files:
```html index.html
<img
src="assets/product.jpg"
data-color-grading='{
"lut":{"src":"assets/luts/product-pop.cube","intensity":0.7}
}'
/>
```
Use `.cube` LUTs when users already have a look from another editor or camera workflow.
- HyperFrames currently supports 3D `.cube` LUTs for this path.
- 3D cube LUTs up to `LUT_3D_SIZE 64` are supported.
- 1D `.cube` LUTs and mixed 1D+3D LUT files are not supported yet.
- Supported headers include common `DOMAIN_MIN` / `DOMAIN_MAX` and DaVinci/IRIDAS-style `LUT_3D_INPUT_RANGE`.
- LUTs are not universal. A LUT looks correct only when the source footage roughly matches the LUT's expected input color space.
- Rec.709 creative LUTs are the safest fit today.
- LOG/camera conversion LUTs can be used, but HyperFrames does not yet manage camera color profiles for you.
## Render Behavior
Color Grading is part of the media runtime, so render uses the same settings as Studio preview. During render, HyperFrames injects exact video frames and asks the color-grading runtime to redraw before capture.
For 4K output, use the existing [4K Rendering](/guides/4k-rendering) workflow. Color Grading can run at 4K when the composition/render surface is 4K, but a 1080p source video does not become sharper just because the final render is 4K.
For HDR output, use the existing [HDR Rendering](/guides/hdr) workflow. Color Grading currently warns on detected HDR media, but the grading controls themselves are not HDR-aware.
When grading a video, animate opacity on a wrapper element instead of directly on the `<video>` element. The runtime hides the native media and draws the graded result through a sibling canvas, so wrapper opacity preserves preview/render parity.
## What Belongs Where
| User wants | Use |
| --- | --- |
| Make uploaded footage look cleaner | Color Grading preset + adjust controls |
| Use a look from another editor | Custom 3D `.cube` LUT |
| Add polish to a product shot | Vignette, subtle grain, contrast, vibrance |
| Blur or pixelate selected media | Effects inside Color Grading |
| Make a presenter float over graphics | Existing [Remove Background](/guides/remove-background) workflow |
| Put text behind a presenter | Existing `remove-background --background-output` workflow |
| Render HDR delivery files | Existing [HDR Rendering](/guides/hdr) workflow |
| Render 4K | Existing [4K Rendering](/guides/4k-rendering) workflow |
| Remove a person and reconstruct the room | External video inpainting, not HyperFrames background removal |
| Green screen keying | Preprocess with FFmpeg `chromakey` or a future HyperFrames command |
| Grade every pixel in the full scene including captions/DOM | Future compositor or render post-process |
| Professional ACES/OCIO color pipeline | Future high-fidelity color-management pipeline |
## Agent-Friendly Examples
For AI agents, keep the instruction declarative:
```text
Apply a clean studio preset to assets/interview.mp4, reduce highlights slightly,
lift shadows, add subtle vignette, and keep captions ungraded.
```
Expected markup:
```html index.html
<video
id="interview"
src="assets/interview.mp4"
data-start="0"
data-duration="6"
muted
playsinline
data-color-grading='{
"preset":"clean-studio",
"intensity":0.8,
"adjust":{"highlights":-0.08,"shadows":0.08},
"details":{"vignette":0.08}
}'
></video>
```
## Related Guides
<CardGroup cols={2}>
<Card title="Remove Background" icon="scissors" href="/guides/remove-background">
Create transparent video/image cutouts for presenter and product overlays.
</Card>
<Card title="Rendering" icon="film" href="/guides/rendering#transparent-background">
Render transparent overlays and final MP4/WebM/MOV outputs.
</Card>
<Card title="HDR Rendering" icon="sun" href="/guides/hdr">
Render HDR10 outputs when your project uses HDR video or image sources.
</Card>
<Card title="4K Rendering" icon="up-right-and-down-left-from-center" href="/guides/4k-rendering">
Render at 4K and understand what supersampling does and does not improve.
</Card>
</CardGroup>
+289
View File
@@ -0,0 +1,289 @@
---
title: Common Mistakes
description: "Pitfalls that break Hyperframes compositions."
---
These are mistakes that cannot be caught by the linter. For automated checks, run `npx hyperframes lint` (see [CLI](/packages/cli#lint)).
<Warning>
The first two mistakes — animating video element dimensions and controlling media playback in scripts — are the most common causes of broken compositions. If your video looks wrong, check these first.
</Warning>
<AccordionGroup>
<Accordion title="Animating video element dimensions">
**Symptom:** Video frames stop updating, or browser performance drops severely.
**Cause:** GSAP animating `width`, `height`, `top`, `left` directly on a `<video>` element can cause the browser to stop rendering frames.
**Before (broken):**
```javascript index.html
// Animating the video element directly — causes frame rendering to stop
tl.to("#el-video", { width: 500, height: 280, top: 700, left: 1400 }, 26);
```
**After (fixed):**
```html index.html
<!-- Wrap the video in a div and animate the wrapper -->
<div id="pip-wrapper" style="position: absolute; width: 1920px; height: 1080px;">
<video id="el-video" data-start="0" data-track-index="0"
src="./assets/video.mp4" style="width: 100%; height: 100%;"></video>
</div>
```
```javascript index.html
// Animate the wrapper — the video fills it at 100%
tl.to("#pip-wrapper", { width: 500, height: 280, top: 700, left: 1400 }, 26);
```
Use a non-timed wrapper `<div>` for visual effects like picture-in-picture. Animate the wrapper; let the video fill it via CSS.
</Accordion>
<Accordion title="Controlling media playback in scripts">
**Symptom:** Audio/video playback is out of sync, or plays when it should not.
**Cause:** Calling `video.play()`, `video.pause()`, or setting `audio.currentTime` in your scripts. The [framework owns all media playback](/reference/html-schema#framework-managed-behavior).
**Before (broken):**
```javascript index.html
// Conflicts with framework media sync
document.getElementById("el-video").play();
document.getElementById("el-audio").currentTime = 5;
```
**After (fixed):**
```javascript index.html
// Don't control media playback at all. The framework handles it.
// Use GSAP for visual animations only:
tl.to("#el-video", { opacity: 1, duration: 0.5 }, 0);
```
The framework reads [`data-start`](/concepts/data-attributes#timing-attributes), [`data-media-start`](/concepts/data-attributes#media-attributes), and [`data-volume`](/concepts/data-attributes#media-attributes) to control when and how media plays. See [Compositions: Two Layers](/concepts/compositions#two-layers-primitives-and-scripts) for the separation between HTML primitives and scripts.
</Accordion>
<Accordion title="Composition duration shorter than video">
**Symptom:** Video plays for a few seconds then stops. Timeline shows 8-10 seconds even though the video is minutes long.
**Cause:** The composition duration equals the [GSAP timeline duration](/guides/gsap-animation#timeline-duration-and-composition-duration), not `data-duration` on the video. If your last GSAP animation ends at 8 seconds, the composition is 8 seconds long — regardless of how long the video source is.
**Before (broken):**
```javascript index.html
// Timeline is only 7.8s long — video cuts off after 7.8 seconds
tl.to("#lower-third", { left: -640, duration: 0.6 }, 7.2);
```
**After (fixed):**
```javascript index.html
tl.to("#lower-third", { left: -640, duration: 0.6 }, 7.2);
// Extend the timeline to 283 seconds to match the video length
tl.set({}, {}, 283);
```
`tl.set({}, {}, TIME)` adds a zero-duration tween at the specified time, extending the timeline without affecting any elements.
<Tip>
A quick check: run `npx hyperframes compositions` to see the resolved duration of each composition. If it is shorter than expected, your timeline needs extending.
</Tip>
</Accordion>
<Accordion title="Setting the root render length from a script or --variables does nothing">
**Symptom:** You set the **root** composition's `data-duration` from a script (or expose it as a variable and pass `--variables`), but the render length never changes. It stays locked at whatever `data-duration` is written in the source HTML.
**Cause:** The root composition's `data-duration` (the total render length / frame count) is read **once at compile time**, before your scripts run, exactly like `data-width` / `data-height`. A static root `data-duration` is locked at that point, so a later `root.setAttribute("data-duration", ...)` or a `--variables`-driven value is ignored. This is unlike a **clip's** `data-duration`, which the renderer re-reads from the live DOM after scripts run.
**Before (broken):**
```html index.html
<div id="root" data-composition-id="main" data-duration="2" data-width="1920" data-height="1080">
...
</div>
<script>
// Ignored: the root render length was already fixed at 2s at compile time.
document.getElementById("root").setAttribute("data-duration", "5");
</script>
```
**After (fixed):** author the root `data-duration` directly, one value per output. To produce a different length, author (or generate) a variant with that root `data-duration`. See [What can't be a variable](/concepts/variables#what-cant-be-a-variable).
<Tip>
Only the root composition's *total length* is compile-time-fixed. A clip's own `data-duration` (and a `<video>`/`<audio>` trim length) is re-read from the live DOM, so scripts and variables can still drive those, see [Swapping media: do you need to vary duration too?](/concepts/variables#swapping-media-do-you-need-to-vary-duration-too).
</Tip>
</Accordion>
<Accordion title="Missing class='clip' on timed elements">
**Symptom:** Elements are always visible, ignoring their `data-start` and `data-duration` timing.
**Cause:** The [`class="clip"`](/concepts/data-attributes#element-visibility) attribute tells the runtime to manage the element's visibility lifecycle. Without it, the element is always rendered.
**Before (broken):**
```html index.html
<!-- Missing class="clip" — this element is always visible -->
<h1 id="title" data-start="2" data-duration="5" data-track-index="0">
Hello World
</h1>
```
**After (fixed):**
```html index.html
<!-- With class="clip", the runtime shows this only from 2s to 7s -->
<h1 id="title" class="clip" data-start="2" data-duration="5" data-track-index="0">
Hello World
</h1>
```
<Note>
The linter catches this one: `npx hyperframes lint` will flag timed elements missing `class="clip"`.
</Note>
</Accordion>
<Accordion title="Oversized source images">
**Symptom:** Preview stutters during scenes with images on screen. Render is slower than expected.
**Cause:** Source images at much higher resolution than the canvas. Chrome decodes images to raw RGBA bitmaps before displaying them, and bitmap size is `width × height × 4` bytes — independent of file size on disk. A 7000×5000 JPEG is 140MB decoded, even if the file is only 2MB.
Displaying such an image in a 384×1080 region wastes memory and forces the compositor to resample a huge texture every frame.
**Before (bloated):**
```html index.html
<!-- 7000x5000 source, ~140MB decoded -->
<img class="clip" data-start="0" data-duration="3"
src="./assets/hero-scene.jpg" />
```
**After (sized to the canvas):**
```bash Terminal
# Resize a batch of images to fit within 3840x3840, preserving aspect ratio
mkdir -p assets/resized
mogrify -path assets/resized -resize 3840x3840\> assets/*.jpg
```
```html index.html
<!-- ~3840x2560 source, ~40MB decoded -->
<img class="clip" data-start="0" data-duration="3"
src="./assets/resized/hero-scene.jpg" />
```
**Rule of thumb:** source images at most 2x the canvas dimensions. For a 1920×1080 composition, 3840×2160 is already plenty. See [Performance: Image sizing](/guides/performance#image-sizing).
</Accordion>
<Accordion title="Heavy backdrop-filter stacks">
**Symptom:** Specific scenes drop to 5-10fps in preview. The composition is fine elsewhere.
**Cause:** `backdrop-filter: blur()` on large elements, especially stacked at high radii. Each blur layer forces the compositor to sample pixels behind the element, run a blur kernel, and composite the result. Stacked layers multiply the cost.
**Before (expensive):**
```css
/* 8 layers per side = 16 blur passes every frame */
.pb-1 { backdrop-filter: blur(1px); }
.pb-2 { backdrop-filter: blur(2px); }
.pb-3 { backdrop-filter: blur(4px); }
.pb-4 { backdrop-filter: blur(8px); }
.pb-5 { backdrop-filter: blur(16px); }
.pb-6 { backdrop-filter: blur(32px); }
.pb-7 { backdrop-filter: blur(64px); }
.pb-8 { backdrop-filter: blur(128px); }
```
**After (3 tuned layers):**
```css
/* Fewer passes with hand-picked radii — visually similar, much cheaper */
.pb-1 { backdrop-filter: blur(4px); }
.pb-2 { backdrop-filter: blur(16px); }
.pb-3 { backdrop-filter: blur(48px); }
```
**Guidelines:**
- Keep stacked `backdrop-filter` layers to 2-3 per region
- Avoid radii above 64px over large areas — the biggest radii dominate the total cost
- For a static blur effect, pre-render it into a PNG once and overlay with a regular `<img>`
See [Performance: backdrop-filter: blur()](/guides/performance#backdrop-filter-blur) for the full breakdown.
</Accordion>
<Accordion title="Expected HDR output but got SDR">
**Symptom:** Expected an HDR render, but the output looks the same as SDR or `ffprobe` reports `color_transfer=bt709`.
**Cause:** By default, Hyperframes only switches to HDR encoding when a source `<video>` or `<img>` is tagged with BT.2020 / PQ / HLG color metadata. Common reasons HDR is not engaged:
1. **All sources are SDR.** Auto-detect leaves SDR-only compositions in SDR. Verify with `ffprobe`:
```bash Terminal
ffprobe -v error -show_streams source.mp4 | grep color_transfer
# Want: smpte2084 (PQ) or arib-std-b67 (HLG)
# SDR: bt709, smpte170m, bt470bg, etc.
```
2. **Wrong output format.** HDR output requires MP4. `--format mov` and `--format webm` fall back to SDR — Hyperframes logs a warning when this happens.
3. **SDR was forced.** `--sdr` disables HDR even when HDR sources are present.
If you need HDR regardless of source metadata, use `--hdr` to force it.
`--docker` works the same as local rendering — auto-detect, `--hdr`, and `--sdr` are all forwarded into the container and produce the same output decisions (slower, since the container falls back to software WebGL for SDR DOM capture).
See [HDR Rendering](/guides/hdr) for the full source requirements and verification steps.
</Accordion>
<Accordion title="Timeline key doesn't match data-composition-id">
**Symptom:** Animations don't play. The composition appears static.
**Cause:** The key used in `window.__timelines` must exactly match the [`data-composition-id`](/concepts/data-attributes#composition-attributes) attribute on the composition root element.
**Before (broken):**
```javascript index.html
// Mismatch: HTML says "my-video", script registers "root"
// <div data-composition-id="my-video" ...>
window.__timelines["root"] = tl;
```
**After (fixed):**
```javascript index.html
// Key matches the data-composition-id attribute
// <div data-composition-id="my-video" ...>
window.__timelines["my-video"] = tl;
```
</Accordion>
</AccordionGroup>
## Debugging Checklist
When something does not work, check in this order:
1. **Run the linter:** `npx hyperframes lint` — catches most structural issues
2. **Timeline registered?** Is `window.__timelines["<id>"]` set? Does the key match [`data-composition-id`](/concepts/data-attributes#composition-attributes)?
3. **GSAP-only animations?** Only animate visual properties (opacity, transform, color) — see [GSAP Animation](/guides/gsap-animation#key-rules)
4. **Timeline long enough?** Add `tl.set({}, {}, DURATION)` at the end — see [Timeline Duration](/guides/gsap-animation#timeline-duration-and-composition-duration)
5. **Console errors?** Open browser console — runtime errors show as `[Browser:ERROR]`
6. **Still stuck?** See [Troubleshooting](/guides/troubleshooting) for environment and rendering issues
## Next Steps
<CardGroup cols={2}>
<Card title="Troubleshooting" icon="wrench" href="/guides/troubleshooting">
Fix environment and rendering issues
</Card>
<Card title="GSAP Animation" icon="wand-magic-sparkles" href="/guides/gsap-animation">
Review animation rules and patterns
</Card>
<Card title="HTML Schema Reference" icon="code" href="/reference/html-schema">
Full attribute reference and checklist
</Card>
<Card title="Data Attributes" icon="database" href="/concepts/data-attributes">
Timing, media, and composition attributes
</Card>
</CardGroup>
+190
View File
@@ -0,0 +1,190 @@
---
title: GitHub Copilot CLI
description: "Set up HyperFrames with GitHub Copilot CLI — install skills, invoke them with slash commands, and render video from the terminal."
---
GitHub Copilot CLI brings AI-powered coding assistance to your terminal. HyperFrames skills teach it how to write correct compositions, GSAP timelines, and framework-specific patterns — so you get valid video HTML without reading the docs yourself.
## Prerequisites
- **GitHub Copilot subscription** — Free, Pro, Pro+, Business, or Enterprise all include CLI access
- **Copilot CLI installed** — `npm install -g @github/copilot` (or via the [install script](https://github.com/github/copilot-cli))
- **Node.js 22+** and **FFmpeg** for the HyperFrames CLI
## Install skills
Install the HyperFrames skill package into your project:
```bash
npx skills add heygen-com/hyperframes
```
This writes skill directories to `.agents/skills/` in your project — the path Copilot CLI scans automatically for workspace-scoped skills.
<Tip>
To make skills available across all your projects, install them globally:
```bash
npx skills add heygen-com/hyperframes --agent github-copilot --global
```
Global skills live in `~/.copilot/skills/` and load in every Copilot CLI session.
</Tip>
If you install skills during an active session, run `/skills` to open the skills picker and verify they appear. New skills installed to the project directory are picked up automatically on the next prompt.
## Using skills
Copilot CLI supports both **explicit invocation** via slash commands and **automatic detection** based on your prompt.
### Slash commands
Prefix a skill name with `/` to load it explicitly:
```
/hyperframes Create a 10-second product intro with a fade-in title and dark background.
```
```
/hyperframes-animation Add a scale-pop animation to the title element.
```
```
/hyperframes-cli How do I render at 60fps with Docker?
```
### Auto-detection
Copilot also matches skills based on the `description` field in each `SKILL.md`. If your prompt mentions compositions, timelines, or video rendering, the agent loads the right skills without you specifying them:
```
Create a 9:16 TikTok hook video about AI productivity with bouncy captions.
```
Explicit invocation is more reliable when you have many skills installed — with large skill sets, the agent's token budget may not fit every skill description, so naming the skill directly guarantees it loads.
### Skill management
| Command | What it does |
|---|---|
| `/skills` | Open the interactive skills picker — browse, enable, or disable skills |
| `/skills add <path>` | Add an additional skill directory to the current session |
## Create and preview a video
<Steps>
<Step title="Scaffold a project">
```bash
npx hyperframes init my-video
cd my-video
```
Skills are installed automatically inside the new project.
</Step>
<Step title="Start the preview server">
```bash
npx hyperframes preview
```
Opens the HyperFrames Studio in your browser. Edits reload automatically.
</Step>
<Step title="Start Copilot CLI in the project directory">
In a second terminal:
```bash
copilot
```
</Step>
<Step title="Prompt with the skill">
```
/hyperframes Create a 15-second dark-themed product intro with hype-style
captions and a flash transition to the CTA.
```
The agent writes valid HyperFrames HTML — `data-*` attributes, `class="clip"` on timed elements, paused GSAP timelines registered on `window.__timelines`. The preview updates as files are saved.
</Step>
<Step title="Iterate">
Keep prompting without re-specifying the full context:
```
Make the title 2x bigger and add a lower third at 0:03.
```
```
Swap the transition to a whip pan.
```
</Step>
<Step title="Render">
```bash
npx hyperframes render --output output.mp4
```
Or ask the agent:
```
/hyperframes-cli Render this composition to output.mp4 at high quality.
```
</Step>
</Steps>
## Agent mode
Copilot CLI's agent mode can handle multi-step tasks autonomously — scaffolding a project, writing the composition, installing registry blocks, and rendering in sequence:
```
/hyperframes Scaffold a new project called "launch-video", create a 30-second
product launch video with 5 scenes, install the flash-through-white transition
block, and render to mp4.
```
In agent mode, Copilot runs terminal commands (like `npx hyperframes init` and `npx hyperframes render`) on your behalf. Review the commands before approving them — especially if the skill pre-approves `shell` in its `allowed-tools`.
<Warning>
Only pre-approve `shell` or `bash` in skill `allowed-tools` for skills you trust. The HyperFrames skills do not pre-approve shell access — the agent will ask for confirmation before running terminal commands.
</Warning>
## MCP alternative
Copilot CLI also supports MCP servers for cloud-based authoring without the local CLI. Use the `--additional-mcp-config` flag to add the HyperFrames MCP server to your session:
```bash
copilot --additional-mcp-config '{"mcpServers":{"hyperframes":{"url":"https://mcp.heygen.com/mcp/hyperframes"}}}'
```
Or save the config to a file and reference it:
```bash
copilot --additional-mcp-config @mcp-config.json
```
Authorize via OAuth when prompted. See the [MCP guide](/guides/mcp) for full details.
## Tips
- **Always start with `/hyperframes`.** It's the entry skill — it orients you to the whole surface and routes your request to the right workflow, pulling in the domain skills (`/hyperframes-core` for composition rules and data attributes, `/hyperframes-animation` for GSAP and other runtime patterns) that generic web knowledge doesn't cover.
- **Use `/skills` to check skill status.** If output looks wrong, open the skills picker to verify the HyperFrames skills are enabled.
- **Run `npx hyperframes lint` before rendering.** The linter catches structural issues the agent might miss on complex multi-scene edits.
- **Keep the preview server running in a separate terminal.** You see every edit reflected in real time while prompting in Copilot CLI.
- **Install registry blocks for transitions and effects.** `npx hyperframes add shimmer-sweep` installs pre-built blocks, then prompt the agent to wire them in.
## Next steps
<CardGroup cols={2}>
<Card title="Prompting guide" icon="message" href="/guides/prompting">
Vocabulary and patterns that produce better compositions.
</Card>
<Card title="Catalog" icon="grid-2" href="/catalog/blocks/data-chart">
50+ ready-to-use blocks the agent can install and wire.
</Card>
<Card title="GSAP Animation" icon="wand-magic-sparkles" href="/guides/gsap-animation">
Motion principles and timeline authoring.
</Card>
<Card title="Pipeline guide" icon="list-check" href="/guides/pipeline">
The 7-step structure agents follow for multi-beat videos.
</Card>
</CardGroup>
+184
View File
@@ -0,0 +1,184 @@
---
title: Deploy
description: "Run a Hyperframes preview + render API in the cloud from an official template."
---
Hyperframes ships three official deployment templates that wrap a composition in a small web app: an in-browser preview and a `/api/render` endpoint that produces an MP4 server-side. All are open source, Apache-2.0 — Vercel and Cloudflare deploy from a single button, Modal from a single `modal deploy`.
| Template | Compute | Storage | Deploy |
|----------|---------|---------|--------|
| [Vercel](https://github.com/heygen-com/hyperframes-vercel-template) | [Vercel Sandbox](https://vercel.com/docs/vercel-sandbox) (Firecracker microVM) | [Vercel Blob](https://vercel.com/docs/vercel-blob) | [vercel.com/templates/ai/hyperframes-on-vercel](https://vercel.com/templates/ai/hyperframes-on-vercel) |
| [Cloudflare](https://github.com/heygen-com/hyperframes-cloudflare-template) | [Cloudflare Containers](https://developers.cloudflare.com/containers/) (Workers + Durable Object) | [R2](https://developers.cloudflare.com/r2/) | One-click button in the repo README |
| [Modal](https://github.com/heygen-com/hyperframes-modal-template) | [Modal Functions](https://modal.com/docs/guide/webhooks) (serverless containers, scale-to-zero) | [Modal Volume](https://modal.com/docs/guide/volumes) | `modal deploy src/app.py` |
All three templates use the same shape:
- **Preview** the bundled `ui-3d-reveal` composition in the browser via the [`<hyperframes-player>`](/packages/player) web component.
- **Render** to MP4 by POSTing to `/api/render`. The handler ships the composition to a sandboxed runtime that has Chromium, FFmpeg, and `hyperframes` pre-installed, then streams the MP4 back to storage and returns a URL.
- **Author locally**, deploy the preview + render API. Compositions are still built on your machine with `npx hyperframes init`, then dropped into the template's compositions directory.
## Choosing a template
<Tabs>
<Tab title="Vercel">
Pick this if you already deploy on Vercel, want zero-config Blob storage, or want to reuse Vercel's CI/preview environments.
[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fheygen-com%2Fhyperframes-vercel-template&stores=%5B%7B%22type%22%3A%22blob%22%2C%22access%22%3A%22public%22%7D%5D)
**What you get**
- A Next.js app with `<hyperframes-player>` preview and a `POST /api/render` route.
- A pre-baked Vercel Sandbox snapshot built during `next build` — cold renders skip the Chromium/FFmpeg install and restore from snapshot in ~100 ms.
- A Vercel Blob store provisioned automatically on deploy. `BLOB_READ_WRITE_TOKEN` is injected for you.
**Performance**
Renders run on `standard-4` (4 vCPU). With `--workers auto`, three parallel Chrome workers cut render time meaningfully vs. single-worker. Concrete render time depends on composition length, complexity, and asset size.
**Pricing**
Vercel Pro plans include Sandbox credit each month. See [Vercel Sandbox pricing](https://vercel.com/docs/vercel-sandbox/pricing) for current per-vCPU and per-GB rates and the up-to-date credit allowance.
<Note>
Vercel Functions cap at 300s and a 50 MB compressed bundle, which can't fit Chromium + FFmpeg. The template uses Vercel Sandbox specifically because it's the purpose-built primitive for this workload — up to 5 hours of runtime and up to 8 vCPUs per render.
</Note>
</Tab>
<Tab title="Cloudflare">
Pick this if you're already on Cloudflare Workers, want R2's free egress, or want full control over the renderer image.
[![Deploy to Cloudflare](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/heygen-com/hyperframes-cloudflare-template)
**What you get**
- A Worker that serves preview HTML and forwards `/api/render` requests to a `RenderContainer` Durable Object.
- A pre-built OCI container image with Chromium + FFmpeg + `hyperframes` baked in — no install at request time.
- An R2 bucket (`hyperframes-renders`) provisioned automatically on deploy.
**Performance**
Renders run on `standard-4` (4 vCPU, 12 GiB). With `--workers auto`, three parallel Chrome workers cut render time meaningfully vs. single-worker. Container instances sleep after 10 minutes of inactivity, so the next request after a quiet period pays a cold-start penalty.
**Pricing**
Cloudflare Containers bills per-10ms for memory, CPU, and disk; R2 storage has no egress within Cloudflare's network. Requires a [Workers Paid](https://developers.cloudflare.com/workers/platform/pricing/) plan. See [Cloudflare Containers pricing](https://developers.cloudflare.com/containers/pricing/) and [R2 pricing](https://developers.cloudflare.com/r2/pricing/) for current rates.
<Note>
Cloudflare's hosted [Browser Rendering](https://developers.cloudflare.com/browser-rendering/) API can't install FFmpeg — that's why the template uses Cloudflare Containers, which gives you a real OCI container in a Worker-bound Durable Object with up to 4 vCPUs and 12 GiB RAM.
</Note>
</Tab>
<Tab title="Modal">
Pick this if you already run on Modal, want containers that scale to zero with per-second billing, or want the render to run as a spawned function decoupled from the request.
Modal deploys from the CLI rather than a button:
```bash Terminal
uv sync
source .venv/bin/activate
modal setup # one-time auth
modal deploy src/app.py
```
**What you get**
- A FastAPI app (`@modal.asgi_app`) that serves the `<hyperframes-player>` preview and a `POST /api/render` route.
- A Modal image with Node.js 22, Python 3.12, FFmpeg, `hyperframes`, and `chrome-headless-shell` baked in at build time — no install at request time.
- A Modal Volume (`hyperframes-renders`) that persists the MP4, served back from the `/renders/:name` endpoint.
**Performance**
First deploy takes ~2 min to build the image; subsequent deploys are ~2s. Renders cold-boot in ~1s and containers scale to zero when idle. With `--workers auto`, three parallel Chrome workers cut render time (GPU is disabled via `--no-browser-gpu`). A 12s 1080p30 composition renders in roughly 1090s depending on complexity.
**Pricing**
You pay per second of render time and nothing while idle, since containers scale to zero. See [Modal pricing](https://modal.com/pricing) for current per-CPU and per-GB rates.
<Note>
Modal web endpoints cap at 150s per request, so `POST /api/render` returns a `call_id` immediately and the render runs in a spawned Modal Function (up to 15 min, `timeout=900`). Poll `GET /api/render/:id` — it returns `202` while the render is still running, then the finished MP4.
</Note>
</Tab>
</Tabs>
## Architecture
All three templates follow the same flow: the browser plays a preview locally, then POSTs to a render endpoint that delegates to a sandboxed runtime with Chromium + FFmpeg.
```
Browser Edge / Function Sandboxed renderer
┌──────────────────┐ ┌────────────────────┐ ┌──────────────────────────┐
│ <hyperframes- │ ────▶ │ /api/render │ ────▶ │ hyperframes render │
│ player> │ │ ship composition │ │ (Chromium + FFmpeg, │
│ preview iframe │ │ → renderer │ │ pre-installed) │
│ │ ◀──── │ ← stream MP4 │ ◀──── │ │
│ │ url │ → upload to blob │ mp4 │ │
└──────────────────┘ └────────────────────┘ └──────────────────────────┘
└─▶ Vercel Blob / Cloudflare R2 / Modal Volume
```
The key cost-saver in all three templates is **pre-baking the renderer**. Installing Chromium system libraries plus `chrome-headless-shell` takes 3060s, which would dominate every cold render. Vercel's template snapshots the sandbox at build time; Cloudflare's and Modal's templates bake everything into the container image. All restore in milliseconds and let you spend the entire request budget on actual rendering.
## Swapping the composition
All three templates ship with one bundled composition (`ui-3d-reveal`). To use your own:
<Steps>
<Step title="Author locally">
Compositions are HTML — author them on your machine with the [CLI](/packages/cli):
```bash Terminal
npx hyperframes init my-video
cd my-video
npx hyperframes preview
```
</Step>
<Step title="Drop the bundle into the template">
Copy your composition into the template's compositions directory — `public/compositions/<your-name>/` for Vercel and Cloudflare, `compositions/<your-name>/` for Modal.
</Step>
<Step title="Point the template at it">
- **Vercel**: edit `PREVIEW_COMPOSITION_DIR` at the top of `lib/preview.ts` and the dimensions in `app/page.tsx` if it isn't 1920×1080.
- **Cloudflare**: set `PREVIEW_COMPOSITION_DIR=compositions/<your-name>` when running `npm run deploy`, or edit the default in `scripts/build.mjs`. Update player dimensions in `public/index.html` if needed.
- **Modal**: set `PREVIEW_COMPOSITION = "<your-name>"` in `src/app.py`. Update the `<hyperframes-player>` dimensions in `web/index.html` if it isn't 1920×1080.
</Step>
<Step title="Deploy">
```bash Terminal
# Vercel
vercel deploy
# Cloudflare
npm run deploy
# Modal
modal deploy src/app.py
```
</Step>
</Steps>
## When to use a template vs. roll your own
Templates are optimized for **a single render endpoint behind a preview UI**. They're the fastest way to get a hosted Hyperframes render API running. If you need:
- A **render queue** with retries, deduplication, or priorities — start from a template, then add your own queue (e.g. Vercel Queues, Cloudflare Queues, SQS).
- **Multi-tenant rendering** with per-user composition uploads — start from a template, replace the bundled composition with a runtime-fetched one.
- **Self-hosted** rendering — see the [Rendering guide](/guides/rendering) and run `hyperframes render --docker` on your own infrastructure.
For everything else, the templates are the recommended starting point.
## Next Steps
<CardGroup cols={2}>
<Card title="Rendering" icon="film" href="/guides/rendering">
Render compositions locally or in Docker
</Card>
<Card title="Player package" icon="play" href="/packages/player">
Embed `<hyperframes-player>` in any HTML page
</Card>
<Card title="Vercel template" icon="github" href="https://github.com/heygen-com/hyperframes-vercel-template">
Source on GitHub
</Card>
<Card title="Cloudflare template" icon="github" href="https://github.com/heygen-com/hyperframes-cloudflare-template">
Source on GitHub
</Card>
<Card title="Modal template" icon="github" href="https://github.com/heygen-com/hyperframes-modal-template">
Source on GitHub
</Card>
</CardGroup>
+238
View File
@@ -0,0 +1,238 @@
---
title: Feedback Collection
description: "How HyperFrames collects feedback, what data is collected, and how to opt out."
---
HyperFrames occasionally asks how a render went or how a Studio session felt. This page explains why we do it, when prompts appear, what data is collected, and how to disable them.
## Why We Ask
We use anonymous satisfaction scores to understand whether the tool is actually working well — not just whether it runs without errors. A render that takes 10 minutes and produces a broken file counts as a success in logs but a failure in practice. The feedback prompt is the only signal we have for that gap.
No account, email, or identity is tied to responses. Each installation generates a random UUID at setup; that is the only identifier.
## How It Works
### CLI — post-render prompt
After a successful `hyperframes render`, a short prompt may appear:
```
How was this render? [1=poor 5=great, enter to skip]
Any details? (enter to skip)
```
**When it shows:**
- First ever successful render
- Then every 15 renders after that (16th, 31st, 46th...)
- At most once per process — re-renders in the same session don't trigger a second prompt
- Automatically suppressed in quiet mode (`--quiet`), non-TTY shells, and CI environments
The prompt has a **10-second auto-timeout** — if you don't respond, it silently disappears and the CLI continues normally.
The render interval is configurable:
```bash
# Show prompt every 5 renders instead of 15 (useful for testing)
HYPERFRAMES_FEEDBACK_INTERVAL=5 hyperframes render --output out.mp4
```
### Studio — session feedback bar
A thin 32px bar slides in at the bottom of the preview area periodically:
- Never on the first session — only starting from the 10th
- Then every 10 sessions (10th, 20th, 30th...)
- Slides in 3 seconds after page load to avoid flash
- Auto-dismisses after 20 seconds if ignored
- After any interaction (dismiss or submit), the session counter resets — next prompt after 10 more sessions
The session interval is configurable at build time:
```
VITE_HYPERFRAMES_FEEDBACK_INTERVAL=3
```
Invalid values (non-integer, zero, negative) fall back to the default.
**Note:** Disabling CLI telemetry (`hyperframes telemetry disable`) does not suppress the Studio bar — Studio feedback is gated separately. The submitted data is still sent through the same anonymous PostHog pipeline, so the same privacy guarantees apply.
### `hyperframes feedback` command
You can submit feedback manually at any time:
```bash
# Quick rating
hyperframes feedback --rating 5
# Rating with details
hyperframes feedback --rating 3 --comment "render succeeded but GSAP timeline didn't animate text overlay"
```
| Flag | Description |
|------|-------------|
| `--rating` | Satisfaction score, 15 (required) |
| `--comment` | Optional free-text details |
| `--file-issue` | Also open a pre-filled GitHub issue with a published minimal repro (opt-in) |
| `--dir` | Project directory to publish as the repro (default: current directory) |
| `--yes` | Skip the publish + file-issue consent prompt (for scripts) |
This command collects a doctor summary automatically, flushes telemetry, and exits. It appears under the **Settings** group in `hyperframes --help`.
### Filing a GitHub issue (`--file-issue`)
When a render misbehaves, add `--file-issue` so maintainers can reproduce it:
```bash
hyperframes feedback --rating 2 --comment "GSAP timeline froze on seek" --file-issue
```
This is **opt-in** and **consented**. With `--file-issue` set, after the usual feedback is sent the CLI:
1. Asks you to confirm (interactive) or requires `--yes` in non-interactive shells, because it will **publicly publish** the project at `--dir`.
2. Publishes a minimal repro of the project and gets a public URL (the same upload as `hyperframes publish`).
3. Opens your browser with a **pre-filled** GitHub issue draft, labelled `bug`, containing the rating, your comment, the public repro link, and the environment summary. It also prints the URL so you can copy it if no browser opens.
The issue is **not auto-submitted**: you review and file it under your own GitHub account. There is no token, backend, or `gh` invocation; if publishing fails the issue still opens, just without a repro link.
## Agent Runtimes
When an AI agent is detected, HyperFrames **skips the interactive readline prompt** and prints a structured hint instead:
```
[hyperframes] Agent feedback: hyperframes feedback --rating <1-5> --comment "..."
```
Agents can then submit feedback using the `hyperframes feedback` command above.
The same cadence gate applies: the hint only appears on the first render, then every 15th.
**Detected agents and their markers:**
| Agent | Environment markers |
|-------|---------------------|
| Claude Code | `CLAUDECODE` present, or `CLAUDE_CODE_ENTRYPOINT` present |
| Codex | `CODEX_THREAD_ID`, `CODEX_CI`, or `CODEX_SANDBOX_NETWORK_DISABLED` present |
| Cursor | `TERM_PROGRAM` equals `cursor` |
| GitHub Copilot Agent | `GITHUB_ACTIONS` equals `true` and (`COPILOT_AGENT_ID` present or `RUNNER_NAME` equals `Copilot`) |
| Replit | `REPL_ID` or `REPLIT_USER` present |
| Hermes | `HERMES_QUIET` present |
| openclaw | `OPENCLAW_STATE_DIR` or `OPENCLAW_CONFIG_PATH` present |
| Pi | `PI_CODING_AGENT` present |
Only the existence (or in some cases the value) of these variables is checked — API keys and secrets that happen to share a prefix are never read.
## What Is Collected
### CLI feedback
| Field | Value |
|-------|-------|
| `$survey_id` | `render_satisfaction` |
| `$survey_response` | Rating (15) |
| `$survey_response_2` | Free-text comment (only when provided) |
| `render_duration_ms` | Time the render took in milliseconds |
| `doctor_summary` | System context (see below) |
The `doctor_summary` is a compact string with environment context — included automatically so you don't need to run `hyperframes doctor` when reporting a problem:
```
os=darwin/arm64 node=v22.11.0 cpu=10cores mem=32GB ffmpeg=yes
```
It may also include `wsl` or sandbox runtime flags when those environments are detected.
### Studio feedback
| Field | Value |
|-------|-------|
| `$survey_id` | `studio_experience` |
| `$survey_response` | Rating (15) |
| `$survey_response_2` | Free-text comment (only when provided) |
| `source` | `studio` |
| `doctor_summary` | Browser context (platform, screen, CPU cores, device memory, network type) |
## What Is NOT Collected
- File paths or project names
- Composition content, HTML, or video files
- Environment variable values
- Personally identifiable information
- IP addresses or precise location
Feedback is anonymous. Each installation has a random UUID (`anonymousId`) — there is no account, login, or email association.
## Config File
The CLI persists feedback state in `~/.hyperframes/config.json`:
```json
{
"telemetryEnabled": true,
"anonymousId": "a1b2c3d4-...",
"telemetryNoticeShown": true,
"commandCount": 47,
"renderSuccessCount": 14,
"lastFeedbackPromptAt": 1
}
```
| Field | Description |
|-------|-------------|
| `renderSuccessCount` | Total successful renders across all sessions |
| `lastFeedbackPromptAt` | The `renderSuccessCount` value when the prompt last appeared — used to compute whether 15 renders have passed |
Studio stores the equivalent state in `localStorage` under the `hyperframes-studio:` prefix.
## Opting Out
### CLI — disable telemetry entirely
Disabling telemetry suppresses the CLI feedback prompt and all other CLI usage tracking:
```bash
# Via CLI command (persisted to ~/.hyperframes/config.json)
hyperframes telemetry disable
# Or via environment variable (per-session)
HYPERFRAMES_NO_TELEMETRY=1 hyperframes render --output out.mp4
# Or via DO_NOT_TRACK (respects the global standard)
DO_NOT_TRACK=1 hyperframes render --output out.mp4
```
Once disabled, the `hyperframes feedback` command will print `Telemetry is disabled. Feedback not sent.` and exit without sending anything.
### CLI — suppress output without disabling telemetry
```bash
# Quiet mode: renders without any post-render output (including the feedback prompt)
hyperframes render --quiet --output out.mp4
```
### CI environments
The CLI feedback prompt is automatically suppressed when the `CI` environment variable is set (GitHub Actions, CircleCI, etc. set this by default).
### Studio
The Studio feedback bar is not affected by the CLI telemetry setting. To disable it at build time, set:
```
VITE_HYPERFRAMES_NO_FEEDBACK=1
```
When set to `"1"`, the bar never shows — `shouldShowFeedback()` returns `false` unconditionally regardless of session count or `localStorage` state.
The session interval is still configurable independently:
```
VITE_HYPERFRAMES_FEEDBACK_INTERVAL=20
```
## Related
- [Telemetry](/packages/cli#telemetry) — full telemetry settings and what usage data is collected
- [`hyperframes feedback`](/packages/cli#feedback) — CLI command reference
- [Troubleshooting](/guides/troubleshooting) — if a prompt is blocking your pipeline unexpectedly
+131
View File
@@ -0,0 +1,131 @@
---
title: Figma Import
description: "Bring Figma designs into HyperFrames — frozen assets, brand tokens, editable components, storyboard reconstruction, and Figma Motion timelines translated to GSAP."
---
The work your designer already did in Figma — layout, color, type, motion — becomes the starting point of a composition instead of a thing to rebuild by hand. Point at a Figma URL; the artifact lands as a native HyperFrames piece: a frozen local file, a composition variable, editable HTML, or a paused GSAP timeline.
## What you can import
| Capability | What you get | Surface |
| --- | --- | --- |
| **Static assets** | A frame/layer rendered to SVG/PNG/JPG/PDF, frozen under `.media/` | `hyperframes figma asset` |
| **Brand tokens** | Figma variables/styles as composition brand variables | `hyperframes figma tokens` (Enterprise) or the `/figma` skill via MCP (any plan) |
| **Components** | A frame as editable HTML with brand-linked colors | `hyperframes figma component` |
| **Motion** | A Figma Motion timeline as an editable, paused GSAP timeline | `/figma` skill (agent, MCP) |
| **Shaders** | A shader fill/effect as a frozen still or clip | `/figma` skill (agent, MCP) |
| **Storyboards** | Scene frames reconstructed as motion — frames read as states, not slides | `/figma` skill (agent) |
Two transports, split by what Figma exposes: assets, tokens, and components run over the **REST API** (headless, works in CI, generous per-minute rate limits). Motion and shaders exist only on Figma's **MCP server**, so an agent drives those. Every path freezes files locally — **renders never call Figma**.
## One-time setup
There are two credentials, and most people only need to set up one to start:
| You want to import… | Set up |
| --- | --- |
| A logo, image, or a whole frame as HTML (assets, components) | A **token** — Step A below |
| Brand colors (tokens) | Either works, but on a non-Enterprise plan the **MCP connector** (Step B) gets you there in one click — the token path needs an Enterprise plan for this specific pull |
| Motion, shaders, or a storyboard | The **MCP connector** only — no token, no setup beyond connecting it |
Do both if your project needs everything; each is independent, so it doesn't matter which you set up first.
### Step A — Figma token (assets, tokens, components)
Needed for anything you run from the `hyperframes figma` CLI.
<Steps>
<Step title="Mint a token">
In Figma: **Settings → Security → Personal access tokens → Generate new token.**
</Step>
<Step title="Check these scopes">
Read-only is all it ever needs — the integration never writes to Figma. **On most accounts (not Figma Enterprise), check exactly these three:**
- **File content** — Read-only
- **File metadata** — Read-only
- **Library content** — Read-only — easy to miss, and without it `tokens` 403s the moment it tries the published-styles fallback
On a **Figma Enterprise** plan, also check **Variables — Read-only** to pull brand colors directly via `tokens`. Not on Enterprise? Skip it — `tokens` falls back to published styles automatically, or use the MCP connector (Step B) instead, which reaches variables on any plan.
</Step>
<Step title="Export it">
```bash
export FIGMA_TOKEN="figd_…"
```
Add the line to your shell profile or the project's `.env` so future sessions skip this step. The same token covers every Figma file your account can view.
</Step>
</Steps>
### Step B — Figma MCP connector (motion, shaders, storyboards — and an easier token-free path to brand colors)
No token, no scopes to pick — connect it once when your agent asks (a one-click OAuth) and it stays connected.
This is also the easiest way to pull brand colors on **any** Figma plan, including free: the connector's variable-reading tool isn't Enterprise-gated, only rate-limited by plan — Starter/free caps at **6 calls/month**, a paid Full/Dev seat gets 200600/day. Fine for an occasional brand pull; not for iterating call-by-call.
## Import an asset
```bash
hyperframes figma asset 'https://www.figma.com/design/KEY/Title?node-id=1-2'
```
The node renders over REST, lands frozen under `.media/images/`, and the command prints a ready-to-paste `<img>` snippet:
```text
imported image_007 → .media/images/image_007.svg
<img src=".media/images/image_007.svg" alt="image_007" data-figma-id="1:2" />
```
- `--format svg|png|jpg|pdf` (default `svg`). SVG for logos and vectors — scalable and animatable. `--format png --scale 2` for raster fidelity.
- Accepted refs: a full Figma URL with `?node-id=…` (right-click a layer → Copy link) or `fileKey:nodeId` shorthand. Asset and component imports always target a specific node; only `tokens` takes a bare `fileKey`.
- Idempotent: the manifest records `fileKey:nodeId:format:scale:version`, so re-running reuses the file unless the design actually changed in Figma.
## Pull your brand
```bash
hyperframes figma tokens KEY
```
Reads the file's variables (or published styles), writes a `figma-tokens.json` sidecar plus a binding index, and prints entries for the composition's `data-composition-variables`. Every scene that references a brand role — instead of a hard-coded hex — is on-brand automatically, and stays on-brand when the file changes.
<Tip>
Import tokens **before** components. That's what lets an imported component's colors link to your brand variables instead of baking duplicate literals.
</Tip>
## Import a component
```bash
hyperframes figma component 'https://www.figma.com/design/KEY/Title?node-id=10-20'
```
The frame's node tree becomes editable HTML at exact Figma geometry, packaged under `compositions/components/<name>/`. Vector and boolean-op nodes that don't map to clean HTML auto-rasterize through the asset path.
Colors bound to a Figma variable resolve against your imported tokens:
- Bound to an **imported** token → emitted as `var(--brand-slug, #0066FF)` — a later brand refresh propagates into the component.
- Bound to a token you **haven't imported** → the literal color is used and the element is flagged `data-figma-unresolved`. The command tells you; run `tokens` on the source (or library) file and re-import to link them.
Matching is by exact Figma ID only — never by hex value — so a coincidentally-shared color can't create a false brand link.
## Motion, shaders, and storyboards
These run through the `/figma` agent skill:
- **Motion** — a Figma Motion timeline (keyframes, easing, repeats) translates structurally into a paused, finite GSAP timeline registered on `window.__timelines`, seekable frame-by-frame like any hand-authored animation, and editable afterward. Tracks that can't translate faithfully fall back to a baked video clip — the agent tells you which path it took and why.
- **Shaders** — Figma's export path doesn't execute shaders, so the default is a native Figma export (PNG or Motion MP4) imported as an asset/clip.
- **Storyboards** — a section of scene frames is decoded, not slideshowed: frames sharing an element are treated as that element's keyframes over time, diffed into element chains and tweened, with text under the strip read as director notes. When the frames depict one product UI in successive states, the agent escalates further — rebuilds the UI as live DOM and performs each frame delta as a real interaction (cursor, click, navigation), so the result reads as a continuous screen recording. See the `/figma` skill for the full grammar.
## Provenance and refresh
Every import records where it came from (`fileKey`, `nodeId`, `version`) in `.media/manifest.jsonl`. Nothing in a rendered composition points at Figma — assets are files, tokens are variables, motion is a timeline. When the Figma file moves on, re-running the same import commands re-pulls only what changed.
## Troubleshooting
| Error | Meaning | Fix |
| --- | --- | --- |
| `NO_TOKEN` | `FIGMA_TOKEN` unset | Follow [One-time setup](#one-time-setup) |
| `BAD_TOKEN` | Token invalid, expired, or revoked (Figma returns **403 `Invalid token`** for bad PATs, not 401) | Re-mint the token |
| `FORBIDDEN` (403) | Missing a read scope, or no access to the file | The message names the exact scope Figma wants (e.g. `library_content:read` for the styles fallback) — add it, or check file visibility |
| `REQUIRES_ENTERPRISE` (403) | Variables API needs Figma Enterprise | Not a failure — `tokens` falls back to published styles (which needs the Library content scope above) |
| `RATE_LIMITED` (429) | Figma's per-minute limit | The client retries with backoff automatically (honoring `Retry-After`); if it still surfaces, wait a minute or batch fewer nodes |
| "Render timeout" on batch export | Too many large frames in one `/v1/images` call | Chunk to ~4 ids per call |
| `ref has no node id` | Link points at a file, not a node | Copy the link with `?node-id=…` (right-click layer → Copy link) |
+134
View File
@@ -0,0 +1,134 @@
---
title: GSAP Animation
description: "Add animations to your Hyperframes compositions with GSAP."
---
Hyperframes uses [GSAP](https://gsap.com/) for animation. Timelines are paused and controlled by the runtime — you define the animations, the framework handles playback. For background on how animation runtimes plug into Hyperframes, see [Frame Adapters](/concepts/frame-adapters).
## Setup
Include GSAP and create a paused timeline:
```html index.html
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>
<script>
// 1. Create a paused timeline — the framework controls playback
const tl = gsap.timeline({ paused: true });
// 2. Add animations using the position parameter (3rd arg) for absolute timing
tl.to("#title", { opacity: 1, duration: 0.5 }, 0);
// 3. Initialize the global timelines registry (if not already present)
window.__timelines = window.__timelines || {};
// 4. Register the timeline using the data-composition-id as the key
window.__timelines["root"] = tl;
</script>
```
<Note>
The key you use in `window.__timelines` must match the `data-composition-id` attribute on your composition's root element. See [Compositions](/concepts/compositions) for how the root element is structured.
</Note>
## Key Rules
1. **Always create timelines with `{ paused: true }`** — the framework controls playback via [deterministic seeking](/concepts/determinism)
2. **Register timelines on `window.__timelines`** with the [`data-composition-id`](/concepts/data-attributes#composition-attributes) as key
3. **Use the position parameter** (3rd argument) for absolute timing: `tl.to(el, vars, 1.5)`
4. **Only animate visual properties** — never control media playback in scripts
## Supported Methods
| Method | Description |
|--------|-------------|
| `tl.to(target, vars, position)` | Animate to values |
| `tl.from(target, vars, position)` | Animate from values |
| `tl.fromTo(target, fromVars, toVars, position)` | Animate from/to values |
| `tl.set(target, vars, position)` | Set values instantly |
## Supported Properties
`opacity`, `x`, `y`, `scale`, `scaleX`, `scaleY`, `rotation`, `color`, `backgroundColor`, and other CSS-animatable transform/color properties. Do **not** animate `visibility`, `width`, or `height` — these break deterministic rendering.
## Timeline Duration and Composition Duration
A composition's duration equals its GSAP timeline duration. The two are directly linked:
```javascript compositions/intro-anim.html
// Your last animation ends at 3 seconds...
tl.from("#title", { opacity: 0, y: -50, duration: 1 }, 0);
tl.to("#title", { opacity: 0, duration: 1 }, 2);
// ...so this composition is exactly 3 seconds long.
```
If your composition contains a video clip that is 283 seconds long, but your last GSAP animation ends at 8 seconds, the composition will be only 8 seconds long and the video will be cut short. To extend the timeline to match the video:
```javascript index.html
// All your visual animations
tl.to("#lower-third", { left: -640, duration: 0.6 }, 7.2);
// Extend the timeline to 283 seconds to match the video length.
// This adds a zero-duration tween at 283s without affecting any elements.
tl.set({}, {}, 283);
```
<Warning>
This is one of the most common mistakes in Hyperframes. If your video cuts off early, the timeline is too short. See [Common Mistakes: Composition Duration Shorter Than Video](/guides/common-mistakes) for more details.
</Warning>
## What NOT to Do
These patterns will break your composition or cause sync issues:
```javascript index.html
// WRONG: Playing media in scripts — the framework owns media playback
document.getElementById("el-video").play();
document.getElementById("el-audio").currentTime = 5;
// WRONG: Creating a non-paused timeline
const tl = gsap.timeline(); // missing { paused: true }!
// WRONG: Animating dimensions directly on a <video> element
tl.to("#el-video", { width: 500, height: 280 }, 5);
// WRONG: Manually nesting sub-timelines
const masterTL = window.__timelines["root"];
masterTL.add(window.__timelines["intro-anim"], 0);
```
The framework automatically manages [media playback](/reference/html-schema#framework-managed-behavior), [clip lifecycle](/concepts/compositions#two-layers-primitives-and-scripts), and [sub-composition nesting](#sub-composition-timelines). Scripts that duplicate this behavior will conflict.
## Sub-Composition Timelines
Each [nested composition](/concepts/compositions#nested-compositions) registers its own timeline. The framework automatically nests sub-composition timelines into the parent based on [`data-start`](/concepts/data-attributes#timing-attributes):
```javascript compositions/intro-anim.html
// In compositions/intro-anim.html
const tl = gsap.timeline({ paused: true });
tl.from(".title", { opacity: 0, y: -50, duration: 1 });
window.__timelines["intro-anim"] = tl;
// DO NOT manually add sub-timelines to the master:
// masterTL.add(window.__timelines["intro-anim"], 0); // UNNECESSARY
```
<Warning>
Don't animate `width`, `height`, `top`, or `left` directly on `<video>` elements — this can cause the browser to stop rendering frames. Wrap the video in a `<div>` and animate the wrapper instead. See [Common Mistakes](/guides/common-mistakes) for a detailed explanation.
</Warning>
## Next Steps
<CardGroup cols={2}>
<Card title="Compositions" icon="layer-group" href="/concepts/compositions">
Understand the building blocks that timelines animate
</Card>
<Card title="Frame Adapters" icon="plug" href="/concepts/frame-adapters">
Learn how GSAP plugs into the render pipeline
</Card>
<Card title="Common Mistakes" icon="triangle-exclamation" href="/guides/common-mistakes">
Avoid pitfalls that break animations
</Card>
<Card title="HTML Schema Reference" icon="code" href="/reference/html-schema">
Full reference for composition attributes
</Card>
</CardGroup>
+197
View File
@@ -0,0 +1,197 @@
---
title: HDR Rendering
description: "Render compositions to HDR10 MP4 (BT.2020 PQ or HLG, 10-bit H.265) when sources contain HDR video or images."
---
Hyperframes can render to HDR10 MP4 (H.265 10-bit, BT.2020) when your composition references HDR video or HDR still images. HDR is auto-detected by default from your media sources and falls back to SDR when none are present.
<Note>
By default, Hyperframes probes your media and enables HDR only when HDR sources are present. Use `--hdr` to force HDR even without HDR sources, or `--sdr` to force SDR even when HDR sources are present.
</Note>
## Quickstart
<Steps>
<Step title="Add an HDR source to your composition">
Hyperframes detects HDR from the source's color space metadata. The most reliable HDR sources are:
- **HDR video** tagged BT.2020 with PQ (`smpte2084`) or HLG (`arib-std-b67`) transfer
- **HDR still images** as 16-bit PNG with BT.2020 PQ encoding
See [Source Media](#source-media-requirements) for full details.
</Step>
<Step title="Render normally">
```bash Terminal
npx hyperframes render --output output.mp4
```
HDR output requires `--format mp4`. If Hyperframes detects HDR sources, it renders HDR automatically. If you also pass `--format mov` or `--format webm`, Hyperframes logs a warning and falls back to SDR.
</Step>
<Step title="Verify the output is HDR">
Use `ffprobe` to confirm the encoded stream carries HDR color tagging and HDR10 metadata:
```bash Terminal
ffprobe -v error -show_streams output.mp4 | grep -E 'color_transfer|color_primaries|color_space'
```
See [Verifying HDR output](#verifying-hdr-output) for what to look for.
</Step>
</Steps>
## How HDR Mode Works
During render, the producer:
<Steps>
<Step title="Probes every video and image source">
Runs `ffprobe` on each `<video>` and `<img>` source to read its color space (primaries, transfer function, matrix). This probe drives the default auto-detect behavior and is skipped only when you explicitly force SDR with `--sdr`.
</Step>
<Step title="Picks the dominant HDR transfer">
If any source uses PQ (`smpte2084`), the output uses **PQ**. Otherwise, if any source uses HLG (`arib-std-b67`), the output uses **HLG**. If no HDR sources are found, the render stays SDR.
</Step>
<Step title="Encodes to H.265 10-bit BT.2020">
The video encoder switches to `libx265` with `-pix_fmt yuv420p10le`, color tagging `colorprim=bt2020:transfer=<smpte2084|arib-std-b67>:colormatrix=bt2020nc`, and HDR10 static metadata (`master-display` and `max-cll`). Without that metadata, players (QuickTime, YouTube, HDR TVs) tone-map the stream as if it were SDR BT.2020 — which looks wrong.
</Step>
<Step title="Composites HDR sources natively, converts SDR overlays">
HDR videos and images are extracted as 16-bit linear-light pixels via FFmpeg, kept out of the DOM screenshot, and composited server-side at full bit depth. SDR DOM overlays (text, shapes, UI from your HTML) are converted from **sRGB** to **BT.2020** before being layered on top, so colors do not shift.
</Step>
</Steps>
## Source Media Requirements
### HDR video
Hyperframes recognizes HDR video from its `ffprobe` color space metadata:
| Indicator | Recognized as HDR |
|-----------|-------------------|
| `color_primaries` contains `bt2020` | Yes |
| `color_space` contains `bt2020` | Yes |
| `color_transfer = smpte2084` (PQ) | Yes — PQ |
| `color_transfer = arib-std-b67` (HLG) | Yes — HLG |
| All else (e.g. `bt709`, `smpte170m`) | No — treated as SDR |
A valid HDR source is any MP4 whose stream metadata reports BT.2020 primaries plus PQ or HLG transfer. Hyperframes will detect it automatically:
```bash Terminal
ffprobe -v error -show_streams assets/clip.mp4 | grep color
# color_primaries=bt2020
# color_transfer=smpte2084
# color_space=bt2020nc
```
### HDR still images
Hyperframes supports HDR still images delivered as **16-bit PNGs** tagged with BT.2020 primaries and PQ transfer. Drop them into the composition as a normal `<img>`:
```html index.html
<img class="clip" data-start="0" data-duration="3"
src="./assets/hdr-photo.png" />
```
When HDR is enabled, the image is decoded once to 16-bit linear-light RGB and composited natively into the HDR output.
<Note>
HDR `<img>` decoding is limited to **16-bit PNG**. JPEG, WebP, AVIF, and APNG are not recognized as HDR sources — they load through the normal SDR DOM path. For HDR motion, use a `<video>` element.
</Note>
### SDR sources mixed with HDR
You can freely mix SDR and HDR media in the same composition:
- **SDR videos** stay in the DOM screenshot path and get the sRGB → BT.2020 conversion described above
- **HDR videos** are extracted natively at 16-bit and composited underneath the SDR DOM layer
- **SDR images and DOM elements** (text, shapes, gradients, GSAP animations) are converted from sRGB to BT.2020
This is the same pipeline that handles compositions where, for example, an HDR drone clip plays under an SDR lower-third with animated text.
## Output Format Requirements
| Output format | HDR supported |
|---------------|---------------|
| `mp4` | Yes — H.265 10-bit BT.2020, HDR10 metadata |
| `mov` | No — falls back to SDR |
| `webm` | No — falls back to SDR |
| `gif` | No — falls back to SDR |
If HDR is enabled and you also pass `--format mov`, `--format webm`, or `--format gif`, Hyperframes logs a message and produces the equivalent SDR render. There is no error — the render still completes — so check the logs (or your verification step) to confirm you got HDR.
## Verifying HDR Output
Use `ffprobe` to confirm both the color tagging and the HDR10 static metadata are present:
```bash Terminal
ffprobe -v error -show_streams -select_streams v:0 output.mp4 \
| grep -E 'codec_name|pix_fmt|color_transfer|color_primaries|color_space'
```
For a PQ HDR10 render you should see:
```
codec_name=hevc
pix_fmt=yuv420p10le
color_space=bt2020nc
color_primaries=bt2020
color_transfer=smpte2084
```
Then check the HDR10 SEI / container boxes:
```bash Terminal
ffprobe -v error -show_frames -read_intervals "%+#1" \
-show_entries frame=side_data_list output.mp4
```
You should see entries for **Mastering display metadata** and **Content light level metadata**. Without them, HDR-aware players will treat the file as SDR BT.2020 and the colors will look washed-out or wrong on an HDR display.
For HLG renders the only difference is `color_transfer=arib-std-b67` — the rest of the checks are the same.
## Docker Rendering
Docker uses the same auto-detect logic as local rendering, so you can produce HDR10 MP4 output from the containerized renderer without extra flags:
```bash Terminal
npx hyperframes render --docker --output output.mp4
```
The container runs the same probe → composite → encode pipeline as the local renderer. Verify the output with the same `ffprobe` checks described in [Verifying HDR output](#verifying-hdr-output).
<Note>
Docker HDR rendering currently runs **CPU-side** for the SDR DOM layer (the container falls back to software WebGL because GPU passthrough is not configured by default). Frame capture is therefore slower than local headed Chrome — measure your own composition with `--quiet` off and compare wall-clock times before sizing CI runners. The encoded HDR10 metadata and pixel data are identical to a local render.
</Note>
## Limitations
- **MP4 only** — HDR output with `--format mov` or `--format webm` falls back to SDR
- **HDR images: 16-bit PNG only** — other formats (JPEG, WebP, AVIF, APNG) are not decoded as HDR and fall through the SDR DOM path
- **H.265 only — H.264 is stripped** — calling the encoder with `codec: "h264"` and `hdr: { transfer }` is rejected; the encoder logs a warning, drops `hdr`, and tags the output as SDR/BT.709. `libx264` cannot encode HDR, so the alternative would be a "half-HDR" file (BT.2020 container tags but a BT.709 VUI block in the bitstream) which confuses HDR-aware players.
- **GPU H.265 emits color tags but no static mastering metadata** — `useGpu: true` with HDR (nvenc, videotoolbox, amf, qsv, vaapi) tags the stream with BT.2020 + the correct transfer (smpte2084 / arib-std-b67) but does **not** embed `master-display` or `max-cll` SEI. ffmpeg does not let those flags pass through hardware encoders. The output is suitable for previews and authoring but not for HDR10-aware delivery (Apple TV, YouTube, Netflix). For spec-compliant HDR10 production output, leave `useGpu: false` so the SW `libx265` path embeds the mastering metadata.
- **Player support** — the [`<hyperframes-player>`](/packages/player) web component plays back the encoded MP4 in the browser and inherits whatever HDR support the host browser provides; it does not implement its own HDR pipeline
- **Headed Chrome HDR DOM capture** — the engine ships a separate WebGPU-based capture path for rendering CSS-animated DOM directly into HDR (`initHdrReadback`, `launchHdrBrowser`). It requires headed Chrome with `--enable-unsafe-webgpu` and is not used by the default render pipeline. See [Engine: HDR](/packages/engine#hdr-apis) if you are building a custom integration.
## Common Pitfalls
| Symptom | Likely cause |
|---------|--------------|
| Output looks identical to SDR | Source media is SDR, or SDR was forced with `--sdr`. Run `ffprobe` on your inputs and check the render logs |
| Output is "kind of HDR" but tone-mapped wrong on YouTube/QuickTime | Missing HDR10 static metadata on the encoded stream. Verify with the ffprobe snippet above |
| Docker render is much slower than local | Expected — the container falls back to software WebGL for SDR DOM capture. Pixel output is the same |
| Used `--format webm` and got SDR | Expected — HDR output is MP4 only |
| HDR `<img>` looks SDR / washed out | Source is not a 16-bit PNG. Re-export as 16-bit PNG (BT.2020 PQ) or use a `<video>` element instead |
## Next Steps
<CardGroup cols={2}>
<Card title="Rendering" icon="film" href="/guides/rendering">
Local vs Docker, quality presets, workers
</Card>
<Card title="CLI" icon="terminal" href="/packages/cli">
Full `render` command reference including HDR auto-detect, `--hdr`, and `--sdr`
</Card>
<Card title="Engine: HDR APIs" icon="gear" href="/packages/engine#hdr-apis">
Public HDR utilities exported from `@hyperframes/engine`
</Card>
<Card title="Common Mistakes" icon="triangle-exclamation" href="/guides/common-mistakes">
Pitfalls that affect render output
</Card>
</CardGroup>
+130
View File
@@ -0,0 +1,130 @@
---
title: "HTML-in-Canvas"
description: "Render live HTML as WebGL textures — GPU shaders, 3D geometry, and cinematic effects on any DOM content."
---
# HTML-in-Canvas
The HTML-in-Canvas API (`drawElementImage`) lets you capture live, rendered DOM elements directly into a canvas at GPU speed. This means you can take any HTML — dashboards, forms, landing pages, app UIs — and render them as textures in WebGL scenes with shaders, 3D transformations, and post-processing effects.
<Warning>
**Chrome flag required.** The `drawElementImage` API is experimental and must be enabled manually:
1. Open `chrome://flags/#canvas-draw-element` in Chrome or Brave
2. Set **CanvasDrawElement** to **Enabled**
3. Restart the browser
HyperFrames enables this flag automatically during rendering (`--enable-features=CanvasDrawElement`), so rendered videos work without manual setup. The flag is only needed for live preview in the Studio.
</Warning>
## How it works
1. Place HTML content inside a `<canvas layoutsubtree>` element
2. The browser renders the HTML children as normal DOM
3. Call `ctx.drawElementImage(element, x, y, w, h)` to capture the rendered pixels into the canvas
4. Use the canvas as a Three.js texture, apply shaders, map to 3D geometry
```html
<!-- 1. HTML content lives inside the canvas -->
<canvas id="capture" layoutsubtree width="1920" height="1080">
<div class="my-dashboard">
<h1>Revenue: $4.2M</h1>
<div class="chart">...</div>
</div>
</canvas>
<!-- 2. WebGL canvas for 3D rendering -->
<canvas id="theater" width="1920" height="1080"></canvas>
```
```javascript
// 3. Capture HTML to canvas
var capCanvas = document.getElementById("capture");
var ctx = capCanvas.getContext("2d");
ctx.drawElementImage(capCanvas.querySelector(".my-dashboard"), 0, 0, 1920, 1080);
// 4. Use as Three.js texture
var texture = new THREE.CanvasTexture(capCanvas);
var material = new THREE.MeshBasicMaterial({ map: texture });
```
## What makes this different
Traditional approaches like `html2canvas` re-parse and re-render the DOM in JavaScript — they're slow, lossy, and miss CSS features like `backdrop-filter`, complex shadows, and web fonts. The `drawElementImage` API uses the browser's own compositor, so:
- **Pixel-perfect** — every CSS feature is supported because the browser renders it natively
- **GPU-accelerated** — captures at 60fps, fast enough for real-time animation
- **Live content** — the HTML can animate, scroll, and change between captures
- **Multiple captures simultaneously** — no nesting restrictions, multiple `<canvas layoutsubtree>` elements can capture different content in the same composition
## Feature detection
Always feature-detect before using the API. Compositions should fall back gracefully for browsers without the flag enabled.
```javascript
function isSupported() {
var tc = document.createElement("canvas");
if (!("layoutSubtree" in tc)) return false;
tc.setAttribute("layoutsubtree", "");
var ctx = tc.getContext("2d");
return ctx && typeof ctx.drawElementImage === "function";
}
if (isSupported()) {
ctx.drawElementImage(element, 0, 0, w, h);
} else {
// Fallback: draw text directly on canvas, use static image, etc.
}
```
## Re-capturing every frame
For animated content (scrolling, transitions, counters), call `drawElementImage` inside your render loop to update the texture every frame:
```javascript
function render() {
// Update HTML state
scrollContainer.style.transform = "translateY(-" + scrollOffset + "px)";
counterEl.textContent = Math.round(currentValue);
// Re-capture
ctx.clearRect(0, 0, W, H);
ctx.drawElementImage(htmlElement, 0, 0, W, H);
texture.needsUpdate = true;
// Render 3D scene with updated texture
renderer.render(scene, camera);
}
```
## Catalog blocks
Install all HTML-in-Canvas blocks at once:
```bash
npx hyperframes add html-in-canvas
```
Or install individually:
| Block | Description | Install |
|-------|-------------|---------|
| [iOS 26 Liquid Glass Home Screen](/catalog/blocks/ios26-liquid-glass) | iPhone home screen with live Liquid Glass notifications and widgets | `npx hyperframes add ios26-liquid-glass` |
| [macOS Tahoe Liquid Glass Desktop](/catalog/blocks/macos-tahoe-liquid-glass) | MacBook desktop with liquid glass windows, dock, and notifications | `npx hyperframes add macos-tahoe-liquid-glass` |
| [Liquid Glass Notification](/catalog/blocks/liquid-glass-notification) | Native-feeling glass notification stack with progress and reply states | `npx hyperframes add liquid-glass-notification` |
| [iPhone & MacBook](/catalog/blocks/vfx-iphone-device) | Real 3D GLTF devices with live HTML screens | `npx hyperframes add vfx-iphone-device` |
| [Text Cursor](/catalog/blocks/vfx-text-cursor) | Dramatic text reveal with chromatic shadows | `npx hyperframes add vfx-text-cursor` |
| [Portal](/catalog/blocks/vfx-portal) | Dimension breach with volumetric light | `npx hyperframes add vfx-portal` |
| [Shatter](/catalog/blocks/vfx-shatter) | HTML shatters into glass fragments | `npx hyperframes add vfx-shatter` |
| [Magnetic](/catalog/blocks/vfx-magnetic) | Magnetic field particle visualization | `npx hyperframes add vfx-magnetic` |
| [Liquid Background](/catalog/blocks/vfx-liquid-background) | Organic liquid simulation | `npx hyperframes add vfx-liquid-background` |
## Rendering
HyperFrames enables the Chrome flag automatically during rendering. No special configuration needed:
```bash
npx hyperframes render --output my-video.mp4
```
For Docker renders, the flag is also enabled automatically inside the container.
+162
View File
@@ -0,0 +1,162 @@
---
title: Hyperframes vs Remotion
description: "Why we built Hyperframes, how it differs from Remotion in practice, and where each tool fits."
---
[Remotion](https://www.remotion.dev) is an awesome project, and we used Remotion in HeyGen's production pipelines for many months. Remotion promoted the idea of using code to orchestrate and animate video production, and it proved that headless Chrome could be a reliable, deterministic video renderer. Several patterns in the Hyperframes source come directly from what the Remotion team pioneered — Chrome launch flags, port selection, image2pipe streaming into FFmpeg, in-order frame buffering. We kept attribution comments in our code on purpose so the lineage stays visible to anyone reading the source.
This guide is the honest breakdown of where Hyperframes and Remotion differ, written by the team that built Hyperframes. We picked different bets; each one has strengths the other doesn't, and this doc walks through both.
## Why we built Hyperframes
As we scaled our code-to-video pipelines at HeyGen, we kept running into the same kinds of limits inside a React-first authoring model. We debated internally whether to keep building on Remotion or write a renderer from scratch. Two factors pushed us to build Hyperframes.
### 1. The agent-native workflow
In our evals, LLMs writing Remotion compositions produced less creative visual outputs and needed a lot more guardrails and prompting than the same LLMs writing HTML + [GSAP](/guides/gsap-animation) compositions directly.
Two related issues compounded it:
- Animation libraries with their own internal clocks (GSAP, Anime.js, Motion One) don't compose cleanly with React's per-frame render.
- Arbitrary HTML or CSS that wasn't written as React doesn't have a clean path into a React composition — you have to rewrite it.
### 2. The human editing workflow
We want the same code to be the render layer _and_ the data layer, because we need a UI for humans on top of the agentic experience.
HTML is both the render layer and the editable source of truth — the same DOM is what you see and what you edit. That makes a real visual editor (selection, drag-and-drop, property panels, timeline) much more natural to build, the same way [Paper.Design](https://paper.design) does it. With Remotion, the source of truth is code plus a build step, so round-tripping through a visual editor is painful and fragile. The Remotion team has made progress here, but it's much more difficult to build a real-time editor on top of React than on top of HTML.
We architected Hyperframes to be the most native to agents while making it easy to build a human interface on top.
## At a glance
| | **Hyperframes** | **Remotion** |
| --- | --- | --- |
| Authoring | HTML + CSS + GSAP | React components (TSX) |
| Runtime | Browser DOM, no framework | React reconciliation per frame |
| Build step | None; `index.html` plays as-is | Required (webpack, bundler) |
| Library-clock animations (GSAP, Anime.js, Motion One) | Seekable; frame-accurate | Plays at wall-clock during render |
| Arbitrary HTML / CSS passthrough | Paste and animate | Rewrite as JSX |
| Distributed rendering | [AWS Lambda](/deploy/aws-lambda) support | [Remotion Lambda](https://www.remotion.dev/lambda), mature and production-tested |
| [HDR output](/guides/hdr) | Supported | Documented as unsupported |
| Visual editor over render source | Native; same DOM is editable | Source is code plus a build step |
| License | [Apache 2.0](https://github.com/heygen-com/hyperframes/blob/main/LICENSE) | [Commercial](https://www.remotion.pro/license) |
The rest of this guide walks through what each row means and where each tool actually wins.
## The core difference: React vs HTML
Hyperframes and Remotion both drive headless Chrome. Both are deterministic. Both ship agent skills. They differ on one decision: what the primary author writes.
Remotion's bet is React. Video compositions are React components. You get typed JSX, the React ecosystem, component reuse, and the whole of React tooling. Remotion's strengths come from committing to that surface: years of production use, Lambda at hyperscale, a large community, careful type-safe APIs.
Hyperframes' bet is HTML. Video compositions are HTML pages. You can paste in a landing page, a design-system component, or a CodePen demo and animate it. We think that's the right surface for two specific use cases: AI agents writing video, and visual editors built directly on the DOM the renderer consumes.
Different bets, different strengths. The rest of this doc breaks down where each shows up.
## What the difference means in practice
### Agents express visuals better in HTML than in React
When an LLM writes Hyperframes, it's writing the medium it was trained on most heavily. The web as browsers see it — HTML, CSS, JavaScript, GSAP idioms from CodePen, 25+ years of accumulated web animation content — is the deepest well in a model's training data. React-specific sources are a much smaller slice.
From running both systems in production: an agent asked to write a Remotion composition spends tokens learning framework rules (which hooks are allowed, which APIs are forbidden, how to scaffold a project) before it can be creative. Output tends to converge on a narrow visual vocabulary — centered titles, stock transitions, conventional typography. The same agent writing HTML with GSAP reaches for a wider creative range, because that's what its training data looks like.
### Animation libraries with their own clocks
Asking an agent to port a GSAP animation or an existing web page into Remotion loses details on the first try: timing nuances, audio level relationships, text sizing. The HTML-first path avoids the translation step entirely.
We gave both tools the same 4-second GSAP timeline: 11 letters of "HYPERFRAMES" enter staggered with a back-out ease, hold for 1.5 seconds, then each letter rotates and falls out of frame. Identical animation code, identical easings, identical stagger. The only thing we changed was the renderer.
**Hyperframes output — what the animation is meant to look like:**
<img src="https://static.heygen.ai/hyperframes-oss/docs/images/comparisons/gsap-hf.gif" alt="Hyperframes GSAP render — letters enter, hold, then fall away over the full 4 seconds" />
All four seconds are used. Letters fly in one by one, the full word holds in the center for about a second and a half, then the letters spin and drop away.
**Remotion output — same timeline, same code:**
<img src="https://static.heygen.ai/hyperframes-oss/docs/images/comparisons/gsap-remotion.gif" alt="Remotion GSAP render — GSAP plays through its entire timeline in the first second, leaving most of the render as an empty stage" />
GSAP plays through its entire 4-second animation in roughly the first second of render wall-clock time. By the time Remotion captures later frames, GSAP's timeline has already completed and every letter has exited — the remainder of the render captures an empty stage.
**Why:** GSAP drives its own timeline via `performance.now()`, which ticks at real-time speed during render. Hyperframes pauses GSAP and seeks it to `frame / fps` before capturing each frame, so the library runs in lockstep with the output. Remotion has no equivalent primitive, so GSAP's internal ticker races through the timeline at wall-clock speed while Remotion captures a handful of frames during the entrance and mostly-empty frames after.
Everything GSAP supports — SplitText, ScrollTrigger, MotionPath, physics, 15 years of snippets on CodePen — works the same way in Hyperframes. The pattern generalizes to Anime.js, Motion One, and any other library with its own clock; any JS library without its own clock just works. Wrapping a library clock in a Remotion component is possible but awkward, and you give up most of what the library is good at.
See the [GSAP animation guide](/guides/gsap-animation) for how this integrates, and [Deterministic Rendering](/concepts/determinism) for how seek-driven capture works under the hood.
### Arbitrary HTML, CSS, JavaScript
Every web page is a potential Hyperframes composition. Landing pages. Claude Design artifacts. Design-system docs. CodePen embeds. You paste in the HTML and render.
Remotion asks you to translate first: rewrite HTML as JSX, convert CSS for React, wrap imperative code (Canvas, WebGL, GSAP) in React components with refs and effects. Every translation step is a chance for an agent or a human to lose fidelity or introduce bugs. The translation is round-tripping work anyway — both frameworks ultimately serve HTML to the browser to render.
The [website-to-video guide](/guides/website-to-video) walks through the full capture-to-render pipeline that the HTML-first model enables.
### Auto-fallback for edge primitives
Hyperframes has two capture modes.
- **BeginFrame mode** (Linux + `chrome-headless-shell`) drives Chrome's compositor atomically via `HeadlessExperimental.beginFrame`, producing byte-for-byte reproducible frames across machines.
- **Screenshot mode** (macOS, Windows, and as an automatic fallback) runs Chrome in real time and takes ordinary screenshots — the same approach Remotion uses.
The renderer inspects each composition at compile time and falls back to screenshot mode when it spots primitives BeginFrame can't handle: inline `<iframe>`s, raw `requestAnimationFrame` loops outside a [Frame Adapter](/concepts/frame-adapters). It injects a virtual-time shim so rAF and iframe content stay frame-driven instead of wall-clock-driven. You get a diagnostic explaining the fallback, and the render produces visibly-correct output. When the composition can run in BeginFrame mode, you get determinism for free.
In practice: GSAP timelines, CSS `@keyframes` (via the Web Animations API adapter), Lottie, Three.js, and the Web Animations API all render deterministically in BeginFrame mode. Raw canvas loops and live-web embeds get screenshot mode automatically.
### React component reuse (Remotion's home turf)
If your team already has a design system in React components, Remotion lets you compose videos from the same primitives you ship in your app. Type safety, IDE completion, cross-file refactoring — everything React brings to developer ergonomics. For teams with existing React investment, this is a real advantage Hyperframes doesn't try to match.
### Distributed rendering
[Remotion Lambda](https://www.remotion.dev/lambda) splits long videos across hundreds of AWS Lambda functions. It's mature, production-tested, and well-documented — a thing teams have used in production for years.
Hyperframes now ships an [AWS Lambda deployment path](/deploy/aws-lambda): one Lambda function behind a Step Functions workflow that fans renders out across chunk workers, stores intermediates in S3, and exposes `lambda render`, `lambda render-batch`, progress polling, and SDK usage. The surface is newer than Remotion Lambda, so the practical tradeoff is maturity versus Hyperframes' HTML-native composition model.
### Visual editing over the render source (Hyperframes' natural bet)
The DOM you render is the DOM you edit. [Hyperframes Studio](/packages/studio) previews compositions in a live iframe, and because the renderer and the editor share one DOM, direct manipulation works against the same source of truth the render pipeline consumes. That UX — click to select, drag to reposition, edit properties in a panel — already ships for captions today, with broader element coverage building out from the same architectural foundation.
Building the same editor on top of React is harder because the source of truth is code plus a build step. Round-tripping a visual edit back to JSX means re-compiling. That's why tools like [Paper.Design](https://paper.design) chose HTML as the editable source in the first place.
### HDR output
Both tools currently render through headless Chrome, which outputs sRGB. Remotion [documents this as unsupported](https://www.remotion.dev/docs/hdr). Hyperframes [supports HDR output](/guides/hdr) via a two-pass compositing pipeline that combines a DOM layer with native HLG/PQ video.
## Open source vs source-available
This is one of the clearest differences between the two projects, and one of the most common reasons teams pick one over the other.
| | **Hyperframes** | **Remotion** |
| --- | --- | --- |
| Classification | Open source ([OSI-approved](https://opensource.org/licenses/Apache-2.0)) | Source-available, not open source |
| License | [Apache 2.0](https://github.com/heygen-com/hyperframes/blob/main/LICENSE) | [Custom Remotion License](https://www.remotion.pro/license) |
| Commercial use | Free at any scale | Requires a paid company license above small-team thresholds |
| Per-render fees | None | Yes, above thresholds |
| Redistribution | Permitted under Apache 2.0 | Restricted by the Remotion License |
Both projects publish their source on GitHub, but the licenses work very differently. Apache 2.0 is an [Open Source Initiative-approved license](https://opensource.org/licenses/Apache-2.0) — you can self-host, modify, redistribute, and use Hyperframes commercially at any scale with no per-render fees and no seat caps. The Remotion License is a custom commercial license: you can read the code and self-host for small teams, but commercial use above their thresholds requires a paid company license.
If open-source licensing matters to you — OSI compliance, redistribution rights, no per-use fees, long-term independence from a vendor's pricing decisions — this is a first-order decision point. If your use case fits within Remotion's free tier or your company is comfortable with a commercial license, it's a non-issue. See the [Remotion license page](https://www.remotion.pro/license) for their current terms.
We open-sourced Hyperframes under Apache 2.0 so anyone can build on it — including commercially, at any scale — and so the project can outlive any single company's priorities.
## Recap
The single difference between Remotion and Hyperframes is the choice of React vs HTML (+ CSS + JavaScript). That decision leads to many differences downstream. For our needs — most native to agents, architected to support a UI layer for humans — HTML + CSS + JavaScript was the obvious bet:
1. Agents already "think" in HTML. LLMs have seen massive amounts of web code.
2. True "one file in, video out." No `package.json`, no installs, no bundler config, no composition setup. Fewer moving parts = way fewer random failures in automated and agentic workflows.
3. Highest creative ceiling — anything the browser can render, HTML can represent.
4. HTML is both the render layer and the editable source of truth. The same DOM is what you see and what you edit.
We at HeyGen are all-in on Hyperframes, but we can't do this alone — that's why we open-sourced it under Apache 2.0, so together we can build the foundation for agentic video creation.
## Further reading
- [Deterministic Rendering](/concepts/determinism) — how seek-driven frame capture works
- [GSAP animation guide](/guides/gsap-animation) — library-clock integration in practice
- [Frame Adapters](/concepts/frame-adapters) — the extension point for animation runtimes
- [Website to video](/guides/website-to-video) — the HTML-first capture-to-render pipeline
+153
View File
@@ -0,0 +1,153 @@
---
title: Keyframes & Arc Motion
description: "Edit GSAP keyframes visually in Studio — timeline diamonds, arc motion paths, and gesture recording."
---
Studio gives you visual tools to create and edit GSAP keyframes without writing code. You can adjust animation properties in the Design Panel, convert straight-line motion into curved arcs, and record gesture-based motion by dragging elements in the preview.
## Timeline Keyframe Diamonds
When you open a composition in Studio, the timeline shows **diamond markers** on clips that have GSAP animations. Each diamond represents a keyframe — a point in time where a property value is set.
- **Start diamond** — where the tween begins (e.g., `x: 0`)
- **End diamond** — where the tween ends (e.g., `x: 1000`)
- Elements with multiple tweens show multiple diamond pairs
<Note>
Keyframe diamonds are synthesized from your GSAP tweens automatically. Every `.to()`, `.from()`, and `.fromTo()` call produces start and end markers on the timeline.
</Note>
## Editing Animation Properties
Select any animated element in the preview or timeline to open the Design Panel. The **Animation** section shows:
- **Method badge** — `Animate`, `Animate In`, or `From → To` (maps to `.to()`, `.from()`, `.fromTo()`)
- **Timing** — Length (duration) and Starts at (position on timeline)
- **Speed** — The GSAP ease (e.g., `power2.inOut`, `back.out(3)`)
- **Speed curve** — Visual preview of the easing function
- **Properties** — Each animated property (Move X, Move Y, Scale, Opacity, etc.) with its target value
<Steps>
<Step title="Select an element">
Click an animated element in the preview or its clip in the timeline. The Design Panel opens on the right.
</Step>
<Step title="Edit property values">
Change any property value directly — for example, set Move X to `500` to make the element travel 500px. Changes apply immediately via soft reload.
</Step>
<Step title="Change the ease">
Click the ease dropdown (e.g., "Smooth ease") to pick a different easing function. The speed curve preview updates live.
</Step>
<Step title="Verify in Code tab">
Switch to the Code tab to see the generated GSAP code. Every Design Panel edit writes valid GSAP that renders identically in preview and headless export.
</Step>
</Steps>
## Arc Motion
Arc Motion converts a straight-line x/y animation into a curved path using GSAP's MotionPathPlugin. Instead of moving in a straight diagonal, the element follows a smooth arc — like tossing an object into a basket.
### When to Use It
Use Arc Motion when an element has both `x` and `y` properties in a single tween. Common examples:
- Add-to-cart animations (item arcs from product to cart icon)
- Throw/toss effects
- Any motion that should feel physical rather than robotic
### Step-by-Step
<Steps>
<Step title="Select an element with x/y motion">
The element must have a `.to()` tween with both Move X and Move Y properties. Select it in the preview or timeline.
</Step>
<Step title="Toggle Arc Motion ON">
In the Animation section of the Design Panel, find the **Arc Motion** toggle below the property list. Switch it ON.
</Step>
<Step title="Adjust Curviness">
The **Curviness** slider controls how exaggerated the arc is:
- `0` — straight line (no curve)
- `1` — gentle natural arc
- `1.52.0` — smooth throw feel (recommended)
- `3.0` — extreme loop
Scrub the timeline to preview the arc in real time.
</Step>
<Step title="Toggle Auto-Rotate (optional)">
Enable **Auto-Rotate** to make the element rotate to face the direction of travel along the arc. This adds a "thrown" feel vs. a "floating" feel.
</Step>
<Step title="Verify the generated code">
Switch to the Code tab. You'll see:
```javascript
tl.to("#element", {
scale: 0.4,
opacity: 0,
duration: 1.0,
ease: "power2.inOut",
motionPath: {
path: [{x: 0, y: 0}, {x: 1400, y: -280}],
curviness: 1.5,
autoRotate: true
}
}, 1.0);
```
The MotionPathPlugin CDN script is added automatically.
</Step>
<Step title="Disable to restore straight motion">
Toggle Arc Motion OFF to restore the original `x` and `y` properties as flat tween values.
</Step>
</Steps>
<Note>
Arc Motion works for flat `.to()` tweens with x/y properties. It synthesizes waypoints from `{x: 0, y: 0}` (start) to `{x: targetX, y: targetY}` (end). For more complex paths with intermediate waypoints, edit the `motionPath.path` array directly in the Code tab.
</Note>
## Gesture Recording
Record motion by physically dragging an element in the preview while the timeline plays. The pointer path is simplified and converted into GSAP keyframes automatically.
<Steps>
<Step title="Select an element">
Click the element you want to animate in the preview.
</Step>
<Step title="Click Record or press R">
In the Animation section of the Design Panel, click **Record gesture (R)** or press the R key. The timeline starts playing.
</Step>
<Step title="Drag the element">
Move the element in the preview by dragging it. Your pointer motion is sampled at ~60fps. A trail overlay shows the path you're drawing.
</Step>
<Step title="Stop recording">
Press R again or wait for the timeline to reach the end. Recording stops, the motion is simplified (reducing ~180 raw samples to 515 clean keyframes), and the keyframes are written to the GSAP script immediately.
</Step>
<Step title="Review or undo">
The timeline seeks back to the recording start so you can scrub through the result. If you don't like it, press **Cmd+Z** to undo and try again.
</Step>
</Steps>
## Computed Timelines (Helpers, Loops, Dynamic Data)
Studio reads your timeline statically, so a composition that builds its tweens with a **helper function called several times**, a **bounded loop**, or **data-driven values** still shows every keyframe at its true time on the timeline — and the Arc Motion panel still activates for `motionPath` tweens, even when the path comes from a variable.
How those keyframes are **edited** depends on how they were authored:
- **Literal tweens** (`tl.to("#x", { x: 100 }, 1.3)`) — edit directly in the Design Panel. One source call, one tween.
- **Helper / loop tweens** — a single source line (e.g. `addCycle(1.0, ...)`) expands into many runtime tweens, so editing one keyframe is ambiguous. The Animation card shows a **"Generated by `addCycle()` — not directly editable"** notice with an **Unroll to edit** action: it rewrites the helper or loop into explicit literal tweens (a visual no-op — the render is identical), after which each keyframe edits directly. Undo restores the helper.
- **Computed-value tweens** (the rare case of a value derived at runtime that can't be resolved or unrolled) — stay display-only; edit them in the **Code tab**. HyperFrames compositions are deterministic, so this case is uncommon.
The notice on each computed tween tells you which path applies.
## Clipboard Context
The **clipboard icon** next to the element name in the Design Panel copies structured element context to your clipboard:
```
Element: Title (#title)
File: index.html:15
Position: x=100, y=40
Size: 264×43
Tag: <div>
Animation: from() 0.5s at 0s, ease: power2.out
Properties: x: -40, opacity: 0
```
Paste this into any AI agent prompt to give it spatial context about the element — its position, size, animation, and source location.
+332
View File
@@ -0,0 +1,332 @@
---
title: HyperFrames MCP
description: "Author, preview, and render HyperFrames videos directly inside Claude.ai, ChatGPT, and Grok — no local install required."
---
The HyperFrames MCP is a hosted [Model Context Protocol](https://modelcontextprotocol.io) server that lets you create, edit, preview, and render HyperFrames video compositions from inside Claude.ai, ChatGPT, or Grok.
<Note>
**In beta.** Features and pricing may change. Found a bug or have feedback? [File an issue on GitHub](https://github.com/heygen-com/hyperframes/issues).
</Note>
## What you can do
- Create compositions from natural-language prompts
- Edit existing compositions conversationally — *"make the title 2x bigger"*, *"add hype-style captions"*
- Preview the result inline in the chat with a video player widget
- Render to `mp4`, `webm`, or `mov` — output URL streamed back to chat
- Revisit compositions you've created previously
- Check your render credits
The agent behind `compose` has 25+ HyperFrames-specific skills baked in — typography, color palettes, motion principles, GSAP effects, audio-reactive animation, and captions. You don't have to specify any of this directly; describe the video you want and the agent picks the right tools.
<Note>
Looking for the open-source CLI? See the [Quickstart](/quickstart). The MCP is a hosted product for zero-install authoring inside an LLM chat. The CLI gives you full control of rendering and runtime; the MCP gives you instant authoring with cloud rendering.
</Note>
## Setup
### 1. Get a HeyGen account
The MCP requires a HeyGen account for authentication and credits. Sign up at [heygen.com](https://heygen.com) if you don't have one.
### 2. Add the connector
<Tabs>
<Tab title="Claude.ai">
<Steps>
<Step title="Open Settings → Connectors">
In Claude.ai web or desktop: **Settings → Connectors**.
</Step>
<Step title="Find HyperFrames">
Search the connector catalog for **HyperFrames** and click **Connect**.
Don't see it in the catalog? Add it manually: **Add custom connector**, then paste:
```
https://mcp.heygen.com/mcp/hyperframes
```
</Step>
<Step title="Sign in to HeyGen">
OAuth opens in a new window. Authorize the HyperFrames connector to access your HeyGen account.
</Step>
<Step title="Start a new chat">
Open a new Claude.ai chat. Try:
> Make me a 10-second product intro for [your product] with bouncy captions and a high-energy soundtrack.
</Step>
</Steps>
</Tab>
<Tab title="ChatGPT">
<Steps>
<Step title="Open Apps & Connectors">
In ChatGPT: **Settings → Apps & Connectors → Add MCP server**.
</Step>
<Step title="Enter the URL">
Paste:
```
https://mcp.heygen.com/mcp/hyperframes
```
</Step>
<Step title="Sign in to HeyGen">
Authorize via OAuth.
</Step>
<Step title="Start a new chat">
Open a new chat. Same prompts work as in Claude.ai.
</Step>
</Steps>
</Tab>
<Tab title="Grok">
<Steps>
<Step title="Open Settings → Connectors">
In Grok: **Settings → Connectors**.
</Step>
<Step title="Find HyperFrames">
Search the connector catalog for **HyperFrames** and click **Connect**.
Don't see it in the catalog? Add it manually: **Add custom connector**, then paste:
```
https://mcp.heygen.com/mcp/hyperframes
```
</Step>
<Step title="Sign in to HeyGen">
Authorize via OAuth.
</Step>
<Step title="Start a new chat">
Open a new chat. Same prompts work as in Claude.ai.
</Step>
</Steps>
</Tab>
</Tabs>
## Available tools
The MCP exposes six tools to the LLM. You don't call them directly — the model picks the right tool based on your message.
| Tool | What it does | Cost |
|---|---|---|
| `compose` | Create a new composition or edit an existing one | Author credits |
| `list_compositions` | List your previously created compositions | Free |
| `get_composition` | Open a specific composition with an inline player | Free |
| `render_video` | Submit a cloud render to mp4 / webm / mov | Render credits |
| `get_render_status` | Poll a long-running render job | Free |
| `get_credits` | Check your remaining credits and tier | Free |
### compose
Authors a new composition or applies an edit to an existing one. The HyperFrames agent handles captions, blocks, layout, transitions, color, and timing internally based on your natural-language prompt.
**Triggered by prompts like:**
- "Make a 30-second product intro about [topic]" → creates a fresh composition
- "Change the title font to a bold serif" → edits the most recent composition
- "Add a flash transition before the call-to-action" → applies a structured edit
**Returns:** A composition reference (id, title, thumbnail) plus an inline player widget. Progress notifications stream during the run so you can see what the agent is doing — *"Drafting outline...", "Selecting style...", "Generating HTML...", "Rendering preview frame..."*
### list_compositions
Lists compositions you've previously created. Newest first, paginated.
**Triggered by prompts like:** *"show me my recent videos"*, *"what did I work on yesterday?"*
### get_composition
Fetches metadata for a single composition along with an inline player widget.
**Triggered by prompts like:** *"open that video again"*, *"show me the one I made about [topic]"*
### render_video
Submits a cloud render. Defaults to `mp4` at `30fps`.
**Format options:**
| Format | Codec | Use it for |
|---|---|---|
| `mp4` (default) | H.264 | Broadest compatibility, social media, web |
| `webm` | VP9 | Smaller files; supports alpha channel for transparent overlays |
| `mov` | ProRes | Lossless quality for editing pipelines |
**Frame rate options:** `24`, `30` (default), `60`.
**Returns:** Either the rendered video URL (if the render finishes within 25 seconds) or a `job_id` for polling. Either way, an inline render-progress widget shows live status.
**Triggered by prompts like:** *"render this"*, *"export to webm"*, *"render at 60fps for editing"*.
### get_render_status
Polls an in-progress render. Used internally by the model when `render_video` returns a `job_id` (long renders).
### get_credits
Returns your tier and remaining credits.
**Triggered by:** *"how many renders do I have left?"*, *"what's my plan?"*
## Prompting tips
### Be specific about what you want
The agent has lots of creative latitude — give it enough direction to use it well.
| Less effective | More effective |
|---|---|
| "make a video" | "make a 15-second TikTok hook about home composting with bouncy captions and a warm earthy palette" |
| "add captions" | "add hype-style captions in my brand color #FF6A00" |
| "make it shorter" | "trim to 10 seconds total — cut the third scene" |
| "more energetic" | "swap to a neon-electric palette and tighten all transitions to 200ms" |
### Iterate conversationally
Once a composition exists, the agent loads the current state and applies edits in place. Keep talking to it.
```
You: "20-second product intro for my app, dark theme, hype style"
Agent: [composition + player widget appears]
You: "make the logo bigger and add a pulse animation on the CTA"
Agent: [updated player widget]
You: "render to webm with alpha"
Agent: [render-progress widget, then player widget with download link]
```
### Reference your existing HeyGen assets
If you've uploaded logos, fonts, or other assets to your HeyGen account, the agent can use them. Just say *"use my logo"* or *"use my brand font."* The agent resolves the asset by name and recency.
### Pick the right format up front
Mention the output format if you have a specific use case:
- *"render to mp4"* — default, social media
- *"render to webm with alpha"* — transparent overlay you'll composite later
- *"render to mov for After Effects"* — ProRes for editing
## Debugging
### Inspect the MCP with MCP Inspector
For developers building integrations or debugging tool responses, the MCP Inspector lets you see exactly what tools are exposed and what they return:
```bash
npx @modelcontextprotocol/inspector npx -y mcp-remote https://mcp.heygen.com/mcp/hyperframes
```
Complete the OAuth flow in the inspector's browser tab. Once authenticated, you can call each tool with custom parameters and see raw responses, including `tool_data` and `widget_data`.
### Watch progress notifications
The MCP emits MCP `notifications/progress` events during long-running `compose` and `render_video` calls. The host (Claude.ai, ChatGPT, or Grok) displays them inline:
```
You: "make me a 30-second product intro"
Agent: [calls compose]
↳ "Drafting outline..." ← progress notification
↳ "Selecting style..." ← progress notification
↳ "Generating HTML..." ← progress notification
↳ "Rendering preview frame..." ← progress notification
↳ [composition + player widget]
Agent: "Here's your video — [player]"
```
If progress stops mid-flow, the run failed. The agent's next message should explain what went wrong.
### Common issues
<AccordionGroup>
<Accordion title="OAuth keeps failing or loops">
Verify you're using the production URL: `https://mcp.heygen.com/mcp/hyperframes`. The dev URL (`mcp.dev.heygen.com`) only accepts dev accounts.
If OAuth completes but you see "not authorized" errors, your HeyGen account may not have access to the MCP — contact support or check your tier.
</Accordion>
<Accordion title="Render is stuck or never completes">
Renders normally finish in 10-90 seconds depending on length, fps, and format.
If `get_render_status` shows `status: rendering` for more than 5 minutes, something has stuck. Try:
1. Start a new chat thread
2. Run `compose("regenerate this composition")` — the underlying composition may reference an asset that's failing to load
3. If the issue persists, file an issue at [github.com/heygen-com/hyperframes/issues](https://github.com/heygen-com/hyperframes/issues) including the `job_id` from `render_video`
</Accordion>
<Accordion title='"Composition not found" error'>
The `composition_id` is owned by the HeyGen space (account) that created it. If you're signed into a different space, you can't access compositions from another. Run `list_compositions` to see what's available from your current account.
</Accordion>
<Accordion title="Player widget shows blank or loads forever">
Usually a transient connection issue between the widget and `mcp.heygen.com`. Refresh the chat or call `get_composition` again.
If it persists, your composition may reference a media asset that failed to upload — recreate the composition with `compose("regenerate this")`.
</Accordion>
<Accordion title="Out of credits">
The MCP returns an error with an upgrade URL when your credits are exhausted. Visit [heygen.com/pricing](https://heygen.com/pricing) to upgrade your tier.
</Accordion>
<Accordion title="Tool call timed out in the host">
`compose` runs can take 30+ seconds for complex prompts. If your client times out before the response comes back, the underlying run is likely still progressing — wait 30 seconds and run `list_compositions`. The composition is probably already created.
</Accordion>
<Accordion title="Agent ignored part of my prompt">
The agent prefers structural decisions (palette, layout, motion) over fine-grained pixel positioning. If a specific edit doesn't land, try rephrasing more directively:
- Less effective: *"the title is a little off"*
- More effective: *"move the title 40px down and increase its weight to 800"*
For pixel-precise control, use the [open-source CLI](/quickstart) — the MCP is optimized for fast natural-language iteration.
</Accordion>
</AccordionGroup>
### Reporting issues
For bugs or feature requests, file an issue at [github.com/heygen-com/hyperframes/issues](https://github.com/heygen-com/hyperframes/issues). Include:
- The prompt you used
- The `composition_id` and `job_id` (if applicable) — these are visible in the tool response details
- A description of what you expected vs. what happened
- The host (Claude.ai web/desktop, ChatGPT, Grok, etc.)
## Limitations
- **Cloud-only rendering.** All renders run on HeyGen infrastructure. Use the [CLI](/quickstart) if you need local rendering.
- **Single-user.** Each composition is owned by one HeyGen account. No team sharing in v1.
- **No binary uploads from chat.** You can reference assets you've already uploaded to HeyGen via the web UI, but the MCP does not currently accept new file uploads through chat. Upload via [app.heygen.com](https://app.heygen.com) first, then reference the asset by name.
- **Aspect ratios:** `16:9`, `9:16`, `1:1`, `4:5`. Other ratios fall back to the closest match.
- **No fine-grained editing tools.** Edits go through the agent. For pixel-precise control, use the [CLI](/quickstart) and a coding agent like Claude Code or Cursor.
- **Widget rendering requires a host that supports MCP widgets.** Claude.ai web/desktop, ChatGPT (Apps SDK), and Grok support widgets today. Text-only MCP clients (Claude Code CLI, Cursor, Windsurf) will see a clickable preview URL instead — full text-mode support is on the roadmap.
## How this relates to the open-source framework
[HyperFrames itself is open source](https://github.com/heygen-com/hyperframes) — the HTML composition format, CLI, renderer, and player. You can use HyperFrames locally without the MCP.
The MCP is a HeyGen-hosted product that wraps:
- The HyperFrames composition agent (the LLM that authors compositions)
- HeyGen's cloud rendering pipeline
- HeyGen's asset libraries
- OAuth, credits, and tier management
| You want… | Use… |
|---|---|
| Zero local install, fast natural-language authoring | The MCP |
| Pixel-precise control, custom rendering, self-hosting | The [CLI](/quickstart) |
| Both | Author with the MCP, then download and refine with the CLI (planned export feature) |
## Next steps
<CardGroup cols={2}>
<Card title="Quickstart" href="/quickstart">
Try HyperFrames locally with the open-source CLI.
</Card>
<Card title="Prompting guide" href="/guides/prompting">
Tips for getting the best results when working with AI agents.
</Card>
<Card title="Catalog" href="/catalog/blocks/data-chart">
Browse 50+ ready-to-use blocks the agent draws from.
</Card>
<Card title="Examples" href="/examples">
Reference compositions you can clone.
</Card>
</CardGroup>
+421
View File
@@ -0,0 +1,421 @@
---
name: hyperframes-handoff
description: |
Produce a HyperFrames-valid HTML composition — paused GSAP timeline, data
attributes, scene structure — that any AI coding agent can immediately
refine with `npx hyperframes lint` and `npx hyperframes preview`. Use when
the brief mentions "video", "reel", "motion graphic", "title card",
"animated explainer", or pairs Open Design with HyperFrames for export.
triggers:
- "hyperframes"
- "video"
- "reel"
- "motion graphic"
- "animated explainer"
- "title card"
- "kinetic typography"
- "动效视频"
- "视频海报"
od:
mode: prototype
platform: desktop
scenario: marketing
preview:
type: html
entry: index.html
design_system:
requires: true
sections: [color, typography, layout, motion]
example_prompt: "Design a 15-second Instagram reel announcing dark mode for Taskflow (#6C5CE7). Output as a HyperFrames composition I can render locally."
---
# HyperFrames Handoff — for Open Design
> **Drop this file at `skills/hyperframes-handoff/SKILL.md` inside your local
> [Open Design](https://github.com/nexu-io/open-design) checkout, restart the
> daemon, and the skill appears in the picker. Or attach it to a fresh chat
> as a one-shot.**
This skill teaches Open Design to emit a **valid first draft** of a
[HyperFrames](https://github.com/heygen-com/hyperframes) composition — plain
HTML + CSS + a paused GSAP timeline. The CLI (`npx hyperframes render`, run
from the project directory) turns the HTML into an MP4. You author the HTML;
the user runs the render locally.
**HyperFrames replaces the default video-artifact workflow.** Do NOT emit a
React/Babel composition, do NOT call other prototype skills, do NOT use the
sandboxed iframe's wall-clock playback for timing decisions. Plain HTML +
GSAP only. Treat the [`claude-design-hyperframes.md`](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/claude-design-hyperframes.md)
companion document as the **upstream spec for HyperFrames structural rules**
the rules below condense it to what Open Design needs at emission time, but
that file is the source of truth for shader catalogs, skeleton variants, and
edge cases.
---
## Your role
**You produce a valid first draft — not a final render.** Open Design's
strengths are visual identity (driven by the active `DESIGN.md`), layout, and
brand-accurate content decisions. The user (or their coding agent) handles
animation polish, timing micro-adjustments, and production QA after handoff.
The user's workflow:
1. **Open Design** (you) — pick palette + typography from the active
`DESIGN.md`, fill scene content, lay down first-pass GSAP entrances and
mid-scene activity, pick shader transitions for 23 key moments
2. **Save to disk** — Open Design writes the project into
`.od/projects/<id>/` (real `cwd`, agent-ready)
3. **Any AI coding agent** (Claude Code, Codex, Cursor, …) — `npx hyperframes
lint`, `npx hyperframes preview`, then iterate timing, eases, shader
choices, pacing
Your output must be a **valid starting point a coding agent can open and
refine immediately** — no structural fixes needed.
### What you optimize for
- The active `DESIGN.md` palette + typography bound onto `:root` (never
freestyle a palette when one is active)
- Strong visual layout per scene (hierarchy, spacing, readability at video
size — 60px+ headlines, 20px+ body)
- Scene content that tells the story (headlines, stats, copy, imagery)
- Structural validity (passes `npx hyperframes lint` with zero errors)
- Appropriate shader choices for the mood (use the catalog at
[hyperframes.heygen.com/catalog](https://hyperframes.heygen.com/catalog))
- Reasonable scene count and durations for the video type
### What the coding agent polishes after you
You ship every scene with entrance tweens, breathing motion, and shader
transitions. The video plays with full motion from your first draft. The
agent does the **edit-bay refinement**: ease curve tweaks, stagger timing,
scene-duration micro-adjustments, richer mid-scene activity, shader swaps,
production QA.
---
## Hard rules (must-pass before emitting `<artifact>`)
These are HyperFrames-structural and non-negotiable. Open Design's
five-dimensional self-critique gate must verify all of them before emission.
1. **Single HTML file.** `<!doctype html>` through `</html>`, all CSS inline,
GSAP loaded from CDN. No build step.
2. **Root composition element.** A single `<div id="stage">` with:
- `data-composition-id="<kebab-name>"`
- `data-start="0"`
- `data-width` / `data-height` (e.g. `1080` × `1920` for 9:16, `1920` ×
`1080` for 16:9, `1080` × `1080` for square)
- `data-duration="<total-seconds>"` matching the sum of scene durations
3. **Scenes are children of `#stage`.** Each scene is `<div class="scene
clip">` with:
- `data-start="<seconds-from-zero>"`
- `data-duration="<scene-seconds>"`
- `data-track-index="0"` (HyperFrames uses tracks for layering; visual
scenes share track 0 unless you intentionally overlap)
- A `.scene-content` wrapper inside it that holds the readable content
(headlines, stats, imagery). Decoratives (glows, grain, vignette) live
directly inside `.scene` but **outside** `.scene-content`.
4. **GSAP timeline registered paused.** A single timeline created with
`gsap.timeline({ paused: true })` and registered on
`window.__timelines = window.__timelines || {}; window.__timelines["<comp-id>"] = tl;`.
This is what makes the composition deterministically seekable — the
HyperFrames engine drives the playhead.
5. **`tl.from()` for entrances.** Animate FROM offscreen/invisible TO the
resting CSS position. Offset the first tween 0.10.3s into each scene to
avoid jump-cuts.
6. **Mid-scene activity on every scene.** Every visible element keeps moving
after its entrance. A still element on a still background is a JPEG with
a progress bar. Use at least 2 patterns per scene from the table below.
7. **Shader transitions ONLY at scene boundaries**, and at most 23 in the
whole video. Use HyperFrames' built-in shader blocks
(`flash-through-white`, `whip-pan`, `cinematic-zoom`, `glitch`,
`ripple-waves`, `light-leak`, `cross-warp-morph`, `chromatic-radial-split`,
`swirl-vortex`, `gravitational-lens`, `domain-warp-dissolve`, `ridged-burn`,
`sdf-iris`, `thermal-distortion`). Hard cuts everywhere else.
8. **No external assets the user didn't provide.** Use solid colors, CSS
gradients, inline SVG, `data:` images. Reference the user's uploaded
images by their saved filenames; don't invent stock URLs.
9. **`preview.html` token forwarding** — emit a sibling `preview.html` that
loads `index.html` in an iframe and forwards URL hash tokens (`?frame=…`
for scrubbing). Skeleton is in §6.
---
## Step 1 — Understand the brief
**Gate:** You can name the subject, duration, aspect ratio, and at least one
source of visual direction.
Open Design's `RULE 1` already covers this — turn 1 is a `<question-form>`
when the brief is sparse. **Do not skip it for video briefs**; pacing
decisions hinge on locking duration and aspect ratio early.
Inputs in order of reliability:
1. **Active `DESIGN.md`** (strongest) — Open Design always has one bound when
this skill runs. Read its palette, typography, and motion sections; bind
verbatim onto `:root`.
2. **Attachments** — screenshots, PDFs, brand guides; mine for any signal the
active DS doesn't already cover.
3. **Pasted content** — hex codes, copy, scripts, exact durations.
4. **Web research** (`WebFetch` + grep for hex) — only if the user names a
brand and the active DS isn't theirs.
---
## Step 2 — Pick a skeleton, fill identity
**Gate:** A working `index.html` exists with the active DS's palette and
typography on `:root`. The preview renders even if scenes are empty.
| Type | Aspect | Duration | Scenes |
| ------------------------- | ------ | --------- | ------ |
| Social reel | 9:16 | 1015s | 57 |
| Launch teaser | 16:9 | 1525s | 710 |
| Product explainer | 16:9 | 3060s | 1018 |
| Cinematic title | 16:9 | 4590s | 712 |
Bind `:root` from the active `DESIGN.md`:
```css
:root {
/* From active DESIGN.md — never invented */
--bg: var(--ds-canvas);
--ink: var(--ds-foreground);
--accent: var(--ds-accent);
--muted: var(--ds-muted);
--font-display: var(--ds-display);
--font-body: var(--ds-body);
}
```
If the active DS uses different token names, alias them — but **always
source the values from the DS file**, never hard-code a hex from memory.
---
## Step 3 — Fill scenes (content + animation)
**Gate:** Every scene has visible content, at least 2 animation patterns from
the table, and mid-scene activity. No scene is a static slide.
### 3a. Content goes inside `.scene-content`
```html
<div class="scene clip" data-start="10.0" data-duration="3.0" data-track-index="0">
<div class="scene-content">
<h1 id="s3-title" class="display">$1.9 Trillion</h1>
<p id="s3-sub" class="body-text">processed annually</p>
<div id="s3-bar-chart"><!-- ... --></div>
</div>
<div class="glow" aria-hidden="true"></div>
</div>
```
### 3b. Entrance tweens (offset 0.10.3s into each scene)
```js
// === SCENE 3 (data-start=10.0) ===
tl.from("#s3-title", { y: 40, autoAlpha: 0, duration: 0.6, ease: "power3.out" }, 10.3);
tl.from("#s3-sub", { y: 20, autoAlpha: 0, duration: 0.5, ease: "power2.out" }, 10.7);
tl.from("#s3-bar-chart", { scaleY: 0, transformOrigin: "bottom", duration: 0.8, ease: "expo.out" }, 11.0);
```
### 3c. Mid-scene activity (this is what separates video from slides)
| Element | Mid-scene motion | Pattern |
| ------------------ | ---------------------------------------- | ----------------------------------------------------------------------- |
| Stat / number | Counter from 0 → target | `tl.to({n:0}, { n: target, duration, onUpdate: …, ease: "power2.out" })` |
| SVG line / path | Draws itself in real time | `strokeDashoffset` from `pathLength → 0` |
| Title / wordmark | Characters enter one by one | `tl.from(chars, { autoAlpha: 0, y: 8, stagger: 0.04 })` |
| Logo / lockup | Subtle vertical drift | `tl.to(el, { y: -6, duration: sceneLength, ease: "sine.inOut" })` |
| Chart / bars | Bars fill sequentially | `tl.from(bars, { scaleY: 0, transformOrigin: "bottom", stagger: 0.08 })` |
| Image / screenshot | Slow zoom: `scale: 1 → 1.03` | Ken Burns — `tl.to(img, { scale: 1.03, duration: sceneLength, ease: "none" })` |
| Background glow | Opacity pulse | `tl.to(".glow", { opacity: 0.6, duration: 1.5, ease: "sine.inOut", yoyo: true, repeat: 1 })` |
**Minimum per scene:** entrance tweens + at least one continuous motion
(float, counter, zoom, or glow).
### 3d. Adjust scene duration by reading time
| Display text | Min duration |
| --------------------------- | ------------ |
| No text (hero, icon) | 1.52s |
| 13 words | 23s |
| 410 words | 34s |
| 1120 words | 46s |
| 2135 words | 68s |
| 35+ words | Split scenes |
**Hard ceiling: 5s per scene** unless you name a specific reason (hero hold,
cinematic push, long counter animation).
When you change a scene's duration, update `data-start` on every subsequent
scene to keep them tiled end-to-end, and update `#stage`'s `data-duration` to
match the total.
### 3e. Vary eases
Use at least 3 different eases across the timeline. Don't default to
`power2.out` on everything. Good defaults: `power3.out` (heavy entrances),
`expo.out` (snappy stat reveals), `sine.inOut` (breathing loops),
`elastic.out(1, 0.5)` (playful overshoot — sparingly).
---
## Step 4 — Shader transitions (23 max)
Use HyperFrames' built-in shader blocks at scene boundaries. Pick by mood:
| Shader | Mood |
| -------------------------- | ------------------------------------- |
| `flash-through-white` | Energetic, optimistic, pop |
| `whip-pan` | High-energy, sports/news cut |
| `cinematic-zoom` | Reveal, magnification, "let me show you" |
| `glitch` | Tech, edgy, glitch-pop |
| `ripple-waves` | Soft, organic, lifestyle |
| `light-leak` | Warm, nostalgic, film-like |
| `cross-warp-morph` | Smooth scene-to-scene continuity |
| `chromatic-radial-split` | Retro tech, VHS aesthetic |
| `swirl-vortex` | Disorienting, dream sequence |
Hard cuts everywhere else. A good rule: shader at the beginning, shader at
the climax, shader at the end. Anything more is over-decorated.
---
## Step 5 — Self-critique (Open Design's 5-dim gate)
Before emitting `<artifact>`, score yourself 15 across:
- **Philosophy** — Is the visual stance coherent with the brief and the
active DS, or is it generic?
- **Hierarchy** — Does each scene have a single dominant element? Is
reading order obvious?
- **Detail** — Do shader/eases/durations match the mood, or are they
defaulted?
- **Function** — Does the timeline play smoothly when the engine seeks?
Are all scene `data-start`s tiled? Does total `data-duration` match?
- **Innovation** — Is there at least one moment that wouldn't appear in a
generic AI render?
Anything under 3/5 is a regression — fix and rescore. Two passes is normal.
---
## Step 6 — Output contract
Emit exactly two files inside `<artifact>`:
### `index.html` — the composition
```html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title><!-- from brief --></title>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<style>
:root { /* bound from active DESIGN.md */ }
html, body { margin: 0; background: var(--bg); color: var(--ink); font-family: var(--font-body); }
#stage { position: relative; width: 100vw; aspect-ratio: 16/9; overflow: hidden; }
.scene { position: absolute; inset: 0; opacity: 0; }
.scene.clip { /* HyperFrames toggles visibility per playhead */ }
.scene-content { position: absolute; inset: 0; display: grid; place-items: center; padding: 6vmin; }
/* + per-scene overrides */
</style>
</head>
<body>
<div id="stage" data-composition-id="my-video" data-start="0" data-width="1920" data-height="1080" data-duration="20">
<div class="scene clip" data-start="0" data-duration="3" data-track-index="0">
<div class="scene-content"><!-- scene 1 content --></div>
</div>
<div class="scene clip" data-start="3" data-duration="4" data-track-index="0">
<div class="scene-content"><!-- scene 2 content --></div>
</div>
<!-- ... -->
</div>
<script>
const tl = gsap.timeline({ paused: true });
// === SCENE 1 ===
tl.from(".scene[data-start='0'] .scene-content > *", { y: 30, autoAlpha: 0, duration: 0.6, ease: "power3.out", stagger: 0.08 }, 0.2);
// === SCENE 2 ===
tl.from(".scene[data-start='3'] .scene-content > *", { y: 30, autoAlpha: 0, duration: 0.6, ease: "power3.out", stagger: 0.08 }, 3.2);
// ...
window.__timelines = window.__timelines || {};
window.__timelines["my-video"] = tl;
</script>
</body>
</html>
```
### `preview.html` — the local-preview shim
```html
<!doctype html>
<html><head><title>Preview</title>
<style>html,body{margin:0;background:#111;color:#eee;font:14px ui-sans-serif} iframe{border:0;width:100vw;height:100vh}</style>
</head><body>
<iframe id="f" src="index.html"></iframe>
<script>
const f = document.getElementById('f');
// Forward HyperFrames preview tokens (frame=, paused=, …) into the iframe
const u = new URL('index.html', location.href);
for (const [k,v] of new URL(location.href).searchParams) u.searchParams.set(k, v);
f.src = u.toString();
</script>
</body></html>
```
Save both files into the project's `cwd` (Open Design has already set this
to `.od/projects/<id>/`). The agent can immediately run:
```bash
npx hyperframes lint # should pass with zero errors
npx hyperframes preview # opens the studio
npx hyperframes render # writes MP4
```
---
## Anti-AI-slop blacklist (HyperFrames-specific)
- **No purple gradients on dark backgrounds** unless the brief explicitly
names that aesthetic.
- **No generic emoji icons** — use inline SVG or DS-provided iconography.
- **No "10× faster" / "AI-powered" filler copy** — write the user's actual
words or use honest placeholders (`` or labelled grey blocks).
- **No invented brand colors** — read from the active DS or the user's
attachment, never from memory.
- **No identical card grids** for every scene — at least 3 distinct layout
postures across the video.
- **No wall-clock JS animations** — `setTimeout`, `setInterval`,
`requestAnimationFrame`-driven animation breaks deterministic seeking. GSAP
timeline only. (Library-clock animations like Anime.js, Motion One, and
Lottie are supported via [HyperFrames' Frame Adapter](https://hyperframes.heygen.com/concepts/frame-adapters)
pattern, but stick to GSAP for first-draft handoffs unless the brief
requires another runtime.)
---
## When to defer to the Claude Design instructions
For these advanced areas, treat
[`claude-design-hyperframes.md`](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/claude-design-hyperframes.md)
as the canonical reference and follow its patterns verbatim:
- The full skeleton catalog (Skeletons AD)
- Complete shader-block insertion patterns
- HDR / wide-gamut color handling
- Audio-reactive animation (`hf-seek` + `window.__hfAudio`)
- Captions / TTS integration
- The `hyperframes add` registry (50+ blocks and components)
This skill stays focused on what Open Design needs at emission time — the
structural rules, the active-`DESIGN.md` binding, and the 5-dim self-critique
that's specific to OD's prompt stack.
+175
View File
@@ -0,0 +1,175 @@
---
title: Open Design
description: "Create HyperFrames video drafts in Open Design — the open-source, BYOK Claude-Design alternative — then refine in any AI coding agent."
---
[Open Design](https://github.com/nexu-io/open-design) is an open-source, local-first alternative to Claude Design. It runs on your laptop with `pnpm tools-dev`, deploys the web layer to Vercel, and delegates to whichever coding-agent CLI you already have on your `PATH` (Claude Code, Codex, Cursor Agent, Gemini CLI, OpenCode, Qwen, Copilot, Hermes, Kimi, Pi) — or to any OpenAI-compatible BYOK endpoint.
Open Design produces a **valid first draft** of a HyperFrames composition — palette, scene content, GSAP entrance tweens, mid-scene activity, and shader transitions. You then download the project and refine in any AI coding agent with linting and live preview.
## Get started
<Steps>
<Step title="Download the instruction file">
Open [`open-design-hyperframes.md`](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/open-design-hyperframes.md) on GitHub and click the download button (↓) to save it.
</Step>
<Step title="Run Open Design locally">
```bash
git clone https://github.com/nexu-io/open-design.git
cd open-design
pnpm install
pnpm tools-dev run web
# open the web URL printed by tools-dev
```
</Step>
<Step title="Drop the file into a skill, or attach it to chat">
**Recommended:** copy `open-design-hyperframes.md` to `skills/hyperframes-handoff/SKILL.md` inside the Open Design repo. The daemon auto-discovers it on the next request and exposes it as a skill in the picker. **Or:** start a new chat and attach the file directly — Open Design reads attachments natively.
</Step>
<Step title="Describe your video">
Pick the `hyperframes-handoff` skill (or your active prototype skill), pick a design system or visual direction, and type the brief. Include screenshots, brand assets, or a palette if you have them.
</Step>
<Step title="Save the project to disk">
Open Design writes `index.html`, `preview.html`, `README.md`, and a `DESIGN.md` snapshot into `.od/projects/<id>/`. Click **Save to disk** or download as a project ZIP.
</Step>
<Step title="Refine in any AI coding agent">
The Open Design project folder is already a real on-disk working directory. Hand it off to Claude Code, Cursor, Codex, or any agent with terminal access:
```bash
cd .od/projects/<id>
npx skills add heygen-com/hyperframes # install skills (one-time)
npx hyperframes lint # should pass with zero errors
npx hyperframes preview # open the studio
```
</Step>
</Steps>
<Tip>
**Drop into `skills/`, don't paste into chat.** Open Design's daemon reads `SKILL.md` files at request time and injects the side files (templates, references) automatically. A pasted URL or chat attachment works, but the skill path gives you the full pre-flight pipeline (template injection + 5-dimensional self-critique gate).
</Tip>
## Which setup to use
| Surface | Recommended setup |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Open Design (open-source) | Drop [`open-design-hyperframes.md`](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/open-design-hyperframes.md) into `skills/hyperframes-handoff/SKILL.md` |
| Claude Code | `npx skills add heygen-com/hyperframes`, then use `/hyperframes` |
| Cursor / Codex / Gemini CLI | `npx skills add heygen-com/hyperframes` |
| Claude Design (closed-source) | Attach [`claude-design-hyperframes.md`](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/claude-design-hyperframes.md) to your chat |
## How it works
The instruction file gives Open Design **pre-valid HTML skeletons** — the structural rules (data attributes, timeline registration, scene visibility, preview token forwarding) are already embedded. Open Design fills in the creative work:
1. **Palette + typography** — driven by the active `DESIGN.md` (Open Design ships 72 systems out of the box, plus 5 deterministic visual directions when no brand is named) bound onto `:root`
2. **Scene content** — text, images, layout inside `.scene-content` wrappers
3. **Animations** — GSAP entrance tweens and mid-scene activity
4. **Transitions** — hard cuts for most scenes, shader transitions at 2-3 key moments
Open Design's [5-dimensional self-critique](https://github.com/nexu-io/open-design/blob/main/apps/web/src/prompts/discovery.ts) runs before emission, so the artifact arrives lint-clean and your coding agent can start refining immediately without structural fixes.
## Example prompts
<CardGroup cols={1}>
<Card title="Feature announcement">
```text
Use the hyperframes-handoff skill. I just shipped dark mode for my app.
Make me a 15-second Instagram reel announcing it.
- App name: Taskflow
- Primary color: #6C5CE7
- The vibe is clean, minimal, dark
- Key stat: "47% of users requested this"
```
</Card>
<Card title="Founder pitch (BYOK with your own CLI)">
```text
Use the hyperframes-handoff skill on Codex CLI. 25-second LinkedIn video.
Problem: Sales teams waste 3 hours/day on manual CRM updates.
Solution: AutoCRM — AI that logs every call, email, and meeting.
Traction: 200+ teams, $1.2M ARR, 18% MoM growth.
CTA: autocrmhq.com
Use the Linear design system. Professional but not corporate.
```
</Card>
<Card title="Brand-driven (use one of the 72 design systems)">
```text
Use the hyperframes-handoff skill. Design system: Stripe. 10-second reel.
One big number: "$4.2 billion processed in Q1 2026"
Dark background, the number animates up from zero. Subtle, confident.
End with logo placeholder and "stripe.com"
```
</Card>
<Card title="Sparse brief (let it ask)">
```text
Use the hyperframes-handoff skill. 30-second launch video for Orbit.
```
Open Design's `RULE 1` always opens with a `<question-form>` before emitting code, so a sparse brief turns into one short question form (surface · audience · tone · brand · scale) instead of an AI-freestyle render.
</Card>
</CardGroup>
## What to include in your prompt
Open Design reads inputs in this order of reliability: **active DESIGN.md > attachments > pasted content > web research > URLs**.
| Input type | What it gives Open Design |
| --- | --- |
| Active design system (72 shipped, switchable from picker) | Full 9-section spec (color, typography, spacing, layout, components, motion, voice, brand, anti-patterns) — strongest source |
| Screenshots / PDFs / brand guides | Palette, typography, UI patterns, tone — read by the agent natively |
| Pasted hex codes, typefaces, copy | Authoritative for what they cover |
| Brand name (well-known) | Open Design can `WebFetch` blogs, press, Wikipedia |
| SPA URL (React/Vue homepage) | Returns near-empty shell — pivot to blog/press instead |
The more specific your prompt, the better the output. Pick a design system or visual direction up front, then describe content.
## Why use Open Design vs. Claude Design
| | Claude Design | **Open Design** |
| --- | --- | --- |
| License | Closed | Apache-2.0 |
| Cost | Pro / Max / Team | BYOK (free with your own CLI / API key) |
| Form factor | Web (claude.ai) only | Web app + local daemon (or deploy to Vercel) |
| Agent runtime | Anthropic only (Opus 4.7) | 10 CLI adapters + OpenAI-compatible BYOK proxy |
| Skills | Proprietary | 31 file-based `SKILL.md` bundles, droppable |
| Design systems | Proprietary | 72 shipped `DESIGN.md` systems |
| Filesystem-grade workspace | ❌ | ✅ Real `cwd`, real `Read` / `Write` / `Bash` / `WebFetch` |
If you want the Claude Design loop without lock-in, Open Design is the same artifact-first mental model — open, local, BYOK.
## Known limitations
- **Render still happens locally** — Open Design produces the HTML; `npx hyperframes render` and HDR encoding still need FFmpeg + Node 22+ on your machine.
- **In-pane preview is sandboxed iframe** — full browser playback is reliable; for frame-accurate scrubbing use `npx hyperframes preview` after handoff.
- **Shader passthrough requires WebGL** — same as the Claude Design path; Open Design's iframe sandbox supports it.
- **Skill pre-flight is daemon-side** — if you bypass the skill picker and paste raw HTML into chat, you lose the side-file injection and 5-dim critique gate. Use the skill.
## The handoff to your coding agent
Open Design's project folder is the agent's `cwd`. There's no "export then re-import" step — open Claude Code, Cursor, Codex, or any AI coding agent against the same directory:
```bash
cd .od/projects/<your-project-id>
npx skills add heygen-com/hyperframes # one-time setup
npx hyperframes lint # verify structure
npx hyperframes preview # open the studio
```
Then iterate the same way as the Claude Design path:
- "Make scene 3's entrance snappier"
- "Add a counter animation to the stat in scene 5"
- "Tighten the pacing — scenes 4 and 6 feel too long"
- "Change the shader on transition 2 to glitch"
## Next steps
<CardGroup cols={2}>
<Card title="Claude Design Guide" icon="message" href="/guides/claude-design">
The closed-source flavor of the same workflow — useful when you don't have a CLI on your laptop.
</Card>
<Card title="Prompt Guide" icon="message" href="/guides/prompting">
More prompt patterns for HyperFrames across Claude Code, Claude Design, Open Design, and other agents.
</Card>
</CardGroup>
+139
View File
@@ -0,0 +1,139 @@
---
title: Performance
description: "How to keep preview playback smooth and diagnose expensive compositions."
---
Preview plays your composition in real time, so any frame that takes longer than 33ms (at 30fps) shows up as stutter. This page covers the patterns that blow that budget and how to spot them.
## Preview vs. render
Render captures frames one at a time and stitches them into a video. Slow frames make the render take longer, but you never see the pauses — you watch the finished mp4.
Preview does the same work in real time. If a frame takes 200ms to paint, you see a 200ms freeze.
This is why "render looks fine, preview stutters" is expected for paint-heavy compositions. It doesn't mean preview is broken — it means individual frames are too expensive for real-time playback.
## Expensive CSS patterns
These are the patterns that most often cause preview to drop below 30fps.
### backdrop-filter: blur()
Each `backdrop-filter: blur(radius)` sampled over a large area forces the compositor to read pixels from behind the element, run a blur kernel across them, and composite the result. Cost scales with both the blurred area and the radius.
Stacked blur layers multiply the cost. Eight layers at progressively larger radii (1, 2, 4, 8, 16, 32, 64, 128px) will happily take 200ms per frame over a 1920x1080 region on mid-tier GPUs.
**What to do:**
- Keep stacked layers to 2-3 maximum, with manually tuned radii
- Avoid `blur(128px)` or `blur(64px)` over large areas — the biggest radii dominate the cost
- For a static blur, render it once into a PNG and use a regular `<img>` overlay
### filter: blur() and filter: drop-shadow()
Same story as `backdrop-filter` but applied to the element itself rather than behind it. Fine on small elements, expensive on large ones.
### Shadows on many elements
`box-shadow` and `text-shadow` on a few elements are fine. On dozens of elements that also animate, the compositor re-rasterizes each shadowed layer on every frame.
### Large gradients with mask-image
Combined with `backdrop-filter`, `mask-image` can force additional compositor passes. If you have both on the same element, consider whether you need both.
## Image sizing
Image source resolution matters more than file size. Chrome decodes JPEGs and PNGs to raw RGBA bitmaps before displaying them — a decoded bitmap is:
```
bitmap_bytes = width × height × 4
```
A 7000×5000 source image is 140MB decoded, regardless of whether the JPEG on disk is 2MB or 5MB.
**Rule of thumb:** resize source images to at most 2x the canvas dimensions. For a 1920x1080 canvas, 3840x2160 source images are already overkill. Anything above that is paying for memory and texture-upload cost that never shows on screen.
```bash Terminal
# ImageMagick one-liner to downsize a directory of images
mogrify -path resized -resize 3840x3840\> *.jpg
```
## Measuring a slow composition
Don't guess — measure. Chrome DevTools has everything you need.
<Steps>
<Step title="Run preview">
Start the preview server and open it in Chrome:
```bash Terminal
npx hyperframes preview
```
</Step>
<Step title="Open DevTools → Performance">
`Cmd+Option+I` (macOS) or `Ctrl+Shift+I` (Linux/Windows), then switch to the **Performance** tab.
</Step>
<Step title="Record during playback">
Hit the record button, click play in the preview, let it run 3-5 seconds through the jank-prone scene, then stop recording.
</Step>
<Step title="Read the main thread track">
Look for long tasks (red-flagged in the timeline). Expand the tallest bars and check what Chrome labels them:
- **Composite Layers / Paint** with a large duration = compositor cost (backdrop-filter, shadows, large textures)
- **Decode Image** = image decode on first paint (rare in Chrome 131+, images decode off-thread by default)
- **Layout / Recalculate Style** = layout thrashing from script
- **Script** = JS work (rare for compositions, check author scripts)
</Step>
</Steps>
Once you know which category dominates, you know what to change.
<Tip>
A composition that runs at 60fps in isolation but stutters only during specific scenes is usually a composite-cost problem. Check which layers become visible during those scenes.
</Tip>
## When preview is unavoidable slow
Some compositions are legitimately too expensive for real-time playback. If you've reduced what you can and preview still stutters, render-to-mp4 and watch the output is a fine workflow — render is still accurate.
```bash Terminal
npx hyperframes render --quality draft --output preview.mp4
```
Draft quality renders fast and is visually close to the final render for everything except encoder-level detail.
## WebM encode speed
Transparent WebM output uses FFmpeg's `libvpx-vp9` encoder. VP9 is CPU-heavy, so short overlay renders can spend most of their wall time in the encode stage even when frame capture is fast.
HyperFrames sets `-cpu-used 4` for VP9 by default. On a devbox check using an 8s 1280×720, 15fps VP9-alpha encode, explicit `-cpu-used 4` cut encode time from 6.3s to 2.6s versus libvpx's default, with SSIM 0.9986 and PSNR 50.5dB against the default encode. Your composition and host CPU will move those numbers, but the direction is consistent: higher values trade some compression efficiency for faster WebM encodes.
Tune per deployment with:
```bash Terminal
PRODUCER_VP9_CPU_USED=2 npx hyperframes render --format webm --output overlay.webm
```
For one-off local renders, pass the same value directly:
```bash Terminal
npx hyperframes render --format webm --vp9-cpu-used 2 --output overlay.webm
```
Valid values are integers from `-8` to `8`; HyperFrames clamps out-of-range values before passing them to FFmpeg.
## Next Steps
<CardGroup cols={2}>
<Card title="Troubleshooting" icon="wrench" href="/guides/troubleshooting">
Environment, tooling, and rendering issues
</Card>
<Card title="Common Mistakes" icon="triangle-exclamation" href="/guides/common-mistakes">
Composition pitfalls that break rendering
</Card>
<Card title="Rendering" icon="film" href="/guides/rendering">
Rendering modes, options, and flags
</Card>
<Card title="CLI Reference" icon="terminal" href="/packages/cli">
Full list of CLI commands
</Card>
</CardGroup>
+221
View File
@@ -0,0 +1,221 @@
---
title: The Pipeline
description: "The 7-step pipeline for producing any Hyperframes video: capture, design, strategy & messaging, storyboard + script, voiceover, build, validate."
---
Every well-structured Hyperframes video flows through the same 7 steps, whether it starts from a website, a PDF, a CSV, or a blank page. Each step produces a named artifact that the next step depends on, so your AI agent (and you) always know what's done, what's next, and where the creative decisions live on disk.
This pipeline is the backbone of the [website-to-video workflow](/guides/website-to-video), but it's just as useful when you're scripting a brand reel from scratch, turning research notes into a launch teaser, or learning Hyperframes for the first time. Most of the production-grade [launch videos](/launch-videos) HeyGen ships are organized this way.
## The seven steps
Each step produces an artifact that feeds the next:
| # | Step | Output | What happens |
|---|-------------------------|-----------------------------------------|-------------------------------------------------------------------------|
| 1 | **Capture** | `capture/` | Extract screenshots, design tokens, fonts, assets, animations from a source |
| 2 | **Design** | `DESIGN.md` | Brand reference: colors, typography, component stylings, spacing, iteration guide |
| 3 | **Strategy & Messaging**| — | Align on video type, style, the ONE message, narrative arc, and audience |
| 4 | **Storyboard + Script** | `STORYBOARD.md` + `SCRIPT.md` | Concept-first storyboard and narration script, written together |
| 5 | **VO + Timing** | `narration.wav` + `transcript.json` | TTS audio with word-level timestamps |
| 6 | **Build** | `compositions/*.html` | Animated HTML compositions, one per beat |
| 7 | **Validate** | Snapshot PNGs + `lint`/`validate` pass | Visual verification and runtime checks before delivery |
<Tip>
Not every project uses every step. A no-narration brand reel skips Step 5; a hand-authored composition skips Steps 1-2. But the order matters: scene durations come from narration, animation choices come from the storyboard, and the storyboard depends on the design reference. Skip a step only when you don't need its artifact downstream.
</Tip>
## Project layout
A typical project directory after the pipeline runs:
```
my-video/
├── capture/ # Step 1, only present when capturing a source
│ ├── screenshots/ # scroll-000.png, scroll-001.png, …
│ ├── assets/ # downloaded images, SVGs, fonts
│ ├── extracted/ # tokens.json, visible-text.txt, asset-descriptions.md
│ ├── AGENTS.md # capture summary for AI agents
│ └── CLAUDE.md
├── DESIGN.md # Step 2, brand cheat sheet
├── SCRIPT.md # Step 3, narration backbone
├── STORYBOARD.md # Step 4, beat-by-beat creative plan
├── narration.wav # Step 5, TTS audio
├── narration.txt # Step 5, exact spoken text (with pronunciation subs)
├── transcript.json # Step 5, word-level timestamps
├── compositions/ # Step 6, one HTML file per beat
│ ├── beat-1-hook.html
│ ├── beat-2-story.html
│ └── …
├── snapshots/ # Step 7, visual verification PNGs
├── renders/ # optional final MP4 outputs
└── index.html # root project file wiring compositions into a timeline
```
Capture artifacts stay in `capture/` so they're cleanly separated from the build outputs. Everything downstream lives at the project root.
## Step 1: Capture
**Output:** `capture/`
When the video is grounded in an existing source (a website, a brand site, a competitor reference), start with capture. Hyperframes ships a built-in capture command for websites:
```bash
npx hyperframes capture https://example.com -o my-video/capture
```
This extracts screenshots at every scroll depth, pixel-sampled color palettes, the CSS font stack (and downloaded woff2 files), images and SVGs with semantic names, Lottie animations, and detected animations on the page. Optional [Gemini vision enrichment](/guides/website-to-video#enriching-captures-with-gemini-vision) adds AI-powered descriptions of every captured asset.
For sources that aren't websites (PDFs, decks, CSVs, notes), capture isn't a literal command. It's the step where you gather assets into `capture/` so later steps can reference paths instead of inlining content.
**Gate:** You can describe the source's visual identity in one or two sentences and name its top colors, fonts, and standout assets.
## Step 2: Design
**Output:** `DESIGN.md` in the project root
`DESIGN.md` is the brand cheat sheet. It encodes the visual identity factually so every downstream decision can reference exact colors, fonts, and components instead of inventing them. It's a reference document, not a creative plan. The creative work happens in the storyboard.
A typical `DESIGN.md` has five sections:
| Section | What it captures |
|---------|------------------|
| **Visual Theme** | 3-5 sentences describing the brand's visual personality — dark/light, contrast, mood, what makes it distinctive |
| **Quick Reference** | Colors (8-12 HEX values with semantic roles and WCAG contrast ratios) and fonts (families, weights, roles, file paths) |
| **Component Stylings** | Exact CSS-level specs for 6-12 components the brand uses: buttons, cards, containers, distinctive UI patterns |
| **Spacing & Layout** | Base spacing unit, scale with usage, max-width / grid, and breakpoint strategy |
| **Iteration Guide** | Do's and don'ts, common failure modes, and rules for modifying the design in later steps |
`DESIGN.md` is also the input format for [Open Design](/guides/open-design) and [Claude Design](/guides/claude-design); both produce a `DESIGN.md` you can drop into a Hyperframes project.
**Gate:** `DESIGN.md` exists with all five sections filled in from real captured data (or chosen deliberately for greenfield projects).
## Step 3: Strategy & Messaging
**Output:** Alignment on video type, duration, style, and — critically — the ONE message and narrative arc
Before any creative decisions, align with the user on the story this video must tell. Parse the user's prompt first — they probably already gave you the video type and style. Only ask about things they didn't specify. If the prompt is detailed enough, confirm the direction in one message and move to Step 4.
The questions to resolve: what type of video (social ad, product demo, brand reel, etc.), what style and energy, what's the ONE thing this video must communicate, what narrative arc serves that message, and whether narration is wanted.
**Gate:** Video type, duration, format, and the message and narrative arc are locked. Without those, Step 4 can't write a concept-first storyboard.
## Step 4: Storyboard + Script
**Outputs:** `STORYBOARD.md` + `SCRIPT.md` in the project root
Write the storyboard concept-first: message → narrative arc → beats that serve the arc → techniques per beat → brand accents pass at the end. Then write the narration script to match. The storyboard and script are written together — the storyboard drives the script, not the other way around.
`STORYBOARD.md` tells the engineer (human or agent) exactly what to build for each beat: mood, camera, animations, transitions, assets, depth layers, sound effects. It's where the creative choices get pinned down.
Each beat in `STORYBOARD.md` typically covers:
| Field | What it specifies |
|------------------|-------------------|
| Timing | `0.0s - 5.8s`, taken from `transcript.json` once Step 5 runs |
| Narration line | The exact words spoken during this beat |
| Mood & camera | One sentence describing the feel and the shot |
| Assets | Which captured images, icons, and fonts go in this beat, referenced by path |
| Techniques | 2-3 picks from the [techniques library](https://github.com/heygen-com/hyperframes/blob/main/skills/hyperframes/references/techniques.md): SVG path drawing, Canvas 2D, CSS 3D, per-word typography, Lottie, video compositing, typing effects, variable fonts, MotionPath, velocity transitions, audio-reactive |
| Transitions | How this beat enters from the previous one and exits to the next |
| SFX | Short, specific sound effects (e.g. _"woosh on logo entry, soft tick on counter"_) |
The storyboard typically opens with a global-direction block: format, voiceover direction, style basis, and guardrails that apply to every beat. `SCRIPT.md` contains the narration backbone: **hook** (one sentence that earns attention), **story** (what the product or topic is), **proof** (numbers, components, customers), **CTA** (one clear action). For videos without narration, `SCRIPT.md` becomes a per-beat copy plan with on-screen text and timing notes.
**Gate:** `STORYBOARD.md` + `SCRIPT.md` exist with beat-by-beat direction, an asset audit that names every file used, and user approval of the plan.
## Step 5: VO and timing
**Outputs:** `narration.wav` (or `.mp3`), `narration.txt`, `transcript.json`
Generate the TTS narration, then transcribe it for word-level timestamps. Those timestamps are the source of truth for every beat duration downstream.
```bash
npx hyperframes tts SCRIPT.md --voice af_nova --output narration.wav
npx hyperframes transcribe narration.wav
```
| File | What it contains |
|------------------|------------------|
| `narration.wav` | The TTS audio that ships with the final render |
| `narration.txt` | The exact spoken text with pronunciation substitutions applied (`API` → `A P I`, `$2T` → `two trillion`). Distinct from `SCRIPT.md` so you can regenerate the audio later with a different voice without redoing the substitutions. |
| `transcript.json`| `[{ text, start, end }]` for every word. Every later step reads this for timing. |
Hyperframes ships multiple TTS adapters (Kokoro, ElevenLabs, HeyGen); see [`/media-use`](/guides/prompting) for the skill that picks one. After generating audio, update `STORYBOARD.md` with the real beat boundaries from `transcript.json`.
**Gate:** `narration.wav`, `narration.txt`, and `transcript.json` exist. `STORYBOARD.md` beat timings reference real timestamps, not estimates.
## Step 6: Build
**Output:** `compositions/<beat-name>.html`, one HTML file per beat
This is where the storyboard becomes runnable HTML. Each composition is a self-contained file that imports captured assets by path, uses the exact colors and fonts from `DESIGN.md`, and animates with the techniques the storyboard picked.
For multi-beat videos, spawn a focused sub-agent per beat. Each one gets fresh context, the storyboard section for its beat, the asset paths it needs, and the relevant technique references. That produces noticeably better output than building every beat in one long-running context.
After each composition is built, run a self-review for layout, asset placement, and animation quality. The [`/hyperframes-core`](/guides/prompting) skill encodes the composition rules — required `class="clip"` attributes, `data-*` attribute semantics — and [`/hyperframes-animation`](/guides/prompting) covers GSAP timeline registration and adapter registries.
**Gate:** Every composition is self-reviewed. No overlapping elements, no misplaced assets, no static images sitting unanimated.
## Step 7: Validate
**Outputs:** `snapshots/frame-*.png`, lint and validate passing with zero errors
Three checks before delivery:
```bash
npx hyperframes lint # static HTML structure checks
npx hyperframes check # one browser session: runtime errors, layout, motion, contrast
npx hyperframes snapshot my-video --at 2.9,10.4 # PNGs at beat midpoints
```
`lint` catches missing attributes, timeline registration issues, tween conflicts, and CSS-transform vs. GSAP conflicts. `validate` loads each composition in headless Chrome and surfaces runtime JS errors, missing assets, and failed network requests. `snapshot` captures frames at specific timestamps so you can _see_ your output without a full render.
The pipeline delivers the localhost Studio URL as the handoff. Your AI agent runs `npx hyperframes preview` and shares the project URL. Rendering to MP4 is on-demand:
```bash
npx hyperframes render --output my-video.mp4
```
For personalized or catalog outputs, render the same validated composition with `--batch rows.json --output "renders/{name}.mp4"` and use the generated `manifest.json` as the delivery checklist.
**Gate:** `lint` and `validate` pass with zero errors. Snapshot frames look right. The Studio preview URL is ready to share.
## Iterating
The pipeline is built around named artifacts on disk so you can re-enter anywhere without re-running everything:
- To rework the creative plan, edit `STORYBOARD.md`: change a beat's mood, swap an asset, retime the entrance, then ask the agent to rebuild just that beat.
- For surgical tweaks, open a composition file directly (e.g. `compositions/beat-3-proof.html`) and adjust animations, colors, or layout. `npx hyperframes preview` shows changes live.
- To rebuild one beat from scratch, prompt the agent: _"Rebuild beat 2 with more energy. Use the product screenshot as full-bleed background."_ It reads `STORYBOARD.md`, `DESIGN.md`, and the transcript, then regenerates just that file.
- To swap the voice without redoing Step 3, re-run TTS against `narration.txt`, which already has the pronunciation substitutions baked in.
Each artifact is a checkpoint, so you can stop, hand off to a human reviewer, or come back tomorrow and the agent still has everything it needs to keep going.
## When to use the pipeline
The pipeline is the recommended structure for:
- Capturing a website with the [/website-to-video](/guides/website-to-video) skill, which follows it end-to-end.
- Shipping a product launch. Most of the [HeyGen launch videos](/launch-videos) use this artifact layout.
- Any narrative video with three or more beats, where a storyboard pays for itself.
- Learning Hyperframes, because the artifacts leave every creative decision inspectable on disk.
For a 5-second one-shot animation, a single hand-authored composition is fine; the pipeline is overhead you don't need. The rough cutoff: if a non-author needs to understand _why_ a beat looks the way it does, write it down in `STORYBOARD.md`.
## Next steps
<CardGroup cols={2}>
<Card title="Website to Video" icon="globe" href="/guides/website-to-video">
The full website-to-video workflow built on this pipeline.
</Card>
<Card title="Prompting" icon="comment" href="/guides/prompting">
How to invoke the pipeline through your AI agent.
</Card>
<Card title="Launch Videos" icon="rocket" href="/launch-videos">
Real production projects organized around this pipeline.
</Card>
<Card title="CLI Reference" icon="terminal" href="/packages/cli">
Every command the pipeline calls.
</Card>
</CardGroup>
+298
View File
@@ -0,0 +1,298 @@
---
title: Prompt Guide
description: "How to prompt Claude Code, Cursor, Codex, Google Antigravity, GitHub Copilot CLI, and other AI agents to author Hyperframes compositions — with copy-pasteable examples and vocabulary tables."
---
Hyperframes is built for AI agents — compositions are plain HTML, the CLI is non-interactive, and the framework ships [skills](https://github.com/vercel-labs/skills) that teach agents the patterns docs alone don't cover. This guide shows how to prompt agents effectively once skills are installed — the vocabulary that changes output, the iteration patterns that save time, and the rules that prevent breakage.
## One-time setup
Install the skills in your project (or globally for your agent):
```bash
npx skills add heygen-com/hyperframes
```
The installer shows a picker. Select the **core skills** below — every project needs them. In Claude Code, restart the session after installing; the skills register as **slash commands**. Start at `/hyperframes`: it orients you to the whole surface and routes "make me a video" requests to the right workflow.
**Core skills — install all of these**
| Slash command | What it loads |
| ----------------------- | -------------------------------------------------------------------------- |
| `/hyperframes` | **Read first.** The entry skill — capability map + video router; sends "make me a video" intent to the right workflow |
| `/hyperframes-core` | Composition contract — HTML structure, `data-*` attributes, clips, tracks |
| `/hyperframes-animation`| All animation — motion rules, scene blueprints, transitions, and the runtime adapters (GSAP, Lottie, Three.js, Anime.js, CSS, WAAPI, TypeGPU) |
| `/hyperframes-creative` | Creative direction — design spec, palettes, typography, narration, beats |
| `/hyperframes-cli` | Dev-loop CLI — `init`, `lint`, `validate`, `inspect`, `preview`, `render`, `doctor` |
| `/media-use` | Asset preprocessing — `tts`, `transcribe`, `remove-background` |
| `/hyperframes-registry` | Block and component installation via `hyperframes add` |
| `/general-video` | The general authoring workflow — fallback for any video that doesn't match a specific workflow below |
**Optional workflows — add the ones that match your inputs** (`/hyperframes` routes to whichever you've installed)
| Slash command | Input → output |
| ------------------------ | --------------------------------------------------------------------------- |
| `/product-launch-video` | A product URL / brief / script → launch or promo video |
| `/website-to-video` | A general website / URL → a video of the site (tour / showcase / social clip) |
| `/faceless-explainer` | Arbitrary text (no URL) → faceless explainer with its own TTS narration |
| `/pr-to-video` | A GitHub PR → code-change explainer |
| `/embedded-captions` | An existing talking-head video → the same footage with captions / subtitles |
| `/talking-head-recut` | An existing talking-head video → footage packaged with designed graphic cards |
| `/motion-graphics` | A short, unnarrated, design-led motion graphic (logo sting, kinetic type, stat / chart) |
| `/music-to-video` | A music track (audio file or video) → a beat-synced video (lyric, slideshow, or kinetic promo) |
| `/slideshow` | A presentation / pitch deck / interactive deck — discrete slides, fragment reveals, branching |
| `/remotion-to-hyperframes` | Port an existing Remotion (React) composition to HyperFrames HTML |
<Tip>
To skip the picker and install everything (core + every workflow) in one shot, run `npx skills add heygen-com/hyperframes --all`. And start Hyperframes prompts with `/hyperframes` (or invoke the skill another way for non-Claude agents) — it loads the routing + composition context explicitly so the agent picks the right workflow and gets the rules right the first time.
</Tip>
## Claude Design
Claude Design uses a different setup. Download [`claude-design-hyperframes.md`](https://github.com/heygen-com/hyperframes/blob/main/docs/guides/claude-design-hyperframes.md) from GitHub (click the ↓ button), then **attach it to your chat** (don't paste the URL — file attachments produce better output):
```text
Use the attached skill. 25-second LinkedIn video for my startup.
Problem: Sales teams waste 3 hours/day on manual CRM updates.
Solution: AutoCRM — AI that logs every call, email, and meeting.
Traction: 200+ teams, $1.2M ARR, 18% MoM growth.
CTA: autocrmhq.com
```
Claude Design produces a valid first draft (brand identity, scene content, animations, transitions). Download the ZIP and refine in any AI coding agent with `npx hyperframes preview` running. See the [Claude Design guide](/guides/claude-design) for the full workflow.
## The two prompt shapes
Most successful Hyperframes prompts fall into one of two shapes.
### Cold start — describe the video
You tell the agent what you want from scratch. Best for greenfield work where you have the creative direction in your head.
> Using `/hyperframes`, create a 10-second product intro with a fade-in title over a dark background and subtle background music.
> Make a 9:16 TikTok-style hook video about [topic] using `/hyperframes`, with bouncy captions synced to a TTS narration.
Cold-start prompts work best when you specify:
- **Duration** (e.g. "10 seconds", "30s", "5 scenes of 3s each")
- **Aspect ratio** ("16:9", "9:16 vertical", "1:1 square") — defaults to 1920x1080 otherwise
- **Mood / style** ("minimal Swiss grid", "warm grain analog", "high-energy social")
- **Key elements** (title, lower third, captions, background video, music)
### Warm start — turn context into a video
You give the agent something to work with — a URL, a doc, a CSV, a transcript — and ask it to synthesize that into a video. This is where Hyperframes shines because the agent does the research/summarization step *and* the production step in one flow.
> Take a look at this GitHub repo https://github.com/heygen-com/hyperframes and explain its uses and architecture to me using `/hyperframes`.
> Summarize the attached PDF into a 45-second pitch video using `/hyperframes`.
> Read this changelog and turn the top three changes into a 30-second release announcement video using `/hyperframes`.
> Turn this CSV into an animated bar chart race using `/hyperframes`.
Warm-start prompts produce richer, more grounded videos because the agent is writing about *something specific* instead of inventing copy.
## Iterating
Hyperframes is a conversation. After the first render, talk to the agent the way you'd talk to a video editor — don't re-prompt from scratch:
> Make the title 2x bigger.
> Swap to dark mode.
> Add a fade-out at the end and a lower third at 0:03 with my name and title.
> The captions are too small and they overlap the lower third. Move them up and shrink them.
> Replace the background music with `assets/track.mp3`.
The agent already has the composition open and the skills loaded — small targeted edits produce better results than long re-specifications.
## Vocabulary that changes output
The skills map natural-language adjectives to specific framework settings. Using the right word gets you the right result without specifying technical details.
### Motion & easing
Describe how motion should *feel* and the agent picks the matching GSAP ease:
| Say this | Agent uses | Feels like |
| ----------- | ---------------- | ------------------------------ |
| smooth | `sine` / `power1`| Natural deceleration |
| snappy | `power4.out` | Quick and decisive |
| bouncy | `back.out` | Overshoots then settles |
| springy | `elastic.out` | Oscillates into place |
| dramatic | `expo.out` | Fast start, long glide |
| dreamy | `sine.inOut` | Slow, symmetrical |
**Timing shorthand:** fast (0.2s) = energy, medium (0.4s) = professional, slow (0.6s) = luxury, very slow (12s) = cinematic.
### Caption tones
Describe the *energy* of your captions and the agent picks matching typography, size, and animation:
| Tone | Typography | Animation | Size range |
| ------------ | ---------------------- | ------------ | ---------- |
| Hype | Heavy weight fonts | Scale-pop | 7296px |
| Corporate | Clean sans-serif | Fade + slide | 5672px |
| Tutorial | Monospace | Typewriter | 4864px |
| Storytelling | Serif | Slow fade | 4456px |
| Social | Rounded, playful | Bounce | 5680px |
```
"Hype-style captions with scale-pop"
"Calm, elegant subtitles with slow fades"
"Karaoke-style word highlighting"
```
Per-word styling also works:
```
"Make brand names larger with accent color"
"Add bounce to emotional keywords"
"Highlight numbers differently"
```
### Transitions
Every multi-scene composition benefits from transitions. Describe the energy level:
| Energy | CSS option | Shader option |
| ------- | ---------------- | ------------------- |
| Calm | Blur crossfade | Cross-warp morph |
| Medium | Push slide | Whip pan |
| High | Zoom through | Glitch, ridged burn |
Or describe by mood:
```
"Warm transitions for this wellness brand"
"Cold, clinical transitions for tech"
"Playful bouncy transitions"
"Dramatic zoom for the reveal"
```
### Audio-reactive animation
Map audio frequency bands to visual properties. The agent uses these defaults:
| Audio band | Maps to | Visual effect |
| ---------- | --------- | ------------------- |
| Bass | `scale` | Pulse on the beat |
| Treble | `glow` | Shimmer intensity |
| Amplitude | `opacity` | Breathing |
| Mids | `borderRadius` | Shape morphing |
```
"Make the text pulse with the beat"
"Add bass-driven scale to the logo"
"Create glow that responds to treble"
```
<Tip>
Keep audio-reactive effects subtle for text (36% intensity). Go bigger for backgrounds (1030%).
</Tip>
### Marker highlights
Hand-drawn emphasis effects for text:
| Mode | Effect | Best for |
| ----------- | ------------------ | ------------- |
| `highlight` | Marker sweep | Key phrases |
| `circle` | Hand-drawn ellipse | Single words |
| `burst` | Radiating lines | Hype moments |
| `scribble` | Chaotic scratch | Rough emphasis|
| `sketchout` | Cross-hatch lines | Crossing out |
```
"Add a marker highlight sweep on 'revolutionary'"
"Circle this keyword with hand-drawn effect"
"Add burst lines around 'AMAZING'"
```
### Text-to-speech voices
HyperFrames supports three TTS providers: **HeyGen** (Starfish voices, requires sign-in via `npx hyperframes auth`), **ElevenLabs** (requires API key), and **Kokoro** (free, runs locally, no API key needed). The agent asks which provider to use — or picks automatically in autonomous mode. Describe the content and the agent picks a voice, or request one directly:
| Content type | Kokoro voices |
| ------------- | -------------------------- |
| Product demo | `af_heart`, `af_nova` |
| Tutorial | `am_adam`, `bf_emma` |
| Marketing | `af_sky`, `am_michael` |
```
"Generate narration for this script"
"Create voiceover with a professional female voice"
"Add TTS with British male voice at 1.1x speed"
"Use HeyGen TTS for this narration"
```
### Rendering quality
| Quality | Use for |
| ---------- | ------------------------ |
| `draft` | Fast iteration |
| `standard` | Review and feedback |
| `high` | Final delivery |
```
"Quick draft render"
"Render at high quality"
"Export as transparent WebM"
```
## Rules to know
The skills enforce these automatically, but if you hand-edit compositions or debug issues, these are the rules that matter:
1. **Register all timelines** on `window.__timelines` — the renderer can't seek animations it doesn't know about.
2. **Video elements must be `muted`** — audio goes in separate `<audio>` elements so the renderer can mix it.
3. **No `Math.random()`** — random values produce different frames on each render, breaking determinism. Use a seeded PRNG (e.g. mulberry32) if you need pseudo-random values.
4. **Synchronous timeline construction** — no `async`/`await` or `fetch()` during GSAP timeline setup.
5. **Timed elements need `class="clip"`** — plus `data-start`, `data-duration`, and `data-track-index`.
6. **Add entrance animations to every scene** — elements appearing without animation feel broken on video.
7. **Add transitions between scenes** — jump cuts between scenes are almost always unintentional in composed video.
<Warning>
Rules 15 are technical requirements — breaking them produces incorrect renders. Rules 67 are best practices that the skills apply by default. You can override them when you have a reason to.
</Warning>
## Anti-patterns
Things that cause friction (or wrong output):
- **Don't ask for React / Vue components.** Hyperframes compositions are plain HTML with `data-*` attributes and a GSAP timeline. Asking for "a React component for the intro" forces the agent to translate later.
- **Don't ask for 4K or 60fps unless you need it.** Defaults (1920×1080, 30fps) render fast and look great. Higher specs slow rendering meaningfully.
- **Don't skip the slash command.** Without `/hyperframes`, the agent may guess at HTML video conventions instead of using the framework's actual rules (`class="clip"` on timed elements, `window.__timelines` registration, etc.).
- **Don't paste long error logs into the prompt without context.** Run `npx hyperframes check` first — lint catches structural issues, validate catches runtime errors (JS exceptions, missing assets, contrast problems).
- **Don't assume the agent knows your assets.** Mention file paths explicitly (`assets/intro.mp4`, `assets/logo.png`) — the agent will check what's there but a hint speeds it up.
## Recommended workflow
1. `npx hyperframes init my-video` — scaffold a project (skills install automatically)
2. Open the project in Claude Code (or Cursor / Codex)
3. Prompt with `/hyperframes` and one of the shapes above
4. `npx hyperframes preview` — watch in the browser as the agent edits
5. Iterate with small targeted prompts
6. `npx hyperframes render --output final.mp4` when you're happy
## Next steps
<CardGroup cols={2}>
<Card title="Quickstart" icon="rocket" href="/quickstart">
Build and render your first video
</Card>
<Card title="Common Mistakes" icon="circle-exclamation" href="/guides/common-mistakes">
Pitfalls the linter can't catch
</Card>
<Card title="GSAP Animation" icon="wand-magic-sparkles" href="/guides/gsap-animation">
Add fade, slide, scale, and custom animations
</Card>
<Card title="Catalog" icon="grid-2" href="/catalog/blocks/data-chart">
50+ ready-to-use blocks and components
</Card>
</CardGroup>
+424
View File
@@ -0,0 +1,424 @@
---
title: Remove Background (transparent video)
description: "Remove the background from a video or image and drop it into any composition as a transparent overlay."
---
Background removal — also called *matting* in VFX — separates a foreground subject (typically a person) from its background. The output is a video with an alpha channel: fully transparent where the background was, opaque where the subject is. Drop it into any HyperFrames composition as a `<video>` tag and the subject floats over whatever you put behind them.
The CLI ships a built-in `remove-background` command that runs locally — no API keys, no cloud upload, no green screen.
## Quick Start
<Steps>
<Step title="Verify ffmpeg is installed">
The pipeline needs `ffmpeg` and `ffprobe` for decode + encode. Most systems already have them; if not:
```bash Terminal
# macOS
brew install ffmpeg
# Ubuntu / Debian
sudo apt install ffmpeg
```
Confirm with `npx hyperframes doctor` — both should be green.
</Step>
<Step title="Remove the background from your video">
```bash Terminal
npx hyperframes remove-background subject.mp4 -o transparent.webm
```
On the first run, the CLI downloads ~168 MB of model weights to `~/.cache/hyperframes/background-removal/models/`. Subsequent runs reuse the cache.
Output:
```
◇ Removed background from 240 frames in 38.4s (6.3 fps, CoreML) → ./transparent.webm
```
</Step>
<Step title="Drop it into a composition">
The output is a standard VP9-with-alpha WebM. Chrome's `<video>` element decodes the alpha plane natively — no special player needed:
```html composition.html
<div class="scene">
<!-- background layer -->
<img src="city.jpg" class="bg" />
<!-- transparent subject floats on top -->
<video src="transparent.webm" autoplay muted loop playsinline></video>
</div>
```
Render the composition with the usual `hyperframes render`.
</Step>
</Steps>
## How it works
The pipeline runs four stages, all locally:
```
ffmpeg decode → u²-net_human_seg inference → alpha composite → ffmpeg encode
(raw RGB) (320×320 mask, then upsampled) (VP9-alpha)
```
The model is **u²-net_human_seg** (MIT license, ~168 MB ONNX). It runs through `onnxruntime-node` with the best-available execution provider on your machine: CoreML on Apple Silicon, CUDA on NVIDIA, CPU otherwise.
The output is encoded with the exact ffmpeg flags Chrome's `<video>` element needs to decode alpha — `-pix_fmt yuva420p` plus the `alpha_mode=1` metadata tag. Get those wrong and the alpha plane is silently discarded by browsers.
## Output formats
| Extension | Codec | When to use | Size (4s @ 1080p) |
|-----------|-------|-------------|-------------------|
| `.webm` (default) | VP9 with alpha | Drop into `<video>` for HTML5-native transparent playback | ~1 MB |
| `.mov` | ProRes 4444 with alpha | Editing round-trip in Premiere / Resolve / Final Cut | ~50 MB |
| `.png` | PNG with alpha | Single-image cutout (only when the input is also a single image) | varies |
```bash Terminal
npx hyperframes remove-background subject.mp4 -o transparent.webm # web playback
npx hyperframes remove-background subject.mp4 -o transparent.mov # editing
npx hyperframes remove-background portrait.jpg -o cutout.png # still image
```
## Layer separation: emit the cutout and the background plate together
Pass `--background-output` (alias `-b`) to write a *second* transparent video alongside the cutout. Same source RGB, alpha is the *inverse* mask — opaque where the surroundings were, transparent where the subject is. The result is a clean two-layer separation in a single inference pass:
```bash Terminal
npx hyperframes remove-background subject.mp4 \
-o subject.webm \
--background-output plate.webm
```
| Output | Alpha | Use it as |
| ------ | ----- | --------- |
| `subject.webm` | Mask — subject opaque | Foreground layer (top of stack) |
| `plate.webm` | `255 mask` — subject region transparent | Background layer; place anything you want **under the subject's silhouette** between this and `subject.webm` |
Both encoders share the source W/H/fps and your `--quality` preset, so the layers are pixel-aligned. Encode cost roughly doubles; segmentation cost is unchanged.
<Tip>
**This is a hole-cut plate, not an inpainted clean plate.** The subject region in `plate.webm` is fully transparent — you have to composite something opaque under it (a graphic, a blurred copy, a different scene) to fill the hole. If you need an actual filled background where the subject was, use a video inpainter (LaMa, ProPainter, RunwayML Inpaint) — `remove-background` is not the right tool for that.
</Tip>
### Hole-cut vs. clean plate — when does the difference matter?
A **hole-cut plate** keeps the original surroundings and makes the subject region transparent. A **clean plate** fills the subject region with reconstructed background — produced by a separate inpainting model. Display each alone over black:
| | Hole-cut plate (this command) | Clean plate (inpainted) |
| --- | --- | --- |
| Subject region | Transparent silhouette | Reconstructed background pixels |
| What you see alone | A person-shaped hole | An empty room |
| Cost | One inference pass, one extra ffmpeg encode | A second model (LaMa, ProPainter, E2FGVI) |
| Tool | `remove-background --background-output` | Outside this CLI |
The line is: **does anything ever need to be visible *through* the subject's silhouette where the subject used to be?**
| Use case | What you need |
| --- | --- |
| Text/graphics live *between* the cutout and the plate (the example above) | **Hole-cut** — the graphics fill the hole. |
| Composite the subject onto an unrelated scene | Neither. Just use `subject.webm`; the plate is irrelevant. |
| Show "the room without the person" as a real background | **Clean plate** — a hole-cut plate would show a transparent void. |
| Replace the person with a different subject (re-target) | **Clean plate** — the new subject needs real pixels under it. |
| VFX rotoscoping / "remove an extra from this take" | **Clean plate** — the canonical inpainting use case. |
If something opaque always covers the silhouette, hole-cut is sufficient and ~1000× cheaper than running an inpainter.
### The two-layer composition pattern
The two-layer pattern is functionally a drop-in for [text-behind-subject](#text-behind-subject-the-recommended-layout) without needing the original `presenter.mp4` in the project — the plate replaces it as the bottom layer:
```html
<!-- z=1 inverse-alpha plate fills everything except the subject's silhouette -->
<video src="plate.webm" data-start="0" data-duration="6" data-track-index="0" muted playsinline></video>
<!-- z=2 anything you want occluded by the subject lives here -->
<h1 style="z-index:2; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);">
MAKE IT IN HYPERFRAMES
</h1>
<!-- z=3 the cutout puts the subject back on top -->
<div class="cutout-wrap" style="position:absolute;inset:0;z-index:3">
<video src="subject.webm" data-start="0" data-duration="6" data-track-index="1" muted playsinline></video>
</div>
```
Constraints: the flag requires a video input and `.webm` or `.mov` for both outputs. It's not valid for image inputs (no temporal pairing to do) and won't accept `.png` for the plate.
## Performance
Real-world numbers from the [matting eval](https://www.heygenverse.com/a/0dd5a431-1832-4858-862d-de7fb7d02654), running u²-net_human_seg on a 4-second 1080p clip:
| Platform | Provider | ms/frame | 30-second clip |
|----------|----------|----------|----------------|
| Apple Silicon (M2 Pro / M3 / M4) | CoreML | ~263 | ~2 min |
| NVIDIA GPU (T4, A10, RTX) | CUDA | ~80150 | ~3060 s |
| Linux x86 | CPU | ~1100 | ~16 min |
| macOS Intel | CPU | ~900 | ~13 min |
Matting is offline preprocessing — you run it once per asset and reuse the output. CPU-only is slow but always works; if you reuse the same subject clip repeatedly, run it once on a faster machine and check the transparent output into your project.
## Picking a device explicitly
`--device auto` is the default and right for almost everyone. The flag exists for two cases:
- **Force CPU on a GPU box** when you want to keep the GPU free for other work, or are debugging an EP-specific issue:
```bash Terminal
npx hyperframes remove-background subject.mp4 -o transparent.webm --device cpu
```
- **Opt into CUDA** by setting `HYPERFRAMES_CUDA=1` and providing a GPU-enabled `onnxruntime-node` build (the bundled build is CPU + CoreML only, to keep the install small for the 99% of users who don't have a GPU):
```bash Terminal
HYPERFRAMES_CUDA=1 npx hyperframes remove-background subject.mp4 -o transparent.webm --device cuda
```
Run `npx hyperframes remove-background --info` to see what providers are detected on your machine and which one `auto` would pick.
## Using the transparent video in a composition
The transparent WebM behaves like any other video element. The two patterns you'll use most:
**Subject over a background image:**
```html
<div style="position: relative; width: 1920px; height: 1080px;">
<img src="background.jpg" style="position: absolute; inset: 0;" />
<video
src="transparent.webm"
autoplay
muted
loop
playsinline
style="position: absolute; right: 80px; bottom: 0; height: 90%;"
></video>
</div>
```
**Subject over a HyperFrames scene:**
```html
<!-- scene contents (text, animations, etc.) -->
<div class="title-card">Welcome</div>
<!-- subject layered on top -->
<video src="transparent.webm" autoplay muted loop playsinline class="subject"></video>
```
The cutout inherits the composition's frame rate and timeline — it plays through once during the scene's duration, so match the source clip length to the scene length when possible. If the scene is longer than the clip, `loop` handles it.
<Tip>
When rendering a composition that contains a `<video>` element, the renderer reads the source via ffmpeg internally. Transparent WebMs are decoded with the alpha plane preserved.
</Tip>
## Compositing patterns and pitfalls
The cutout webm is a **re-encoded copy** of the source mp4's RGB — the matter pipeline decodes the source to raw RGB, runs segmentation, and re-encodes to VP9 with alpha. That choice has consequences depending on what you put behind it.
### The three patterns
| Pattern | Behind the cutout | Result |
|---|---|---|
| **Cutout over a different scene** *(most common)* | Static image, gradient, animated bg, or unrelated footage | Clean. The cutout is the only source of the subject — no doubling, no edge halo. Use any `--quality`. |
| **Cutout over its own source mp4** *(text-behind-subject, talking-head with overlays)* | The same mp4 the cutout was generated from | Two RGB sources for the same person. At default `--quality balanced` (crf 18) the doubling is barely visible; at `--quality fast` (crf 30) you'll see a slight color shift / soft edge on the silhouette. Use `--quality best` (crf 12) for hero shots. |
| **Cutout over different footage of the same subject** | Another take of the same person | Looks like two overlapping people. Avoid — re-shoot or re-cut the source. |
### Text-behind-subject: the recommended layout
Putting a headline *behind* a presenter so their silhouette occludes the text:
```html
<!-- z=1 base mp4: full lobby + presenter, plays the whole scene -->
<video
id="cf-base"
data-start="0" data-duration="6" data-media-start="0" data-track-index="0"
src="presenter.mp4"
muted playsinline
></video>
<!-- z=2 headline -->
<h1 id="cf-headline" style="position:absolute;top:50%;left:50%;
transform:translate(-50%,-50%); z-index:2;
color:#fff; text-shadow:0 6px 32px rgba(0,0,0,.55);
clip-path:inset(0 0 100% 0); font-size:220px; font-weight:900;">
MAKE IT IN HYPERFRAMES
</h1>
<!-- z=3 cutout: same source, alpha around presenter, hidden until the cut.
The wrapper carries the opacity, NOT the <video> itself. -->
<div class="cutout-wrap" style="position:absolute;inset:0;z-index:3;opacity:0">
<video
id="cf-cutout"
data-start="0" data-duration="6" data-media-start="0" data-track-index="1"
src="presenter.webm"
muted playsinline
></video>
</div>
```
```js
const tl = gsap.timeline({ paused: true });
const CUT = 3.3;
// Reveal the headline early
tl.to("#cf-headline", { clipPath: "inset(0 0 0% 0)", duration: 0.6, ease: "expo.out" }, 0.25);
// At the cut, flip the cutout wrapper visible — silhouette punches through the headline
tl.set(".cutout-wrap", { opacity: 1 }, CUT);
// Sentinel: extend timeline to the composition's full duration so the renderer
// doesn't bail past the last meaningful tween.
tl.set({}, {}, 6);
```
### Two non-obvious rules
**1. Wrap the cutout video in a non-timed `<div>` and animate the wrapper, not the video.**
The framework forces `opacity: 1` on any element with `data-start`/`data-duration` while it's "active" — that's how it controls clip visibility. CSS `opacity: 0` on the video element is silently overwritten by the framework's clip lifecycle, so an opacity tween on the video element won't do anything. Wrap the video in a `<div>` that has no `data-*` attributes; the wrapper is owned entirely by your CSS/GSAP.
**2. Both videos start at `data-start="0"` and decode in sync from t=0.**
It's tempting to "late-mount" the cutout (`data-start="3.3"` to match the cut). Don't — Chrome does a seek + decoder warm-up at mount, which can land one frame off the base mp4 at the cut moment. With both videos mounted from t=0 and the cutout's wrapper opacity-animated, both decoders advance the same way and stay frame-accurate.
### Quality preset and color match
When the cutout is overlaid on its own source mp4, the encoder's CRF directly affects how visible the doubling is at edges:
| `--quality` | CRF | File size (12s @ 1080p) | When to use |
|---|---|---|---|
| `fast` | 30 | ~2 MB | Cutout sits over an unrelated background and file size matters |
| `balanced` *(default)* | 18 | ~6 MB | Recommended for text-behind-subject and any pattern that overlays on the source |
| `best` | 12 | ~12 MB | Hero shots, masters, or anything you'll re-encode downstream |
The encoder also writes BT.709 + limited-range color metadata so Chrome's YUV→RGB pipeline matches the source mp4's. Without those tags, the cutout would render slightly differently from the underlying mp4 even at lossless quality (visible red/skin shift).
## What u²-net_human_seg is and isn't good for
The model is purpose-built for **portrait / human matting**. It excels when:
- ✅ The subject is a person, head-and-shoulders or full-body
- ✅ The framing is reasonably stable (not a wide handheld shot)
- ✅ The background contrasts with the subject
It struggles or fails on:
- ❌ Non-human subjects (products, animals, objects). The model will return a mostly-empty mask.
- ❌ Very fine hair detail on a busy background. The 320×320 inference resolution means hair tips get softened — fine for most use cases, but compositors notice.
- ❌ Frame-to-frame temporal consistency. Each frame is processed independently, so static backgrounds with moving subjects can show subtle edge flicker. For most web playback this is invisible; for high-end VFX it may matter.
- ❌ Live streams or real-time capture. The pipeline is batch-only.
If your use case hits one of these, see the alternatives below.
## Alternatives — when the built-in command isn't the right tool
The CLI ships **one model on purpose** — the one that's MIT-licensed, runs everywhere, and produces production-quality output for person/portrait video. The list below leads with **free, open-source tools** that pair naturally with HyperFrames. Each entry calls out the actual catch — license, install effort, hardware needs — so you can pick the right one for your situation. Full benchmarks are in the [matting eval](https://www.heygenverse.com/a/0dd5a431-1832-4858-862d-de7fb7d02654).
### Free, open-source CLIs and libraries
These all run locally with no account, no upload, no watermark.
| Tool | When to use it | Catch |
|------|----------------|-------|
| [`rembg`](https://github.com/danielgatis/rembg) (Python, MIT) | You need a different subject type — `isnet-general-use` for objects/animals/products, `birefnet-portrait` for a quality ceiling on hair, `silueta` for a tiny ~40 MB footprint. Same family as our default model, more variety. | Requires Python + `pip install rembg`. Some bundled models (`birefnet-*`) need ~4 GB RAM and are CPU-only |
| [BiRefNet](https://github.com/ZhengPeng7/BiRefNet) (PyTorch, MIT) | Highest-fidelity portrait mattes available — visibly better hair edges than u²-net | Heavy (~4 GB inference RAM), slow on CPU, broken on Apple CoreML at the time of the eval |
| [Robust Video Matting (RVM)](https://github.com/PeterL1n/RobustVideoMatting) (PyTorch, **GPL-3.0**) | The only widely-available model with **temporal consistency** built in — no edge flicker on moving subjects. Best choice when you're matting a long talking-head clip and frame-to-frame stability matters | GPL-3.0 license is incompatible with most commercial / proprietary codebases. Read your repo's license before using |
| [Backgroundremover](https://github.com/nadermx/backgroundremover) (Python, MIT) | Simple `pip install` wrapper around u²-net; nice if you want a Python API instead of our Node CLI | Same model family as ours, no quality difference — pick whichever fits your stack |
| [ComfyUI](https://github.com/comfyanonymous/ComfyUI) (open-source, GPL-3.0 core) | Custom workflows: chain a segmentation model + alpha refinement + temporal smoothing. The right tool for tricky cases (multiple subjects, hair against a similar background, sports footage) | Setup is involved (Python, models, node graph). Worth it for repeat specialty work |
After running any of these externally, encode the output as a HyperFrames-compatible transparent WebM with:
```bash Terminal
ffmpeg -i frames-%04d.png -c:v libvpx-vp9 \
-pix_fmt yuva420p \
-metadata:s:v:0 alpha_mode=1 \
-auto-alt-ref 0 -cpu-used 4 -b:v 0 -crf 30 \
transparent.webm
```
### Free desktop / GUI tools
| Tool | When to use it | Catch |
|------|----------------|-------|
| [DaVinci Resolve — Magic Mask](https://www.blackmagicdesign.com/products/davinciresolve) | You're already editing in Resolve, want a brush-based UI with manual refinement, and need to round-trip the alpha into a larger edit | macOS / Windows / Linux desktop install. The free tier covers Magic Mask; paid Studio version unlocks higher resolutions on some features |
| [Backgroundremover.app](https://backgroundremover.app) (web) | One-off image cutout, no signup, no watermark | Single images only, not video. Free tier is hosted but the underlying tool is the same `rembg` model family |
| [PhotoRoom Background Remover](https://www.photoroom.com/tools/background-remover) (web) | Quick one-off image, polished UI, no signup | Single images only, e-commerce-tuned model |
### Web SaaS tools (free tiers, with strings)
| Tool | When to use it | Catch |
|------|----------------|-------|
| [unscreen.com](https://www.unscreen.com) | Quick one-off video, no install, drag-and-drop | **Free tier is watermarked and capped at short clips** (~10s preview). Paid removes both. Run by the team behind remove.bg |
| [RunwayML — Green Screen](https://runwayml.com) | Polished UI with brush refinement and time-aware tracking; the closest a SaaS gets to professional roto | Free tier exists but is credit-limited; serious use is a subscription |
| [Kapwing — Background Remover](https://www.kapwing.com/tools/remove-video-background) | Browser-based, integrates with their video editor | Free tier is watermarked; paid removes it |
### How to choose
- **Person / portrait video, web playback, MIT-clean** → use the built-in `hyperframes remove-background` (this is what it's tuned for).
- **Non-human subject** (product, animal, object) → `rembg` with `isnet-general-use`.
- **Maximum portrait quality, especially hair** → `BiRefNet` via Python.
- **Long video where edge flicker would be visible**, GPL is OK → `RVM`.
- **One-off marketing clip, no install** → DaVinci Resolve (free) for video, Backgroundremover.app for a still image.
- **Specialty case the off-the-shelf models can't handle** → ComfyUI with a custom graph.
## Troubleshooting
### Model download fails or hangs
The weights live on GitHub Releases (rembg's `v0.0.0` release, ~168 MB). If your network blocks GitHub or the download is interrupted:
```bash Terminal
# Manually download and drop into the cache
mkdir -p ~/.cache/hyperframes/background-removal/models
curl -L -o ~/.cache/hyperframes/background-removal/models/u2net_human_seg.onnx \
https://github.com/danielgatis/rembg/releases/download/v0.0.0/u2net_human_seg.onnx
```
Subsequent `remove-background` runs skip the download and use your local copy.
### "ffmpeg and ffprobe are required"
The pipeline shells out to ffmpeg for decode + encode. Install via `brew install ffmpeg` on macOS or `sudo apt install ffmpeg` on Debian/Ubuntu. Verify with `npx hyperframes doctor`.
### The output WebM looks fully opaque in the browser
Chrome only reads the alpha plane when the WebM is encoded as `yuva420p` with the `alpha_mode=1` metadata tag. The CLI sets both. If you re-encode the output yourself (e.g. with another ffmpeg invocation), preserve those flags:
```bash Terminal
ffmpeg -i in.webm -c:v libvpx-vp9 \
-pix_fmt yuva420p \
-metadata:s:v:0 alpha_mode=1 \
-auto-alt-ref 0 -cpu-used 4 \
out.webm
```
To verify a WebM has alpha, extract the first frame and inspect:
```bash Terminal
ffmpeg -y -c:v libvpx-vp9 -i out.webm -frames:v 1 -pix_fmt rgba -update 1 frame0.png
```
The decoded `frame0.png` should be RGBA and have non-trivial alpha values.
### CoreML is "available" but inference fails to start
The pipeline auto-falls-back to CPU if CoreML fails to bind, with a warning. If you want to skip the CoreML attempt entirely, force CPU:
```bash Terminal
npx hyperframes remove-background subject.mp4 -o transparent.webm --device cpu
```
### The alpha mask has rough or jagged edges
That usually means the source frame is high-contrast against a similar-toned background and the model's 320×320 inference resolution is showing through. Two paths forward:
1. Re-frame or re-shoot to give the subject a more contrasting background.
2. Try `birefnet-portrait` via `rembg` (see [Other open-source models](#other-open-source-models)) — it's higher quality at hair edges but slower and heavier.
## Reference
- CLI: [`hyperframes remove-background`](/packages/cli#remove-background)
- Eval: [Matting eval — v7](https://www.heygenverse.com/a/0dd5a431-1832-4858-862d-de7fb7d02654)
- Source model: [danielgatis/rembg](https://github.com/danielgatis/rembg)
- ONNX runtime: [`onnxruntime-node`](https://www.npmjs.com/package/onnxruntime-node)
+431
View File
@@ -0,0 +1,431 @@
---
title: Rendering
description: "Render compositions to MP4, MOV, WebM, GIF, or PNG sequences locally or in Docker."
---
Render your Hyperframes [compositions](/concepts/compositions) to MP4, MOV, WebM, GIF, or PNG sequences with the [CLI](/packages/cli). The rendering pipeline is frame-by-frame and seek-driven — see [Deterministic Rendering](/concepts/determinism) for how this works under the hood.
## Getting Started
<Steps>
<Step title="Verify your environment">
Run the diagnostics command to check for required dependencies:
```bash Terminal
npx hyperframes doctor
```
Expected output:
```
✓ Node.js v22.x
✓ FFmpeg 7.x
✓ FFprobe 7.x
✓ Chrome (bundled)
✓ Docker available
```
</Step>
<Step title="Preview your composition">
Before rendering, preview your composition in the browser to verify it looks correct:
```bash Terminal
npx hyperframes preview
```
</Step>
<Step title="Render to MP4">
Run the render command from your project directory:
```bash Terminal
npx hyperframes render --output output.mp4
```
Expected output:
```
⠋ Rendering composition "root" (30fps, standard quality)
✓ Captured 240 frames in 8.2s
✓ Encoded to output.mp4 (8.0s, 1920x1080, 4.2MB)
```
</Step>
</Steps>
## Rendering Modes
<Tabs>
<Tab title="Local Mode">
### Local Mode (default)
Uses Puppeteer (bundled Chromium) and your system's FFmpeg. Fast for iteration during development.
**Requires:** FFmpeg installed on your system. See [Troubleshooting](/guides/troubleshooting) if FFmpeg is not found.
```bash Terminal
npx hyperframes render --output output.mp4
```
**Pros:**
- Fast startup, no container overhead
- Can use your system GPU for Chrome/WebGL capture by default
- Can use your system GPU for hardware-accelerated encoding (with `--gpu`)
- Best for iterative development
**Cons:**
- Output may vary across platforms due to font and Chrome version differences
- Not suitable for CI/CD pipelines that require reproducibility
</Tab>
<Tab title="Docker Mode">
### Docker Mode
[Deterministic](/concepts/determinism) output with an exact Chrome version and font set. Use this for production renders and CI pipelines.
**Requires:** Docker installed and running.
```bash Terminal
npx hyperframes render --docker --output output.mp4
```
**Pros:**
- Identical output on every platform — same Chrome, same fonts, same FFmpeg
- The same pipeline used in production
- Ideal for CI/CD and automated workflows
**Cons:**
- Slower startup due to container initialization
- Browser capture stays on the deterministic software-GL path
- GPU encoding requires Docker host GPU passthrough and is not cross-platform on Docker Desktop
<Note>
Docker mode uses `chrome-headless-shell` with [BeginFrame](/concepts/determinism#how-it-works) control for frame-perfect, deterministic capture.
</Note>
</Tab>
</Tabs>
## When to Use Each Mode
| Scenario | Recommended Mode |
|----------|-----------------|
| Local development and iteration | Local |
| CI/CD pipeline | Docker |
| Sharing renders with a team | Docker |
| Quick preview export | Local |
| AI agent-driven rendering | Docker |
| Benchmarking performance | Local |
## Options
| Flag | Values | Default | Description |
|------|--------|---------|-------------|
| `--output` | path | `renders/<name>.mp4` | Output file path |
| `--format` | mp4, mov, webm, gif, png-sequence | mp4 | Output format (see [Transparent Video](#transparent-video) below) |
| `--fps` | 1-240 or rational (e.g. `30000/1001`) | 30 | Frames per second |
| `--gif-loop` | 0-65535 | 0 | GIF loop count. `0` loops forever |
| `--quality` | draft, standard, high | standard | Encoding quality preset |
| `--crf` | 051 | — | Override CRF (lower = higher quality). Cannot combine with `--video-bitrate` |
| `--video-bitrate` | e.g. `10M`, `5000k` | — | Target bitrate encoding. Cannot combine with `--crf` |
| `--video-frame-format` | auto, jpg, png | auto | Source video frame extraction format. Use `png` for UI recordings, screen captures, and color-sensitive source videos |
| `--workers` | 1-24 or `auto` | auto | Parallel render workers (see [Workers](#workers) below) |
| `--max-concurrent-renders` | 1-10 | 2 | Max simultaneous renders via the producer server (see [Concurrent Renders](#concurrent-renders) below) |
| `--batch` | path | — | JSON array of variable rows (or `{ "rows": [...] }`), rendering one output per row |
| `--batch-concurrency` | integer | 1 | Maximum batch rows to render at once |
| `--batch-fail-fast` | — | off | Stop launching new batch rows after the first row failure |
| `--gpu` | — | off | GPU encoding (NVENC, VideoToolbox, AMF, VAAPI, QSV) |
| `--browser-gpu` / `--no-browser-gpu` | — | on locally, off in Docker | Use or opt out of host GPU acceleration for local Chrome/WebGL capture |
| `--hdr` | — | off | Force HDR output even if no HDR sources are detected (MP4 only). See [HDR Rendering](/guides/hdr) |
| `--sdr` | — | off | Force SDR output even if HDR sources are detected |
| `--docker` | — | off | Use Docker for [deterministic rendering](/concepts/determinism) |
| `--quiet` | — | off | Suppress verbose output |
## Quality and Encoding
The `--quality` flag selects a preset that controls the H.264 CRF (Constant Rate Factor) and encoder speed:
| Preset | CRF | x264 Preset | Best For |
|--------|-----|-------------|----------|
| `draft` | 28 | ultrafast | Quick previews, iteration |
| `standard` | 18 | medium | General use — visually lossless at 1080p |
| `high` | 15 | slow | Final delivery, near-lossless quality |
For finer control, use `--crf` or `--video-bitrate` to override the preset:
```bash
# Near-lossless quality (CRF 15 = very high quality, large file)
npx hyperframes render --crf 15 --output pristine.mp4
# Target a specific bitrate (useful for size-constrained delivery)
npx hyperframes render --video-bitrate 10M --output controlled.mp4
```
**Tip**: The default `standard` preset (CRF 18) is visually lossless at 1080p — most people cannot distinguish it from the source. Use `--quality draft` for faster iteration, or `--quality high` / `--crf 10` when file size is no concern.
## Animated GIF
Use GIF when the output needs to autoplay inline in GitHub PRs, READMEs, issue reports, and docs pages:
```bash Terminal
npx hyperframes render --format gif --fps 15 --gif-loop 0 --output demo.gif
```
GIF output uses a two-pass FFmpeg palette encode (`palettegen` with diff statistics, then `paletteuse` with Sierra dithering) for better gradients and text edges than a single-pass conversion. GIFs are still much larger than MP4/WebM at the same dimensions, so prefer short compositions. GIF renders are capped at 30fps; pass `--fps 15` for smaller files.
GIF does not carry audio and only has 1-bit transparency. For transparent overlays, use `--format webm`, `--format mov`, or `--format png-sequence` instead.
For UI recordings, screen captures, or other source videos where saturated interface colors matter, pass `--video-frame-format png` to extract source video layers as PNG before browser capture. The default `auto` mode preserves the historical behavior: alpha-capable sources use PNG, opaque sources use JPG.
## GPU Acceleration
Hyperframes has two separate GPU acceleration surfaces:
- `--gpu` uses a hardware video encoder in FFmpeg when one is available. Supported backends include VideoToolbox on macOS, NVENC on NVIDIA systems, AMD AMF on Windows, VAAPI on Linux, and Intel QSV on supported Windows/Linux hosts.
- Browser GPU uses the host GPU for local Chrome/WebGL capture. It is enabled automatically for local renders and disabled in Docker. Use `--no-browser-gpu` to opt out.
```bash Terminal
# Add hardware FFmpeg encoding to the default local browser-GPU render
npx hyperframes render --gpu --output encoded-fast.mp4
# Opt out of hardware Chrome/WebGL capture
npx hyperframes render --no-browser-gpu --output software-browser.mp4
# Use browser GPU plus hardware FFmpeg encoding
npx hyperframes render --gpu --output gpu.mp4
```
Browser GPU capture is local-mode only. It maps to platform-native Chrome GPU backends: Metal on macOS, D3D11 on Windows, and EGL on Linux. Use `--no-browser-gpu` or Docker mode when exact cross-machine reproducibility matters more than local render speed.
## Workers
Each render worker launches a **separate Chrome browser process** to capture frames in parallel. More workers can speed up rendering, but each one consumes ~256 MB of RAM and significant CPU.
### Default behavior
By default, Hyperframes uses **CPU cores minus 2** (reserving headroom for FFmpeg encoding and your other applications):
| Machine | CPU cores | Default workers |
|---------|-----------|----------------|
| MacBook Air (M1) | 8 | 6 |
| MacBook Pro (M3) | 12 | 10 |
| 4-core laptop | 4 | 2 |
| 2-core VM | 2 | 1 |
Each worker spawns its own Chrome process (~256 MB RAM), so the per-worker overhead is significant. The maximum is 24 workers (hard ceiling).
### Choosing a worker count
```bash Terminal
# Explicit worker count
npx hyperframes render --workers 1 --output output.mp4
# Let Hyperframes pick based on your CPU
npx hyperframes render --workers auto --output output.mp4
# Maximum parallelism (use with caution on laptops)
npx hyperframes render --workers 24 --output output.mp4
```
<Tip>
Start with the default. If renders feel slow and your system has headroom (check Activity Monitor / `htop`), try increasing `--workers`. If you see high memory pressure or fan noise, reduce it.
</Tip>
### When to use 1 worker
- Short compositions (under 2 seconds / 60 frames) — parallelism overhead exceeds the benefit
- Low-memory machines (4 GB or less)
- Running renders alongside other heavy processes (video editing, large builds)
### When to increase workers
- Long compositions (30+ seconds) on a machine with 8+ cores and 16+ GB RAM
- Dedicated render machines or CI runners
- Docker mode on a well-provisioned host
## Batch Rendering
Batch rendering runs the same composition once per variables row:
```json rows.json
[
{ "name": "Alice", "headline": "Welcome, Alice" },
{ "name": "Bob", "headline": "Welcome, Bob" }
]
```
```bash Terminal
npx hyperframes render --batch rows.json --output "renders/{name}.mp4" --strict-variables
```
`--output` is a template. Use `{index}` or any scalar key from the row to make each path unique. Hyperframes preflights the full batch before rendering: malformed rows, missing placeholders, duplicate output paths, and strict variable mismatches fail before the first video starts. A `manifest.json` file is written next to the outputs with per-row status, output path, render time, duration when available, and error details.
Rows continue after failures by default so a bad data row does not discard the rest of the batch. Add `--batch-fail-fast` to stop launching new rows after the first failure, or `--json` to stream machine-readable progress events while the manifest is updated.
## Concurrent Renders
When multiple render requests hit the producer server simultaneously (common with AI agents), each render spawns its own set of Chrome worker processes. Too many concurrent renders can exhaust CPU and cause failures.
The producer server uses a **request-level semaphore** to queue renders. Only `maxConcurrentRenders` renders execute at a time — additional requests wait in a FIFO queue until a slot opens.
### Configuration
```bash Terminal
# CLI flag
npx hyperframes render --max-concurrent-renders 2 --output output.mp4
# Environment variable (for the producer server)
PRODUCER_MAX_CONCURRENT_RENDERS=2
```
The default is **2** concurrent renders, which works well on 8-core machines where each render uses 2-3 workers.
### Queue status
The producer server exposes a `GET /render/queue` endpoint that returns the current state:
```json
{
"maxConcurrentRenders": 2,
"activeRenders": 1,
"queuedRenders": 3
}
```
AI agents can poll this endpoint to decide whether to submit a render or wait.
### SSE queue events
When using the streaming endpoint (`POST /render/stream`), queued requests receive a `queued` event before rendering begins:
```json
{"type": "queued", "requestId": "...", "position": 2}
```
This lets agents report "waiting in queue" to users rather than appearing stuck.
### Choosing a concurrency limit
| Machine | CPU cores | Recommended limit |
|---------|-----------|------------------|
| 4-core VM | 4 | 1 |
| 8-core workstation | 8 | 2 |
| 16-core server | 16 | 3-4 |
| 32-core render box | 32 | 5-6 |
<Tip>
When in doubt, use 1. Renders will queue up and execute sequentially, but each one gets full CPU and finishes as fast as possible. This is better than 3 renders fighting for CPU and all finishing slowly — or failing.
</Tip>
## Transparent Video
Hyperframes supports rendering with a transparent background — useful for overlays, lower thirds, subscribe cards, and any element you want to composite over other footage in a video editor.
### Recommended format: MOV (ProRes 4444)
```bash Terminal
npx hyperframes render --format mov --output overlay.mov
```
**MOV with ProRes 4444** is the industry standard for transparent video. It works in all major video editors:
- CapCut
- Final Cut Pro
- Adobe Premiere Pro
- DaVinci Resolve
- After Effects
<Warning>
ProRes MOV files are large (typically 5-40 MB for short clips) because ProRes is a high-quality intermediate codec optimized for editing, not delivery. This is expected — the same tradeoff Remotion and professional pipelines make.
</Warning>
### Format comparison
| Format | Codec | Transparency | Video editors | Browsers | File size |
|--------|-------|-------------|---------------|----------|-----------|
| **MOV** | ProRes 4444 | Yes | CapCut, Final Cut, Premiere, DaVinci, After Effects | No | Large |
| **WebM** | VP9 | Yes | None (shows black background) | Chrome, Firefox | Small |
| **PNG sequence** | RGBA PNGs (no encoding) | Yes (lossless) | After Effects, Nuke, Fusion (image-sequence import) | No | Largest |
| **MP4** | H.264 | No | All | All | Small |
<Note>
**WebM VP9 alpha** is technically supported but all major video editors ignore the alpha channel and render transparent areas as black. Only Chromium-based browsers (Chrome, Arc, Brave, Edge) decode VP9 alpha correctly. Safari does not support it. Use MOV for editor workflows and WebM only for browser-based playback.
</Note>
### PNG sequence (no encoding)
```bash Terminal
npx hyperframes render --format png-sequence --output frames/
```
`--format png-sequence` skips the encoder entirely. The captured RGBA frames are copied to `<output>/frame_NNNNNN.png` (zero-padded) and, if the composition has audio, an `audio.aac` sidecar is written alongside. Use this when you want lossless frames — for compositing in After Effects / Nuke / Fusion, or as the input to a custom encode pipeline. `--output` is treated as a directory and is created if it doesn't exist.
### How it works
When you render with `--format mov`, `--format webm`, or `--format png-sequence`, Hyperframes:
1. Captures each frame as a **PNG with alpha channel** (instead of JPEG for MP4)
2. Sets Chrome's page background to transparent via `Emulation.setDefaultBackgroundColorOverride`
3. Encodes with an alpha-capable codec (ProRes 4444 for MOV, VP9 for WebM); `png-sequence` skips encoding and writes the captured frames directly
Your composition's HTML should **not** set a `background` on `html` or `body` — leave it unset so the transparent background comes through.
### Authoring transparent compositions
```html
<style>
/* Do NOT set background on html/body — leave them transparent */
* { margin: 0; padding: 0; box-sizing: border-box; }
[data-composition-id="my-overlay"] {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
/* No background here either */
}
</style>
```
Only the visible elements (cards, text, images) will appear in the final video. Everything else will be transparent.
### Verifying transparency
- **In a browser:** Open the MOV file — it won't play (ProRes is not a browser codec). Instead, render a WebM copy and open it in Chrome on a checkerboard background page.
- **In a video editor:** Import the MOV file and place it on a track above other footage. Transparent areas should show the footage below.
- **Online tool:** Use [rotato.app/tools/transparent-video](https://rotato.app/tools/transparent-video) to verify your MOV or WebM has working transparency.
<Note>
**`ffprobe` reports a transparent WebM as `yuv420p`, not `yuva420p`.** This is expected and does not mean the alpha is missing. VP9 stores its alpha plane in a Matroska `BlockAdditional` sidecar, not in the primary stream's pixel format, so `ffprobe` reports the primary stream as `pix_fmt=yuv420p` even when the file genuinely carries alpha. First check that the WebM declares the alpha sidecar, then force the VP9 library decoder to verify per-pixel alpha:
```bash
ffprobe -v error -show_streams out.webm | grep -i alpha_mode # ALPHA_MODE=1 or alpha_mode=1
ffmpeg -c:v libvpx-vp9 -i out.webm -frames:v 1 -f rawvideo -pix_fmt rgba frame.raw
```
A fully transparent corner pixel in `frame.raw` reads `00 00 00 00`. MOV (ProRes 4444) and `png-sequence` report their alpha directly, so this caveat is WebM-only.
</Note>
## Tips
<Tip>
Use `draft` quality during development for fast previews. Switch to `standard` or `high` for final output.
</Tip>
- Use `npx hyperframes benchmark` to find optimal settings for your system
- Docker mode is slower but guarantees [identical output](/concepts/determinism) across platforms
- For compositions with many frames, `--gpu` can significantly speed up local encoding
## Next Steps
<CardGroup cols={2}>
<Card title="Deterministic Rendering" icon="lock" href="/concepts/determinism">
Understand the determinism guarantees
</Card>
<Card title="HDR Rendering" icon="sun" href="/guides/hdr">
Render HDR10 MP4 from HDR video and image sources
</Card>
<Card title="Cloud Rendering" icon="cloud" href="/deploy/cloud">
Render on HeyGen's hosted cloud — no local Chrome or FFmpeg
</Card>
<Card title="CLI Reference" icon="terminal" href="/packages/cli">
Full list of CLI commands and flags
</Card>
<Card title="Troubleshooting" icon="wrench" href="/guides/troubleshooting">
Fix common rendering issues
</Card>
</CardGroup>
+125
View File
@@ -0,0 +1,125 @@
---
title: Skills catalog
description: "Every HyperFrames skill agents load on demand — what each one is for, and how to install them."
---
HyperFrames ships AI agent skills via [vercel-labs/skills](https://github.com/vercel-labs/skills). They teach your agent the framework-specific patterns — `data-*` attributes, GSAP timeline registration, adapter registries, runtime-owned media playback — that generic web docs don't cover. **Install them before writing compositions** and your agent produces valid HyperFrames HTML on the first try.
The skills split into three groups:
- **Router** — the entry skill that picks a workflow for any "make me a…" request — usually a video, but also a navigable deck (`/slideshow`) or a composition port (`/remotion-to-hyperframes`).
- **Creation workflows** — one per input shape (URL, PR, music track, captions, etc.). Each owns its task end-to-end: project setup, gated steps, and the final deliverable. Read by the router; you can also invoke directly if you already know which one fits.
- **Domain skills** — atomic capabilities (animation, media, CLI, registry) the workflows compose against; they never own the end-to-end task. Load one when you need that specific layer.
## Install
<Steps>
<Step title="Pick what to install (interactive picker)">
```bash
npx skills add heygen-com/hyperframes --full-depth
```
Opens a picker so you can choose which skills to add. Keep `--full-depth`: it installs the current `main`. Without it, `skills add` fetches the skills.sh registry blob, which lags `main` by hours, so you may get an older copy of a skill. Works with [Claude Code](https://claude.ai/claude-code), [Cursor](https://cursor.sh), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Codex CLI](https://github.com/openai/codex), [GitHub Copilot CLI](/guides/copilot-cli), and [Google Antigravity](/guides/antigravity).
</Step>
<Step title="Or install everything at once (skip the picker)">
```bash
npx skills add heygen-com/hyperframes --all --full-depth
```
Writes every skill to your project in one shot. Recommended when you want the full set without selecting from the picker.
</Step>
<Step title="Or install one skill at a time">
```bash
npx skills add heygen-com/hyperframes --skill <name> --full-depth
```
Pass the bare skill name (no leading `/`) — e.g. `--skill hyperframes-animation`. Useful when you want a single capability without the full set.
</Step>
<Step title="Read /hyperframes first">
Once installed, invoke `/hyperframes` in your agent. It's the capability map for everything below and routes "make me a…" intent — a video, a deck, or a composition port — to the right creation workflow based on your input.
</Step>
</Steps>
<Tip>
**Don't see a slash command after install?** Open a new agent session, or run `/skills` (Copilot CLI) / restart your agent's skill loader. Most agents pick up new skills on the next prompt.
</Tip>
## Keeping skills current
After the first install, skills stay lean and current on their own — nothing re-pulls the full set behind your back:
- **Core set** — the `/hyperframes` router, the `hyperframes-*` domain skills, and `/media-use`. `npx hyperframes init` (which every creation workflow runs when scaffolding) refreshes the core set plus anything else you already have installed. It never _expands_ the install — workflow skills you haven't used are not pulled — and re-running `init` on an up-to-date machine is a no-op. Offline (or rate-limited) it degrades gracefully and never hard-fails.
- **Workflow skills** (and `/figma`) — install **on demand**, when their workflow is first triggered. The `/hyperframes` router runs `npx hyperframes skills update <workflow>` before entering a workflow, so the one it routes to is guaranteed present.
Check or refresh manually anytime:
```bash
npx hyperframes skills check # non-zero exit if installed skills are stale or the core set is incomplete
npx hyperframes skills update # refresh the core set + everything installed (never expands)
npx hyperframes skills update <name> # also install a specific workflow / domain skill on demand
npx hyperframes skills # install the full set explicitly
```
`skills check` reports workflow skills you haven't installed as _available on demand_, not as a failure.
## Router
The single entry point. Read `/hyperframes` before invoking anything else — it knows what's available and which workflow fits your request.
| Skill | Use when |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/hyperframes` | **Read first** for any request to make / create / edit / animate / render a video, animation, or motion graphic. Capability map for the domain skills and intent router for the creation workflows below. |
## Creation workflows
One workflow per input shape. The router (`/hyperframes`) picks one of these for you — but you can invoke them directly when you already know which fits.
| Skill | Use when |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/product-launch-video` | Marketing / launching / promoting a **product** — from its URL, a brief, or a script (even if the site is only named). Up to ~3 min (sweet spot 30-90s). |
| `/website-to-video` | Turning a **general website** into a video — site tour, portfolio / landing-page showcase, social clip from the site's own visuals. |
| `/faceless-explainer` | **Explaining a topic / concept** from arbitrary text — no product, no URL, no website capture; every visual is LLM-invented (typography / abstract / diagram / data-viz). |
| `/pr-to-video` | A **GitHub pull request** (PR URL, `owner/repo#N` ref, or "this PR") → changelog / feature-reveal / fix / refactor explainer, read via the `gh` CLI. |
| `/embedded-captions` | Adding **captions / subtitles** to an existing talking-head video (footage untouched) — verbatim rail, embedded climax behind the subject, or pure-cinematic embed. |
| `/talking-head-recut` | Packaging an existing talking-head / interview / podcast video with **designed graphic overlays** — lower-thirds, data callouts, kinetic titles, pull-quotes, side panels, PiP. |
| `/motion-graphics` | A short, **unnarrated, design-led motion graphic** (~under 10s) — kinetic type, stat / chart hit, logo sting, lower-third, animated tweet / headline. MP4 or transparent overlay. |
| `/music-to-video` | A **music track** (audio file, or video to pull audio from) → a **beat-synced** video — lyric, slideshow, or kinetic promo; music drives pacing. |
| `/slideshow` | A **presentation / pitch deck / interactive deck** — discrete slides, fragment reveals, branching, hotspot navigation, presenter mode. Output is a navigable deck, not a rendered video. |
| `/general-video` | **Anything else** — longer or multi-scene pieces, brand / sizzle reel, title card, static loop, freeform composition. Input- and length-agnostic fallback. |
| `/remotion-to-hyperframes` | **Porting an existing Remotion** (React) composition's source to HyperFrames HTML. One-way migration, not creation. |
## Domain skills (loaded on demand)
Atomic capabilities the creation workflows compose against — pull one when you need that specific layer.
| Skill | Covers |
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `/hyperframes-core` | The composition contract — `data-*` timing attributes, `class="clip"`, tracks, sub-compositions, variables, framework-owned media playback, determinism rules. |
| `/hyperframes-animation` | All animation knowledge — atomic motion rules, scene blueprints, transitions, runtime adapters (GSAP / Lottie / Three.js / Anime.js / CSS / WAAPI / TypeGPU). |
| `/hyperframes-keyframes` | Seek-safe keyframe authoring across runtimes — GSAP timelines, CSS keyframes, Anime.js, WAAPI, FLIP, paths, masks, SVG morph/draw, 3D depth — plus `hyperframes keyframes` diagnostics for rendered motion. |
| `/hyperframes-creative` | Non-animation creative direction — `frame.md` / `design.md`, palettes, typography, narration, beat planning, audio-reactive visuals, composition patterns. |
| `/media-use` | The media OS — resolve any media need (BGM, SFX, image, icon, logo, voice, color grade, LUT) into a frozen local file or paste-ready block + ledger record, generate via TTS/music/image models when the catalog misses, transcribe, caption, remove backgrounds, and reuse assets across projects. One shared audio engine + manifest tracking. |
| `/hyperframes-cli` | CLI dev loop — `init`, `lint`, `check`, `snapshot`, `preview`, `render`, `publish`, `doctor`, plus AWS Lambda cloud rendering (`lambda deploy / render / progress / destroy / policies`). |
| `/hyperframes-registry` | Install and wire registry blocks and components into compositions via `hyperframes add`. Authoring a new block or component to contribute upstream. |
| `/figma` | Import Figma assets, tokens, components, and storyboard sections → reconstructed motion (frames read as states, not slides) (REST/CLI) plus Motion animations (MCP) and shaders (MCP source / native export) into a composition. |
## Source of truth
The one-line "use when" for each skill comes from its own `SKILL.md` frontmatter `description:` field in the [hyperframes repo](https://github.com/heygen-com/hyperframes/tree/main/skills). The same catalog lives in the [README's `## Skills` section](https://github.com/heygen-com/hyperframes#skills) and the repo `CLAUDE.md`; all three surfaces are kept in sync when a skill is added or renamed.
## Next steps
<CardGroup cols={2}>
<Card title="Quickstart" icon="rocket" href="/quickstart">
Install skills, scaffold a project, and render your first video.
</Card>
<Card title="Claude Design" icon="palette" href="/guides/claude-design">
Produce a valid first draft in Claude Design, then refine in any AI coding agent.
</Card>
<Card title="GitHub Copilot CLI" icon="terminal" href="/guides/copilot-cli">
Install and invoke HyperFrames skills from Copilot CLI in your terminal.
</Card>
<Card title="Google Antigravity" icon="atom" href="/guides/antigravity">
Use HyperFrames skills in Google Antigravity.
</Card>
</CardGroup>
+110
View File
@@ -0,0 +1,110 @@
---
title: Timeline Editing
description: "What you can edit in the Studio timeline today, how those edits map back to HTML, and the current limitations."
---
The Studio timeline lets you edit the parts of a HyperFrames composition that can be persisted cleanly back into source HTML.
It is not a separate project format or hidden binary state. Every supported timeline action updates the same `data-*` attributes and inline styles that your composition already uses.
## What the Timeline Can Do
- **Move clips in time** — drag a clip horizontally to update `data-start`
- **Move clips between rows** — drag a clip vertically to update `data-track-index`
- **Change visual stacking** — top timeline rows render above lower rows, and that ordering is persisted back into inline `z-index`
- **Trim the end of a clip** — drag the right handle to reduce `data-duration`
- **Trim the start of media clips** — drag the left handle on clips backed by media offsets to advance the clip start and playback offset together
## How Timeline Edits Map To Source
The timeline works directly against your HTML:
- horizontal move updates `data-start`
- vertical move updates `data-track-index`
- right trim updates `data-duration`
- media left trim updates `data-start` and `data-media-start` or `data-playback-start`
- changing row order also updates inline `z-index` so the preview matches the timeline
This means timeline editing stays inspectable and versionable. If you open the file after a move or trim, you can see the exact attributes that changed.
## Current Editing Model By Clip Type
### Generic motion / DOM clips
Examples:
- `div`
- `section`
- `aside`
- GSAP-driven cards, overlays, and text blocks
Supported:
- move the clip later or earlier on the timeline
- move the clip to another row
- trim the end of the clip
Not supported yet:
- true front trim that removes the beginning of the animation itself
### Media clips
Examples:
- `video`
- `audio`
- wrappers backed by `data-media-start` / `data-playback-start`
Supported:
- move the clip later or earlier on the timeline
- move the clip to another row
- trim the end of the clip
- trim the start of the media content itself
## Why Start Trim Is Media-Only
Media clips have a real content-offset model:
- `data-media-start`
- `data-playback-start`
Those attributes let the Studio say:
> Start this clip later on the timeline, and also start reading the media later inside the source.
Generic motion clips do not have an equivalent playback-offset model yet. For a GSAP-driven `section` or `div`, the Studio can:
- move the whole clip later by changing `data-start`
- shorten its visible window by changing `data-duration`
But it cannot yet say:
> Start this animation halfway through its timeline.
That is why generic motion clips do **not** show an interactive left trim handle. The control is hidden instead of implying behavior the runtime cannot currently represent truthfully.
<Note>
A useful mental model is: **move** changes when a clip starts, **right trim** changes when it ends, and **left trim** only appears when the clip can actually skip the beginning of its own content.
</Note>
## Stacking Rule
The Studio follows the normal timeline-editor convention:
- the visually top row renders on top
- lower rows render underneath
If you want captions, lower-thirds, or overlays to sit above other content, place them on a visually higher timeline row.
## Current Limitations
- **No true front trim for generic motion clips yet.**
You can move those clips later in time, but you cannot start their internal animation phase partway through.
- **Layering is still driven by row order plus persisted inline `z-index`.**
If a clip already has custom CSS stacking rules outside the Studio flow, keep that in mind when editing manually.
- **Timeline editing is intentionally scoped.**
The Studio currently focuses on move and trim behavior. It does not yet expose full split, slip, slide, ripple, or roll editing semantics.
## Best Practices
- Use **move** when you want an element to start later but still play its full animation.
- Use **right trim** when you want the element to end sooner.
- Use **media left trim** when you want to remove the beginning of a video or audio clip.
- Put overlays and captions on visually higher rows so they render above base footage.
+173
View File
@@ -0,0 +1,173 @@
---
title: Troubleshooting
description: "Solutions for common Hyperframes issues."
---
If your issue is about a specific coding mistake (animations not working, video cutting off early), see [Common Mistakes](/guides/common-mistakes) first. This page covers environment, tooling, and rendering issues.
<AccordionGroup>
<Accordion title='"No composition found"'>
Your directory needs an `index.html` with a valid [composition](/concepts/compositions). The root element must have a [`data-composition-id`](/concepts/data-attributes#composition-attributes) attribute.
**Fix:** Run `npx hyperframes init` to create a composition from an [example](/examples), or verify your `index.html` has the correct structure:
```html index.html
<div id="root" data-composition-id="my-video"
data-width="1920" data-height="1080">
<!-- elements here -->
</div>
```
</Accordion>
<Accordion title='"FFmpeg not found"'>
Local [rendering](/guides/rendering) requires FFmpeg installed on your system. Install it for your platform:
<CodeGroup>
```bash macOS
brew install ffmpeg
```
```bash Ubuntu/Debian
sudo apt install ffmpeg
```
```bash Windows
# Download from https://ffmpeg.org/download.html
# Add the bin directory to your PATH
```
```bash Verify installation
ffmpeg -version
```
</CodeGroup>
After installing, run `npx hyperframes doctor` to verify the CLI can find it.
<Tip>
If you cannot install FFmpeg, use [Docker mode](/guides/rendering) instead — it bundles FFmpeg inside the container: `npx hyperframes render --docker --output output.mp4`
</Tip>
</Accordion>
<Accordion title="Lint errors">
Run `npx hyperframes lint` to check for common structural issues (see [CLI: lint](/packages/cli#lint)):
| Error | Meaning |
|-------|---------|
| Missing `data-composition-id` | Root element needs this attribute. See [Compositions](/concepts/compositions). |
| Missing `class="clip"` | Timed visible elements need this class. See [Data Attributes](/concepts/data-attributes#element-visibility). |
| Overlapping timelines | Clips on the same [`data-track-index`](/concepts/data-attributes#timing-attributes) cannot overlap in time. |
| Unmuted video elements | Video elements should be `muted` unless `data-has-audio="true"` is set. |
| Deprecated attribute names | `data-layer` and `data-end` have been replaced. Check the [HTML Schema Reference](/reference/html-schema). |
</Accordion>
<Accordion title="Preview not updating">
Make sure you are editing the `index.html` in the project directory. The [preview server](/packages/cli#preview) watches for file changes and auto-reloads.
If changes still do not appear:
1. Check the terminal for errors from the preview server
2. Stop and restart `npx hyperframes preview`
3. Hard-refresh the browser: **Ctrl+Shift+R** (Windows/Linux) or **Cmd+Shift+R** (macOS)
4. Clear the browser cache if CSS changes are not reflected
</Accordion>
<Accordion title="Preview stutters or plays at a low frame rate">
**Symptom:** Preview playback is jerky or skips frames, but the rendered mp4 looks fine.
**Cause:** Individual frames are taking longer than 16-33ms to paint. Render hides this (it captures frames one at a time), preview does not.
**Common culprits, most to least frequent:**
- Stacked `backdrop-filter: blur()` layers, especially at radii above 32px
- Source images at very high resolution (above 4K) displayed in small regions
- `filter: blur()` or `filter: drop-shadow()` on large elements
- Many elements with `box-shadow` or `text-shadow` that also animate
**First thing to check:** does the stutter happen only during specific scenes, or throughout? Scene-specific stutter usually points at an element, often a blur overlay, that becomes visible in that scene.
**How to diagnose:** open Chrome DevTools, switch to the Performance tab, record a few seconds of playback, and look for long tasks labeled "Composite Layers" or "Paint". See [Performance: Measuring a slow composition](/guides/performance#measuring-a-slow-composition) for the full walkthrough.
**Temporary workaround:** render to mp4 and watch the output. Render is accurate regardless of per-frame cost.
```bash Terminal
npx hyperframes render --quality draft --output preview.mp4
```
See [Performance](/guides/performance) for the full guide on expensive CSS patterns and how to fix them.
</Accordion>
<Accordion title="Render looks different from preview">
Use `--docker` mode for [deterministic output](/concepts/determinism). Local renders may differ due to:
- **Font availability** — different fonts on different platforms cause text reflow
- **Chrome version** — local Chromium vs. Docker's pinned version can render slightly differently
- **System-specific rendering** — GPU compositing, subpixel antialiasing, etc.
```bash Terminal
npx hyperframes render --docker --output output.mp4
```
See [Rendering: When to Use Each Mode](/guides/rendering#when-to-use-each-mode) for guidance on choosing between local and Docker rendering.
</Accordion>
<Accordion title="Docker mode fails to start">
Verify Docker is installed and the daemon is running:
```bash Terminal
docker info
```
Common issues:
- **Docker not running:** Start Docker Desktop or the Docker daemon
- **Permission denied:** Add your user to the `docker` group (`sudo usermod -aG docker $USER`) and restart your shell
- **Image pull fails:** Check your internet connection; the first render downloads the Hyperframes Docker image
</Accordion>
<Accordion title="Render is slow">
Try these optimizations:
1. Use `--quality draft` during development for faster encoding
2. Run `npx hyperframes benchmark` to find the optimal worker count for your system
3. Local Chrome/WebGL GPU capture is enabled automatically; compare with `--no-browser-gpu` if troubleshooting
4. Use `--gpu` for hardware-accelerated encoding (local mode only)
5. Reduce `--fps` to 24 if 30fps is not needed
6. Check that your composition does not have unnecessary elements or overly complex animations
See [Rendering: Options](/guides/rendering#options) for all available flags.
</Accordion>
</AccordionGroup>
## System Diagnostics
Run `npx hyperframes doctor` to check your environment:
```bash Terminal
npx hyperframes doctor
```
This checks for Node.js version, FFmpeg availability, Docker status, and other requirements. If `doctor` reports issues, address them before rendering.
## Still Stuck?
If none of the above resolves your issue:
1. Run `npx hyperframes info` to gather system and project details
2. Check [GitHub Issues](https://github.com/heygen-com/hyperframes/issues) for similar reports
3. Open a new issue with the output of `npx hyperframes info` and steps to reproduce
## Next Steps
<CardGroup cols={2}>
<Card title="Common Mistakes" icon="triangle-exclamation" href="/guides/common-mistakes">
Coding pitfalls that break compositions
</Card>
<Card title="Rendering" icon="film" href="/guides/rendering">
Rendering modes, options, and tips
</Card>
<Card title="CLI Reference" icon="terminal" href="/packages/cli">
Full list of CLI commands
</Card>
<Card title="Contributing" icon="code-branch" href="/contributing">
Report bugs and contribute fixes
</Card>
</CardGroup>
+124
View File
@@ -0,0 +1,124 @@
---
title: Video Components
description: "Install production-ready video blocks and components from the HyperFrames catalog with one command — or contribute your own."
---
The HyperFrames **catalog** is a registry of 50+ production-ready video components — captions, code animations, social overlays, shader transitions, data viz, and more. Each one installs into any project with a single command and renders to a deterministic MP4. Don't rebuild a scene from scratch — install the one that already exists.
```bash Terminal
npx hyperframes add x-post
```
## Browse the catalog
<CardGroup cols={2}>
<Card title="Code Animations" icon="code" href="/catalog/blocks/code-morph">
Typing, diff, morph, highlight, scroll, and GPU 3D/particle/shader code reveals
</Card>
<Card title="Captions" icon="closed-captioning" href="/catalog/components/caption-highlight">
15 caption styles — karaoke, kinetic slam, neon, gradient, glitch, emoji pop
</Card>
<Card title="Social Overlays" icon="share-nodes" href="/catalog/blocks/x-post">
X posts, TikTok / Instagram follow cards, Reddit, Spotify, lower thirds
</Card>
<Card title="Shader Transitions" icon="bolt" href="/catalog/blocks/whip-pan">
14 WebGL transitions — whip pan, glitch, light leak, vortex, iris, burn
</Card>
<Card title="Liquid Glass &amp; VFX" icon="wand-magic-sparkles" href="/catalog/blocks/ios26-liquid-glass">
HTML-in-canvas effects — iOS 26 glass, device frames, portals, shatter, magnetic
</Card>
<Card title="Data" icon="chart-column" href="/catalog/blocks/data-chart">
Animated charts and US / world / region maps with bubbles, flows, and hexes
</Card>
<Card title="CSS Transitions" icon="right-left" href="/catalog/blocks/transitions-3d">
13 zero-dependency transitions — 3D, blur, cover, dissolve, push, radial
</Card>
<Card title="Code Snippets" icon="terminal" href="/catalog/blocks/code-snippet-dark-modern">
24 syntax-highlighted code cards and Apple-terminal themes
</Card>
</CardGroup>
Plus **Effects** (grain, vignette, shimmer, parallax), **Text Effects** (morph text, texture mask), and **Showcases** — browse them all under the [Catalog](/catalog/blocks/data-chart) tab.
## Blocks vs components
The catalog ships two kinds of item, installed the same way but wired differently:
| | **Blocks** | **Components** |
|---|---|---|
| What it is | A full standalone scene | A reusable snippet / effect |
| Has its own size & duration | Yes | No — adapts to the host |
| Installs to | `compositions/<name>.html` | `compositions/components/<name>.html` |
| Wired by | `data-composition-src` on a host element | Pasting its HTML / CSS / JS into your composition |
| Examples | `x-post`, `data-chart`, `code-morph` | `grain-overlay`, `caption-highlight`, `shimmer-sweep` |
## Install and wire
<Steps>
<Step title="Find it">
Browse the [Catalog](/catalog/blocks/data-chart) tab, or have your AI agent consult the registry — every item lists its name, description, tags, dimensions, and duration.
</Step>
<Step title="Add it">
```bash Terminal
npx hyperframes add data-chart
```
The CLI writes the files and prints a snippet to paste into your host composition.
</Step>
<Step title="Wire a block">
Blocks are standalone compositions — include them with `data-composition-src` and a timeline position:
```html index.html
<div
data-composition-id="data-chart"
data-composition-src="compositions/data-chart.html"
data-start="0"
data-duration="15"
data-track-index="1"
data-width="1920"
data-height="1080"
></div>
```
</Step>
<Step title="Wire a component">
Components are snippets — paste their HTML into your composition's markup, their CSS into your styles, and any JS into your script, then hook their timeline calls into yours.
</Step>
</Steps>
<Tip>
Using an AI agent? Install the HyperFrames skills with `npx skills add heygen-com/hyperframes` and ask it to "add a chart block" — the `/hyperframes-registry` skill discovers, installs, and wires the catalog item for you.
</Tip>
## Contribute a video component
Your agent already knows how to build video components — it writes HTML, HyperFrames renders it. The registry is where that work ships to **every** HyperFrames user. Spotted a caption style on TikTok that doesn't exist yet, or built a transition worth sharing? Add it to the catalog.
<Info>
**Quick version** — Fork the repo. Write one HTML file with a paused GSAP timeline. Add `registry-item.json`. Run `hyperframes lint` + `validate`. Publish with `npx hyperframes publish`. Open a PR.
</Info>
Every item is a single self-contained HTML file with a paused GSAP timeline — no build step, no framework. It must be deterministic (seeded randomness only, no `Date.now()` / `Math.random()`), register its timeline on `window.__timelines`, and meet the production-quality bar.
<CardGroup cols={2}>
<Card title="Contributing to the Catalog" icon="cube" href="/contributing/catalog">
The full idea → scaffold → build → validate → preview → ship workflow, the `registry-item.json` schema, the rules, and the quality bar
</Card>
<Card title="Open a component request" icon="lightbulb" href="https://github.com/heygen-com/hyperframes/issues/new">
No code needed — share a visual reference and tag it <code>component-request</code>
</Card>
</CardGroup>
## Next Steps
<CardGroup cols={2}>
<Card title="Compositions" icon="layer-group" href="/concepts/compositions">
How blocks nest into a host composition
</Card>
<Card title="Variables" icon="sliders" href="/concepts/variables">
Parameterize an installed block to stay on-brand
</Card>
<Card title="The Pipeline" icon="diagram-project" href="/guides/pipeline">
Design → plan → layout → build → validate → render
</Card>
<Card title="Contributing to the Catalog" icon="cube" href="/contributing/catalog">
Add your own block or component to the registry
</Card>
</CardGroup>
+300
View File
@@ -0,0 +1,300 @@
---
title: Video Editor Cheatsheet
description: "Fast reference for video editors and creative people directing agents, cutting timing, tweaking layouts, previewing, and publishing HyperFrames projects."
---
Use this as a fast reference when you are directing agents, cutting timing, making visual layout tweaks, previewing, and sharing HyperFrames projects.
## The Fast Loop
```bash
npx hyperframes init my-video --example blank
cd my-video
npx hyperframes preview
```
Keep the preview running while your agent edits `index.html` or files in `compositions/`. The Studio updates automatically, so you can direct the agent, scrub the result, make manual visual tweaks, then repeat.
Most production work should feel like this:
1. Ask the agent for the first cut, scene, caption pass, transition, or cleanup.
2. Use the Studio preview and timeline to check timing.
3. Use manual DOM editing for Figma-like layout tweaks: select elements, move them, and adjust visual properties directly.
4. Ask the agent to clean up or generalize anything you changed manually.
5. Lint, validate, render, and publish.
Before showing or rendering a project:
```bash
npx hyperframes lint
npx hyperframes check
npx hyperframes render --quality standard --output review.mp4
```
For fast iteration renders, use draft quality:
```bash
npx hyperframes render --quality draft --output draft.mp4
```
For final delivery:
```bash
npx hyperframes render --quality high --fps 30 --output final.mp4
```
## Terminal Shortcuts
Move around projects quickly:
```bash
pwd # show current folder
ls # list files
cd my-video # enter a project folder
cd .. # go up one folder
cd - # jump back to the previous folder
open . # open the current folder in Finder on macOS
code . # open the current folder in VS Code, if installed
```
Common HyperFrames project folders:
```bash
cd assets # source videos, images, audio
cd compositions # reusable scenes and overlays
cd .. # back to the project root
```
Run HyperFrames commands from the project root, where `index.html` lives. If you are not sure where you are, run `pwd` then `ls`. If you see `index.html`, you are in the right place.
## Preview Shortcuts
Start the Studio:
```bash
npx hyperframes preview
```
Use a different port if `3002` is already busy:
```bash
npx hyperframes preview --port 4567
```
Inside the Studio, shortcuts are grouped the same way the playbar's `⌨` panel groups them — playback first, then work-area markers, view controls, and app-level commands. Open the `⌨` panel in the playbar for an in-app cheatsheet, a frame-jump input, and live readouts of the in/out points.
### Playback
| Shortcut | Action |
| --- | --- |
| `Space` | Play or pause |
| `K` | Stop |
| `J` | Play backward (press again to shuttle faster) |
| `L` | Play forward (press again to shuttle faster) |
| `←` / `→` | Step 1 frame |
| `Shift+←` / `Shift+→` | Step 10 frames |
`J` and `L` build the classic NLE shuttle: each repeat ramps the playback rate up. Tap `K` between shuttle bursts to stop. With `K` held, tapping `J` or `L` steps one frame in that direction.
### Work area (in / out points)
| Shortcut | Action |
| --- | --- |
| `I` | Set in-point at the playhead |
| `Shift+I` | Clear in-point |
| `O` | Set out-point at the playhead |
| `Shift+O` | Clear out-point |
| `A` | Jump to in-point (or composition start if unset) |
| `E` | Jump to out-point (or composition end if unset) |
The in and out points define a **work area**. When loop is on, both forward and backward playback loop within those boundaries — useful for tightening a transition or scrubbing a single clip without trimming it. The seek bar renders a teal band between in and out, with tick markers at each point. Clear the markers from the `⌨` panel or with `Shift+I` and `Shift+O`.
### View
| Shortcut | Action |
| --- | --- |
| `Cmd+Scroll` / `Ctrl+Scroll` over the preview | Zoom the preview at the cursor |
### Application
| Shortcut | Action |
| --- | --- |
| `Shift+T` | Show or hide the timeline editor |
| `Cmd+1` / `Ctrl+1` | Switch sidebar to Compositions |
| `Cmd+2` / `Ctrl+2` | Switch sidebar to Assets |
| `Cmd+Z` / `Ctrl+Z` | Undo |
| `Cmd+Shift+Z` / `Ctrl+Shift+Z` / `Ctrl+Y` | Redo |
| `Delete` / `Backspace` | Delete the selected timeline clip or DOM element (when not typing in an editor) |
| `Escape` | Leave a sub-composition or close editor dialogs |
<Tip>
Preview uses the same runtime as rendering, so the visual frame matches the output. If preview stutters on a heavy frame but the render is clean, that is expected — preview plays in real time, render captures one frame at a time.
</Tip>
## Agent-Led Editing
Ask the agent to verify visible changes in the browser. For a user-visible edit, a good handoff is:
```
Run the preview, check it with agent-browser, take a screenshot, and render a draft MP4 to take a look at the frames with ffmpeg.
```
## Manual DOM Editing
In the Studio, you can edit the DOM visually for the final 10% of creative adjustment where dragging is faster than describing.
Use manual DOM editing for:
- moving titles, captions, product cards, logos, and overlays into position
- adjusting size, spacing, opacity, color, and other visual properties
- checking composition balance at an exact timestamp
- making Figma-like placement tweaks
Use agents for:
- creating scenes from scratch
- refactoring repeated visual patterns
- wiring GSAP timelines
- fixing broken timing, layout overflow, or render errors
- turning a manual visual tweak into reusable, clean HTML/CSS
After manual DOM edits, ask the agent to inspect the diff and keep the source clean:
```
I moved the hero title and resized the CTA manually in Studio. Inspect the changes, clean up the CSS if needed, then run lint and validate.
```
## CLI Commands Editors Use Most
| Command | Use it for |
| --- | --- |
| `npx hyperframes init my-video` | Create a new project |
| `npx hyperframes init my-video --example warm-grain` | Start from a visual template |
| `npx hyperframes init my-video --video source.mp4` | Import video and generate captions from the source audio |
| `npx hyperframes capture https://example.com` | Capture a website as source material for a video |
| `npx hyperframes preview` | Open the live Studio preview |
| `npx hyperframes lint` | Catch structural mistakes before preview or render |
| `npx hyperframes check` | Run the composition in headless Chrome to catch runtime errors |
| `npx hyperframes check` | Find text overflow and layout problems across the timeline |
| `npx hyperframes snapshot --at 1,3,5` | Save PNG checks at exact timestamps |
| `npx hyperframes render --output final.mp4` | Render the video |
| `npx hyperframes publish` | Upload the project and get a shareable HyperFrames URL |
| `npx hyperframes doctor` | Check Node.js, FFmpeg, Chrome, Docker, and other dependencies |
| `npx hyperframes docs` | Open local CLI docs |
| `npx hyperframes upgrade` | Check for a newer CLI version |
## Timing Cheatsheet
Every visible timed layer should usually be a clip:
```html
<h1
class="clip"
data-start="0"
data-duration="3"
data-track-index="0"
>
Opening title
</h1>
```
Use these attributes like timeline controls:
| Attribute | Video editor meaning |
| --- | --- |
| `data-start` | When the layer starts |
| `data-duration` | How long the layer stays active |
| `data-track-index` | Timeline track number |
| `data-media-start` | Offset into a media file |
| `data-volume` | Audio volume for an audio or video clip |
| `data-composition-src` | Nested scene or reusable overlay |
For GSAP animation, register one paused timeline per composition:
```html
<script>
window.__timelines = window.__timelines || {};
const tl = gsap.timeline({ paused: true });
tl.from("#title", { opacity: 0, y: 40, duration: 0.6 });
tl.set({}, {}, 5); // keeps the timeline at least 5 seconds long
window.__timelines["main"] = tl;
</script>
```
<Warning>
If a video cuts off early, check that the GSAP timeline is at least as long as the intended edit. The final `tl.set({}, {}, 5)` pattern is the fix.
</Warning>
## Render Presets
| Goal | Command |
| --- | --- |
| Fast iteration | `npx hyperframes render --quality draft --output draft.mp4` |
| Review link | `npx hyperframes render --quality standard --output review.mp4` |
| Final export | `npx hyperframes render --quality high --fps 30 --output final.mp4` |
| Transparent overlay | `npx hyperframes render --format webm --output overlay.webm` |
| Deterministic output | `npx hyperframes render --docker --output final.mp4` |
Use WebM for transparent overlays, captions, and lower thirds. Use `--docker` when you need pixel-consistent output across different machines.
## Publish and Share
Use `publish` when you want to share the editable project, not just the rendered MP4:
```bash
npx hyperframes publish
```
Publish zips the current project, uploads it, and prints a stable `hyperframes.dev` URL. The URL includes a claim token so the recipient can open it, claim the project, and continue editing in the web app.
```bash
npx hyperframes publish ./my-video # publish a specific folder
npx hyperframes publish --yes # skip the confirmation prompt in scripts
```
Publish expects an `index.html` at the project root. It ignores `.git`, `node_modules`, `dist`, `.next`, and `coverage`.
## What Agent Browser Is
`agent-browser` is a browser automation tool for AI agents. It opens Chrome, navigates to your preview, clicks controls, reads page state, and captures screenshots. It is how an agent proves the video preview actually works instead of only saying the code looks right.
Typical verification flow:
```bash
agent-browser open http://localhost:3002
agent-browser snapshot -i
agent-browser screenshot --screenshot-dir ./qa
```
Use it when you want the agent to open the HyperFrames Studio preview, play or scrub the video, click timeline controls, inspect visible UI text, capture screenshots for review, or record proof of a tested flow.
For editor-facing changes, keep `npx hyperframes preview` running, then have the agent use `agent-browser` against the local preview URL.
## Quick Fixes
| Problem | Command or check |
| --- | --- |
| Preview will not start | `npx hyperframes doctor` |
| Port already in use | `npx hyperframes preview --port 4567` |
| Render fails | `npx hyperframes lint` then `npx hyperframes check` |
| Need exact frame checks | `npx hyperframes snapshot --at 1,2.5,5` |
| Text overflows in the frame | `npx hyperframes check` |
| Final render is too slow | Try `--quality draft`, reduce image sizes, or lower `--fps` |
| Need to share editable project | `npx hyperframes publish` |
<CardGroup cols={2}>
<Card title="Prompt Guide" icon="wand-magic-sparkles" href="/guides/prompting">
How to direct AI agents to build better videos
</Card>
<Card title="Timeline Editing" icon="timeline" href="/guides/timeline-editing">
Timing, tracks, and GSAP timeline patterns
</Card>
<Card title="Common Mistakes" icon="circle-exclamation" href="/guides/common-mistakes">
Pitfalls the linter can't catch
</Card>
<Card title="CLI Reference" icon="terminal" href="/packages/cli">
Full command reference
</Card>
</CardGroup>
+238
View File
@@ -0,0 +1,238 @@
---
title: Website to Video
description: "Capture any website and turn it into a production video with a single prompt."
---
Give your AI agent a URL and a creative direction. It captures the site, extracts the brand identity, writes a script and storyboard, generates voiceover, builds animated compositions, and delivers a renderable video.
```
"Create a 20-second product launch video from https://linear.app.
Make it feel like an Apple keynote announcement."
```
## Getting Started
<Steps>
<Step title="Install skills">
Skills teach your AI agent how to capture websites and create HyperFrames compositions. Install once — they persist across sessions.
```bash
npx skills add heygen-com/hyperframes
```
Works with [Claude Code](https://claude.ai/claude-code), [Cursor](https://cursor.sh), [Gemini CLI](https://github.com/google-gemini/gemini-cli), and [Codex CLI](https://github.com/openai/codex).
</Step>
<Step title="Prompt your agent">
Open your agent in any directory and describe the video you want:
```
Create a 25-second product launch video from https://example.com. Bold, cinematic, dark theme energy.
```
The agent loads the skill when they see a URL and a video request, and runs the full pipeline — capture, design, strategy & messaging, storyboard + script, voiceover, build, validate.
<Note>
Agents also trigger this skill automatically when they see a URL and a video request.
</Note>
</Step>
<Step title="Preview">
```bash
npx hyperframes preview
```
Opens the video in your browser. Edits reload automatically.
</Step>
<Step title="Render to MP4">
```bash
npx hyperframes render --output my-video.mp4
```
```
✓ Captured 750 frames in 12.4s
✓ Encoded to my-video.mp4 (25.0s, 1920×1080, 6.8MB)
```
</Step>
</Steps>
<Note>
You don't need to run `npx hyperframes capture` manually — the skill instructs the agent to capture as the first step. The capture command is documented [below](#capture-command) for advanced use.
</Note>
## How the Pipeline Works
The skill follows the [Hyperframes pipeline](/guides/pipeline): seven steps, each producing a named artifact that feeds the next.
| Step | Output | What happens |
|------|--------|-------------|
| **Capture** | `capture/` | Extract screenshots, design tokens, fonts, assets, animations |
| **Design** | `DESIGN.md` | Brand reference — colors, typography, component stylings, spacing, iteration guide |
| **Strategy & Messaging** | — | Align on video type, style, the ONE message, and narrative arc |
| **Storyboard + Script** | `STORYBOARD.md` + `SCRIPT.md` | Concept-first storyboard and narration script, written together |
| **VO + Timing** | `narration.wav` + `transcript.json` | TTS audio with word-level timestamps |
| **Build** | `compositions/*.html` | Animated HTML compositions, one per beat |
| **Validate** | Snapshot PNGs + lint/validate pass | Visual verification and runtime checks before delivery |
See [the pipeline guide](/guides/pipeline) for a detailed walkthrough of each step, the contents of every generated file, and how to iterate without re-running the whole pipeline. The structure is useful for any Hyperframes project, not just website captures.
## Video Types
The prompt determines the format. Include a duration and creative direction:
| Type | Duration | Example |
|------|----------|---------|
| Social ad | 1015s | _"15-second Instagram reel. Energetic, fast cuts."_ |
| Product demo | 3060s | _"45-second demo showing the top 3 features."_ |
| Feature announcement | 1530s | _"Feature announcement highlighting the new AI agents."_ |
| Brand reel | 2045s | _"30-second brand video. Celebrate the design."_ |
| Launch teaser | 1020s | _"12-second teaser. Super minimal. Just the hook."_ |
<Tip>
Creative direction matters more than format. _"Playful, hand-crafted feel"_ or _"dark, developer-focused, show code"_ shapes the storyboard and drives every visual decision the agent makes.
</Tip>
## Enriching Captures with Gemini Vision
By default, captures describe assets using DOM context — alt text, nearby headings, CSS classes. Add a vision API key for richer AI-powered descriptions.
Create a `.env` file in your project root with **either** a [Gemini API key](https://aistudio.google.com/apikey):
```bash
echo "GEMINI_API_KEY=your-key-here" > .env
```
…**or**, if you don't have Google access, an [OpenRouter key](https://openrouter.ai/keys) — a single API that fronts many vision models:
```bash
echo "OPENROUTER_API_KEY=your-key-here" > .env
```
OpenRouter is used when its key is present (it takes priority if both are set). The default model is `google/gemini-3.1-flash-lite`; override it with `HYPERFRAMES_OPENROUTER_MODEL` (any vision-capable OpenRouter model), just as `HYPERFRAMES_GEMINI_MODEL` overrides the Gemini default.
<Tabs>
<Tab title="Without Gemini">
```
- hero-bg.png — 582KB, section: "Hero", above fold
```
The agent knows the file exists and where it was on the page, but not what it looks like.
</Tab>
<Tab title="With Gemini">
```
- hero-bg.png — 582KB, A gradient wave in purple and blue sweeps
across a dark background, creating an aurora-like effect.
```
The agent knows what the image actually shows, enabling better creative decisions in the storyboard.
</Tab>
</Tabs>
| Tier | Rate limit | Cost per image |
|------|-----------|----------------|
| Free | 5 RPM | Free |
| Paid | 2,000 RPM | ~$0.001 |
A typical capture with 40 images costs about **$0.04** on the paid tier.
## Capture Command
The skill runs capture automatically, but you can run it directly for pre-caching, debugging, or using the data outside of video production.
```bash
npx hyperframes capture https://stripe.com
```
```
◇ Captured Stripe | Financial Infrastructure → capture
Screenshots: 12
Assets: 45
Sections: 15
Fonts: sohne-var
```
| Flag | Default | Description |
|------|---------|-------------|
| `-o, --output` | `./capture` | Output directory (auto-suffixes to `./capture-2/`, `./capture-3/`, … if `./capture/` is taken) |
| `--timeout` | `120000` | Page load timeout in ms |
| `--skip-assets` | `false` | Skip downloading images and fonts |
| `--max-screenshots` | `24` | Maximum screenshot count |
| `--json` | `false` | Output structured JSON for programmatic use |
### What Gets Captured
| Data | Description |
|------|-------------|
| **Screenshots** | Viewport captures at every scroll depth — dynamic count based on page height |
| **Colors** | Pixel-sampled dominant colors + computed styles, including oklch/lab conversion |
| **Fonts** | CSS font families + downloaded woff2 files |
| **Assets** | Images, SVGs with semantic names, Lottie animations, video previews |
| **Text** | All visible text in DOM order |
| **Animations** | Web Animations API, scroll-triggered animations, WebGL shaders |
| **Sections** | Page structure with headings, types, background colors |
| **CTAs** | Buttons and links detected by class names and text patterns |
## Snapshot Command
Capture key frames from a built video as PNGs — verify compositions without a full render:
```bash
npx hyperframes snapshot my-project --at 2.9,10.4,18.7
```
| Flag | Default | Description |
|------|---------|-------------|
| `--frames` | `5` | Number of evenly-spaced frames |
| `--at` | — | Comma-separated timestamps in seconds |
| `--timeout` | `5000` | Ms to wait for runtime to initialize |
## Iterating
You don't need to re-run the full pipeline to make changes:
- **Edit the storyboard** — `STORYBOARD.md` is the creative north star. Change a beat's mood or assets, then ask the agent to rebuild just that beat.
- **Edit a composition** — open `compositions/beat-3-proof.html` directly and tweak animations, colors, or layout.
- **Rebuild one beat** — _"Rebuild beat 2 with more energy. Use the product screenshot as full-bleed background."_
See the [pipeline guide](/guides/pipeline#iterating) for more re-entry patterns.
## Troubleshooting
<AccordionGroup>
<Accordion title="Capture times out">
Increase the timeout for sites with Cloudflare or heavy client-side rendering:
```bash
npx hyperframes capture https://example.com --timeout 180000
```
</Accordion>
<Accordion title="Few assets captured">
Sites using frameworks like Framer lazy-load images via IntersectionObserver. The capture scrolls through the page to trigger loading, but very long pages may miss images near the bottom. Adding a Gemini key improves descriptions of captured assets, but doesn't increase the count.
</Accordion>
<Accordion title="Colors look wrong">
The capture uses pixel sampling combined with DOM computed styles. Dark sites should show dark colors in the palette. Check the scroll screenshots in `<output>/screenshots/` (default `./capture/screenshots/`) to see what the capture actually saw.
</Accordion>
<Accordion title="Agent doesn't find the skill">
Verify skills are installed:
```bash
npx skills add heygen-com/hyperframes
```
Lead your prompt with _"Use the /website-to-video skill"_ for the most reliable results. Agents also discover it automatically when they see a URL and a video request.
</Accordion>
</AccordionGroup>
## Next Steps
<CardGroup cols={2}>
<Card title="The Pipeline" icon="list-check" href="/guides/pipeline">
The canonical 7-step structure this workflow follows.
</Card>
<Card title="Quickstart" icon="rocket" href="/quickstart">
New to HyperFrames? Start here.
</Card>
<Card title="GSAP Animation" icon="wand-magic-sparkles" href="/guides/gsap-animation">
Animation patterns used in compositions.
</Card>
<Card title="Rendering" icon="film" href="/guides/rendering">
Render to MP4, MOV, or WebM.
</Card>
</CardGroup>