
Website To Hyperframes
- 8 installs
- 33 repo stars
- Updated July 27, 2026
- dirnbauer/webconsulting-skills
This is a copy of website-to-hyperframes by heygen-com - installs and ranking accrue to the original listing.
Helps with ai & agent building tasks.
About
website-to-hyperframes is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- website-to-hyperframes
- AI & Agent Building
- AI-coding skill
Website To Hyperframes by the numbers
- 8 all-time installs (skills.sh)
- Data as of Jul 29, 2026 (Skillselion catalog sync)
npx skills add https://github.com/dirnbauer/webconsulting-skills --skill website-to-hyperframesAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 33 |
| Last updated | July 27, 2026 |
| Repository | dirnbauer/webconsulting-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Website to HyperFrames
Capture a website, then produce a professional video from it.
Users say things like:
- "Capture https://... and make me a 25-second product launch video"
- "Turn this website into a 15-second social ad for Instagram"
- "Create a 30-second product tour from https://..."
The workflow has 7 steps. Each produces an artifact that gates the next. By default it's collaborative — gates marked 💬 stop and ask the user. If the user signals autonomous mode ("decide for me", "surprise me"), 💬 user-preference gates are skipped; see step-2-brief.md for how that propagates.
Autonomous mode is NOT "skip all gates." Auto mode covers user-preference questions (TTS provider, voice, color emphasis, beat count, music yes/no, captions yes/no — where the agent decides on the user's behalf). It does NOT cover quality-verification gates. The following remain non-skippable in auto mode:
- Asset Audit (Step 3) — viewing contact sheets and justifying USE/SKIP for each asset
- Per-beat HTML read (Step 5) — structured evidence block per beat
- DoD checklist (Step 6) — including animation-map, per-warning WCAG verification, audio/motion playback
- Honest disclosure section (Step 6) — "What I did NOT verify" must appear in your final summary
If you find yourself reasoning "auto mode says bias toward action, so I'll skip X" — and X is a verification gate, not a preference question — that reasoning is wrong. Bias toward action applies to deciding _what to build_, not to deciding _whether to verify_.
---
Step 0: Capture & Understand the Brand
Read: references/step-0-capture.md
Capture the site, then read the extracted data to understand the brand and product — what it does, who it's for, what voice it speaks in, what mood it lives in. The captured assets are a brand toolkit for later, not the building blocks the video is made from.
Gate: Site summary printed — strategy-first (what the product does, who it's for, brand voice) before the asset / color / font inventory.
---
Step 1: Brand Identity
Read: references/step-1-design.md
Write DESIGN.md — a brand cheat sheet covering the visual identity: colors, typography, component styles, layout principles. Use design-styles.json for exact computed values.
Speed option: For fast-pacing videos (billboard-per-beat), DESIGN.md can be a 50-line summary of colors + fonts + do's/don'ts — not a 300-line document. The sub-agent prompt in Step 5 pastes brand values directly, so DESIGN.md depth only matters for complex compositions.
Gate: DESIGN.md exists (any length) with at minimum: color palette, font choices, and do's/don'ts.
---
Step 2: Strategy & Messaging
Read: references/step-2-brief.md, references/capabilities.md (scan the Table of Contents — deep-dive sections only as needed)
Align with the user on what the video must communicate before talking visuals or assets. Parse the user's prompt — they probably already gave you the video type and style. Ask only what's missing: the ONE thing this video must say, the narrative arc, and the audience.
Gate: Video type, duration, format, and — critically — the message and narrative arc are locked. Without those, Step 3 can't write a concept-first storyboard.
---
Step 3: Storyboard + Script 💬
Read: references/step-3-storyboard.md
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. Present both to the user with a beat-by-beat summary. Iterate until they approve.
Gate: STORYBOARD.md + SCRIPT.md exist AND the user has approved the plan.
---
Step 4: VO, Timing + Captions 💬
Read: references/step-4-vo.md
If Step 2 said no narration — ask about background music, then skip to Step 5. Otherwise: ask the user which TTS provider (HeyGen TTS, ElevenLabs, or Kokoro), generate audio, transcribe, map timestamps to beats. Then ask about captions.
Gate: Either (a) no narration was requested and storyboard has manual beat timings, or (b) narration.wav + transcript.json exist and beat timings updated with real durations.
---
Step 5: Build Compositions
Read: The hyperframes skill (load it — every rule matters) Read: references/step-5-build.md
Build index.html and compositions following the architecture and pacing chosen in the storyboard (Step 3). Sub-agents run hyperframes lint and hyperframes snapshot on each beat before reporting back.
Gate: Every compositions/beat-N.html has been read top-to-bottom by the main agent against DESIGN.md and STORYBOARD.md. The per-beat checklist lives in step-5-build.md.
---
Step 6: Validate & Deliver
Read: references/step-6-validate.md
Lint, validate, take snapshots scaled to video length (formula: max(beats × 3, ceil(duration_seconds / 2))), and review each one. Fix issues before delivering. Deliver the localhost Studio project URL — only render to MP4 on explicit user request.
Deliver something you're proud of. Before handing off, ask yourself: would I post this on social media with my name on it? If not, fix what's wrong.
Gate: npx hyperframes lint and npx hyperframes validate pass with zero errors, and the final response includes the active Studio project URL.
---
Quick Reference
Video Types
Typical constraints by video type — use as a starting point, not a formula. Beat count should follow from the content and the narration, not from a target range.
| Type | Typical duration | Duration driver | Narration |
|---|---|---|---|
| Social ad (IG/TikTok) | 10–15s | Platform limit | Optional |
| Product demo | 30–60s | Script length | Full narration |
| Feature announcement | 15–30s | Feature complexity | Full narration |
| Brand reel | 20–45s | Music track | Optional, music focus |
| Launch teaser | 10–20s | Hook energy | Minimal |
Beat count is not in this table intentionally — it should come from the storyboard, not from "social ad = 3-4 beats." A social ad for a complex product might need 5 well-timed beats. A brand reel with one strong visual thesis might need 3.
Format
- Landscape: 1920x1080 (default)
- Portrait: 1080x1920 (Instagram Stories, TikTok)
- Square: 1080x1080 (Instagram feed)
Reference Files
| File | When to read |
|---|---|
| step-0-capture.md | Step 0 — capture, understand the brand and product, write strategy-first site summary |
| step-1-design.md | Step 1 — write DESIGN.md brand cheat sheet (5 sections, 250-350 lines; 50-line fast-path for billboard-style social ads) |
| step-2-brief.md | Step 2 — align on message, narrative arc, audience with user |
| capabilities.md | Steps 2 & 5 — full inventory of what HyperFrames can do (24 sections). Scan the TOC during the brief, deep-dive specific sections during build |
| step-3-storyboard.md | Step 3 — storyboard + script (combined) with user review gate |
| step-4-vo.md | Step 4 — TTS provider choice, generation, timing |
| step-5-build.md | Step 5 — build index.html + compositions |
| step-6-validate.md | Step 6 — lint, validate, snapshots (scaled to video length), preview |
| techniques.md | Steps 3 & 5 — 13 primitive animation techniques with code patterns (adapt, don't copy-paste) |
| html-in-canvas-patterns.md | Step 5 — complete code patterns for HTML-in-Canvas effects (lives in the hyperframes skill) |
---
Credits & Attribution
This skill is based on the excellent work by [HeyGen](https://www.heygen.com/).
Original repository: https://github.com/heygen-com/hyperframes
Copyright (c) HeyGen - HyperFrames HTML-to-video composition and workflow guidance (Apache 2.0)
Special thanks to HeyGen for their generous open-source contributions, which helped shape this skill collection. Adapted by webconsulting.at for this skill collection
SFX Credits
All sound effects in this directory are sourced from Pixabay and used under the Pixabay Content License.
The Pixabay license allows free use for commercial and non-commercial purposes without attribution, but attribution is appreciated and given here for transparency.
Files
The following .mp3 files are bundled with this skill:
chime.mp3click.mp3/click-soft.mp3error.mp3glitch-1.mp3/glitch-2.mp3/glitch-3.mp3impact-bass-1.mp3/impact-bass-2.mp3key-press.mp3notification.mp3ping.mp3pop.mp3riser.mp3sparkle.mp3typing.mp3whoosh.mp3/whoosh-short.mp3/whoosh-cinematic.mp3
See manifest.json for per-file metadata (duration, energy character, recommended use).
License
All files are distributed under the Pixabay Content License, which permits:
- Commercial and non-commercial use
- Modification and remixing
- Redistribution as part of derivative works (such as videos rendered with HyperFrames)
without any attribution requirement.
{
"chime": {
"file": "chime.mp3",
"duration": 2.5,
"description": "This is a soft, melodic chime sound effect perfect for signaling a gentle transition or a lighthearted moment in a video."
},
"click-soft": {
"file": "click-soft.mp3",
"duration": 0.37,
"description": "This is a smooth, medium-energy swoosh sound effect, perfect for transitioning between two scenes or highlighting a sleek reveal."
},
"click": {
"file": "click.mp3",
"duration": 0.37,
"description": "This is a sharp, medium-energy digital \"whoosh\" effect that works perfectly for a quick transition between video clips."
},
"error": {
"file": "error.mp3",
"duration": 1.62,
"description": "This is a sharp, medium-energy digital glitch sound that serves as an effective, abrupt transition for a video cut or error screen."
},
"glitch-1": {
"file": "glitch-1.mp3",
"duration": 2.64,
"description": "This is a high-energy, distorted digital impact sound effect that works perfectly for a sudden reveal or a punchy transition in a fast-paced edit."
},
"glitch-2": {
"file": "glitch-2.mp3",
"duration": 3.5,
"description": "This is a harsh, distorted digital glitch effect with high energy that serves as a jarring transition or a chaotic digital reveal."
},
"glitch-3": {
"file": "glitch-3.mp3",
"duration": 3.1,
"description": "This is a low-energy, subtle electronic click sound effect that works well as a minimal audio transition or a UI element for a subtle selection reveal."
},
"impact-bass-1": {
"file": "impact-bass-1.mp3",
"duration": 2.12,
"description": "This is a medium-energy, sharp electronic chime that works perfectly as an accent for a quick transition or a brief, high-tech visual reveal."
},
"impact-bass-2": {
"file": "impact-bass-2.mp3",
"duration": 2.59,
"description": "This sound effect is a shimmering, ethereal synth riser with a medium energy level, perfect for creating anticipation before a magical transition or reveal."
},
"key-press": {
"file": "key-press.mp3",
"duration": 0.4,
"description": "This is a medium-energy, distorted digital impact sound effect that works well for a sudden glitch transition or a glitchy text reveal."
},
"notification": {
"file": "notification.mp3",
"duration": 2.46,
"description": "This is a medium-energy, whimsical digital transition sound effect that fits perfectly with a playful visual reveal or a cute UI interaction."
},
"ping": {
"file": "ping.mp3",
"duration": 1.32,
"description": "This is a medium-energy, sharp electronic impact sound that works perfectly for punchy transitions or highlighting key on-screen reveals."
},
"pop": {
"file": "pop.mp3",
"duration": 0.72,
"description": "This is a medium-energy, metallic clinking sound that works perfectly as an accent or a subtle impact for a precise visual reveal or UI element."
},
"riser": {
"file": "riser.mp3",
"duration": 10.03,
"description": "This is a high-energy cinematic air whoosh transition that builds tension before suddenly cutting, making it perfect for a dramatic scene change or a quick reveal."
},
"sparkle": {
"file": "sparkle.mp3",
"duration": 1.8,
"description": "This is a bright, high-energy \"whoosh\" transition sound with a fast, ascending pitch that is perfect for a rapid scene change or a quick reveal."
},
"typing": {
"file": "typing.mp3",
"duration": 1.5,
"description": "This is a hard, high-pitched \"ding\" sound that works well as an accent for a reveal or a quick transition."
},
"whoosh-cinematic": {
"file": "whoosh-cinematic.mp3",
"duration": 5.54,
"description": "This is a medium-energy, cinematic whoosh effect, ideal for smooth scene transitions or emphasizing a quick reveal."
},
"whoosh-short": {
"file": "whoosh-short.mp3",
"duration": 0.57,
"description": "This is a medium-energy, organic wooden texture sound effect that works well as a subtle transition or a background accent."
},
"whoosh": {
"file": "whoosh.mp3",
"duration": 0.57,
"description": "This is a hard, metallic impact sound effect that works well for a sudden reveal or a punchy transition."
}
}
Beat Builder Guide
You are building ONE beat of a multi-beat video composition. This file tells you what to read, how to build, how to verify, and how to report back.
Step 1: Read and understand
Required (every beat):
1. Load the `hyperframes` skill — composition rules, data attributes, timeline contract, deterministic rendering. Read the whole skill. 2. [capabilities.md](capabilities.md) — full inventory of HyperFrames capabilities (24 sections). Read the Table of Contents first, then deep-dive sections your beat needs. 3. The beat spec the main agent gave you — concept, choreography, assets, brand values, timing.
Read based on what your beat needs (pick relevant ones):
| Resource | What it covers | Read when |
|---|---|---|
| techniques.md | 13 primitive animation techniques: SVG path drawing, Canvas 2D, CSS 3D, kinetic type, variable fonts, MotionPath, etc. | Beat uses any of these techniques |
| text-effects.md | 24 named text animations from pixel-point/animate-text (separate skill — load via /animate-text for specs) | Beat has text animation |
| html-in-canvas-patterns.md | HTML-in-Canvas: iPhone/MacBook mockups, liquid glass, magnetic, portal, shatter, text cursor | Beat uses device mockups or WebGL effects on HTML |
| transitions.md | Shader transition API, HyperShader.init() pattern, all 14 WebGL shaders | Beat has shader transitions |
| transitions/ | 14 CSS transition category files: push, scale, dissolve, blur, 3D flip, light leak, distortion, grid, mechanical, destruction | Beat uses CSS transitions |
| css-patterns.md | Text markers: highlight sweeps, hand-drawn circles, burst lines, scribble, sketchout | Beat uses text emphasis/markers |
| audio-reactive.md | Bass→scale, mid→shape, treble→glow mappings | Beat reacts to music/audio |
| captions.md | Per-word karaoke, tone-adaptive styling, positioning | Beat includes captions |
| typography.md | Font hierarchy, variable fonts, responsive type scaling | Beat has complex typography |
| motion-principles.md | Velocity matching, easing philosophy, motion continuity | Beat needs polished motion design |
| dynamic-techniques.md | Counter animations, data-driven visuals, dynamic content | Beat has counters or data visualization |
| video-composition.md | Frame composition, color presence, scale, density rules | General composition quality |
Other skills you can load if needed:
/gsapor/gsap-core,/gsap-timeline,/gsap-plugins— deeper GSAP reference/animate-text— curated text animation catalog with exact JSON specs/hyperframes-registry— if you need to install and wire registry blocks/hyperframes-contrast— audit color contrast (WCAG)/lottie,/three,/waapi,/animejs,/css-animations— if beat uses these engines
Always open the captured assets folder before designing the beat:
capture/assets/svgs/— brand logos, icons, decorative marks. SVGs are infinitely scalable and stroke-animatable (path drawing, dash offset). A logo SVG drawing itself onto frame can carry an entire beat.capture/assets/— hero illustrations, screenshots, product art, gradients, photography. These are first-class beat subjects, not background decoration. A breathing hero illustration with a single line of kinetic type is a complete shot.- VIEW every image before placing text on it. Check safe zones, contrast, actual content, where the focal point sits.
If your beat spec names a captured asset, USE it. Don't substitute a CSS recreation. The user captured these from the real brand site precisely so the video carries the brand's actual visual identity.
Step 2: Build the composition
Save to the path the main agent specified (usually compositions/beat-N-name.html).
<template>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* your styles */
</style>
<div
id="beat-N-name"
data-composition-id="beat-N-name"
data-width="1920"
data-height="1080"
style="width:1920px; height:1080px; position:relative; overflow:hidden; background:#YOUR_BG;"
>
<!-- your elements -->
</div>
<script>
(function () {
var BEAT = 5.5; // MUST match data-duration on the host div in index.html
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
// your GSAP animations
window.__timelines["beat-N-name"] = tl;
})();
</script>
</template>Critical: data-composition-id, data-width, data-height on the root div MUST match the host div in index.html.
Step 3: Lint
npx hyperframes lint .Fix ALL errors. Zero errors required.
Step 4: Snapshot and verify
npx hyperframes snapshot . --frames 3READ the contact sheet (snapshots/contact-sheet.jpg). For each frame:
- Is content visible? (not black, blank, or loading)
- Is text readable, properly positioned, correct font/color?
- Are assets at the right size and position?
- Does the animation state match the beat spec at this timestamp?
If anything is wrong: fix, re-snapshot, re-check. You are done ONLY when every frame matches the spec.
Step 5: Report back honestly
After lint passes, snapshots are taken, and you've fixed every issue you saw — report back to the main agent with concrete observations. Not "0 errors, looks good." That phrasing is what got prior videos shipped with mismatched brand colors, missing logos, and headlines too small to read.
The main agent will OPEN your composition file and read it top-to-bottom to cross-check against DESIGN.md and STORYBOARD.md — does the brand bg/accent hex actually appear in your CSS, are the captured assets the storyboard called for actually referenced, is the headline ≥80px, does the GSAP timeline cover the full beat duration. You cannot pass that check by claiming things you didn't do; the file is on disk, the truth is in the file.
So in your report, name the hex codes you used, the captured asset paths you placed, the headline font-size, and the GSAP timeline's last tl.fromTo(...) timestamp. Brief, concrete, true. If anything diverges from DESIGN.md or the storyboard, say so explicitly — the main agent can decide whether to accept the divergence or send you back to fix it. Surprises caught at this hand-off cost minutes; surprises caught at Step 6 cost iterations.
FLAG protocol — required phrasing for non-blocking issues
When you find any of these, surface them as FLAGS in your report, not as conditional suggestions:
- Visual states that briefly look broken (empty containers, hanging elements, gap moments)
- Spec ambiguities you had to resolve by guessing
- Linter bugs you worked around
- Tween values you changed from the spec because they wouldn't fit
Forbidden phrasing: "if the X feels too long, you could...", "consider tweaking Y", "might want to..."
Required phrasing — concrete, actionable, with line numbers:
FLAG: at beat-local t=1.2s the doc card is visible but its inner content is still
opacity 0 — a 0.4s empty-panel window.
RECOMMENDED FIX: pull title typewriter from 1.6s → 1.4s
in compositions/beat-5-name.html line 234.The main agent MUST EITHER apply each FLAG's fix OR write a one-sentence rejection with reason. Silently dropping a FLAG is a verification failure that gets caught at Step 6 (or worse, in the user's preview).
Spec ambiguity — escalate, don't paper over
If STORYBOARD.md gives you a transition or transformation but doesn't establish the start state, do NOT guess. Examples of ambiguity worth flagging:
- "Row 1 transitions from Huly Blue to Huly Orange at 3.5s" — but Row 1's initial color isn't specified
- "Headline grows" — but the start size isn't specified
- "Cards slide in" — but the off-screen position isn't specified
- "Subhead appears after the headline" — but exact timing offset isn't specified
Required action: FLAG the ambiguity in your report verbatim:
FLAG: STORYBOARD.md beat 3 says "Row 1 transitions blue → orange at 3.5s" but
Row 1's initial color is not specified anywhere. I interpreted Row 1 starts
blue and tweened to orange. CONFIRM or correct.The main agent then confirms or corrects before Step 6 advances. Picking an interpretation silently means the build looks "fine" while diverging from intent — and the user only notices in motion.
Sub-agent diagnoses are unverified claims, not facts
When a sub-agent reports "this is a linter false positive" / "this is a known bug" / "this attribute doesn't work as documented" — those are HYPOTHESES, not findings. Sub-agents diagnose from one symptom; they don't have repo-wide context.
Before propagating any sub-agent diagnosis (e.g., applying the same "workaround" to another beat, or telling the user "this is a known bug"), do ONE of:
1. Verify by reading the source. Open the file the sub-agent claims is buggy. Confirm the bug exists. Example: "I read packages/core/src/lint/utils.ts:42 and confirmed the regex matches url(\"data:image/svg+xml...\") incorrectly. The workaround is to base64-encode the URI." 2. Disclose the unverified claim. Don't suppress it — surface it. Example: "Sub-agent for beat 2 diagnosed root_missing_composition_id as a linter false positive on inline SVG data URIs. I applied the same workaround to beat 4 WITHOUT verifying the underlying claim. Worth filing as a regression against packages/core/src/lint/utils.ts to confirm."
Forbidden: silently adopting the workaround pattern and presenting "lint passes" as evidence. If the workaround came from an unverified diagnosis, "lint passes because the diagnosis was correct AND I worked around it" and "lint passes because the diagnosis was wrong but the workaround happened to make the symptom disappear" are both possible. Without verification, you don't know which. The next session inherits the workaround AND the unverified diagnosis.
When you accept a sub-agent's divergence from spec — UPDATE the spec
If a sub-agent reports "I diverged from STORYBOARD.md because..." AND you accept the divergence, you MUST update STORYBOARD.md to reflect the actual implementation. Otherwise the spec lies about the artifact.
Examples:
- Sub-agent: "Storyboard says 'HULY' uppercase but the actual logo asset is lowercase 'huly'. I used lowercase." Accept → edit STORYBOARD.md beat N to say "huly" lowercase. Note the change inline.
- Sub-agent: "Storyboard says cells at 56px but they read too small at 1920×1080. I used 96px." Accept → edit STORYBOARD.md beat N's cell size to 96px.
- Sub-agent: "Storyboard says SFX at t=4.7s but the visual moment lands at t=5.2s; I aligned SFX to the visual." Accept → edit STORYBOARD.md SFX line to t=5.2s.
Forbidden: accepting the divergence silently and leaving the storyboard with the wrong spec. The next session reading STORYBOARD.md will trust it as ground truth. The spec is a contract; if you break the contract, update the contract.
---
Continuous motion — the most important rule
A beat is a SHOT in a film, not a webpage with entrance animations. Your GSAP timeline should have events spread across the ENTIRE beat duration — not just entrance tweens in the first 1-2 seconds followed by nothing. If an element is on screen, it should be doing something. After elements enter, add continuous hold motion: camera dolly, parallax layers moving at different speeds, secondary elements appearing mid-beat, real depth shifts.
You are building a SHOT, not a webpage
The storyboard tells you the shot framing (close-up / medium / wide / etc.) and the camera move. Implement them. A beat is a moment, not a screenshot. The distinction is what the camera is doing, not whether the subject is a UI element or a logo — a tight push-in on a real product screenshot is a shot; a centered card on a parked camera is a webpage.
Patterns that turn a shot back into a webpage:
These are defaults to avoid, not absolute prohibitions. If the storyboard genuinely calls for "the kanban app interface" or "the browser chrome" as the subject of a specific beat (a product tour, a "this is how it works" demo, a stylized window mockup for the closer), then build it. The rule is: don't reach for these patterns by default when the storyboard didn't ask for them.
- ⚠ macOS / browser window chrome reproduced in CSS — traffic-light dots, URL bars, browser tabs. Fine when the storyboard makes the chrome the subject (e.g. "stylized macOS window framing the product UI" for a closer). NOT fine when it's a frame you added around a card "to make it look like an app."
- ⚠ Full webpage layout (sidebar + header + footer + main content area) — fine when the beat is genuinely a product tour shot. NOT fine when the beat was supposed to be about _the kanban moment_ and you defaulted to drawing the whole app around it.
- ❌ Parked-camera composition — centered card with 60–120px margins on all sides and no camera move. Almost always wrong. Either give it a real push-in / dolly / parallax, or reframe.
- ❌ "Hold with breathing" implemented as
y: ±1–2pxorscale: 1.01— invisible at 1920×1080+ scale. If continuous motion is required, use camera dolly (scale 1.0 → 1.05), parallax pan (x/y ±30–80px), or progressive reveals. - ❌ Hover-state simulations — videos have no hover. If the brand uses hover effects, show the BEFORE and AFTER as discrete frames in the timeline.
- ❌ Counter pulses + dot pulses + tiny scale wobbles as the only motion during the hold — these are "I ran out of ideas" filler.
The test: if the storyboard says _"this beat is the product tour, viewer sees the app interface"_, building a CSS dashboard with chrome is correct. If the storyboard says _"this beat is the kanban moment, single card sliding home"_, drawing the full app around it is wrong. Read the beat spec carefully.
Patterns that ARE shots (do these freely):
- ✅ Captured SVG logo drawing itself stroke-by-stroke (DrawSVG / path dashoffset) — a complete opener or stinger.
- ✅ Captured hero illustration with camera dolly — push-in from 1.0 → 1.08 over 4s, focal element holds frame.
- ✅ Captured product screenshot with parallax layers — separate the foreground UI from background panels and move them at different speeds, or use HTML-in-Canvas for an iPhone/MacBook mockup.
- ✅ Captured asset as the bed, kinetic type as the punchline — the brand's hero image holds the frame while a one-line message arrives, splits, reflows.
- ✅ Composed-from-divs UI moment when the beat is specifically about that UI's interaction (a card sliding into a column, a search result resolving) — this is the legit case for CSS-only composition.
Required motion magnitudes (anything smaller is invisible at video scale):
| Motion type | Minimum magnitude |
|---|---|
| Translate (y/x) | 30px (entrance) / 8px (drift during hold) |
| Scale | 0.05 change (1.0 → 1.05 or larger) |
| Opacity | full 0 → 1 or vice versa for reveals |
| Rotate | 4° minimum to read (Dutch angles, ticks) |
| Camera dolly | scale 1.0 → 1.06 minimum over beat duration |
Required cinematography per beat (the storyboard should give you these; if it doesn't, escalate):
- A shot type (close-up / medium / wide / over-the-shoulder / Dutch)
- A camera move (dolly in/out, push, parallax pan, orbit, rack focus)
- A depth strategy (what's foreground / midground / background)
- A purpose (what specific feeling or noticing the shot delivers)
If any are missing from the beat spec, the beat is under-defined. Don't fill the gap with "centered layout + breathing" — re-read the spec, and if it's genuinely missing, ask the main agent.
Rules
- SCRIPT PLACEMENT: scripts inside
<template>, never after</template>. Scripts outside see no DOM. - GSAP FROM TRAP: never
gsap.from(el, {opacity:0})with CSSopacity:0. It animates 0→0. Usetl.fromTo(). - STYLE: avoid CSS
opacity:0on GSAP-animated elements. Use GSAP fromTo for initial states. - ASSET PATHS: project-root-relative.
capture/assets/file.png✅../capture/assets/file.png❌ - SVG VIA IMG:
<img src="logo.svg">can't inherit CSS color. Inline SVG orfilter: brightness(0) invert(1). - CSS CENTERING: no
transform: translate(-50%, -50%)with GSAP transforms. Use flexbox orxPercent/yPercent. - QUERYSELECTOR:
document.getElementById("id")with null guards. No method calls without null check. - CHARACTER SPANS:
display:inline-blockon spaces collapses them. Use or per-word spans. - COUNTERS: no
onUpdatefor numeric counters — use discretetl.set(el, {textContent: "42"}, 2.5)at timestamps.onUpdateandtl.call()ARE supported for canvas/WebGL rendering loops and character-by-character typing — see capabilities.md §10. - TIMELINE:
window.__timelines["beat-N-name"] = tlsynchronously. Key =data-composition-id. - DETERMINISTIC: no
Math.random(),Date.now(),requestAnimationFrame,repeat:-1. - Always
tl.fromTo()nottl.from()for entrances. - Never stack two transform tweens on same element at same time.
- FONTS: copy the
@font-faceblock VERBATIM from DESIGN.md's Fonts section. Do NOT guess which.woff2file belongs to which family — capture filenames are content-hashed (14d7ce3e41dcbb66-s.p.woff2) and there is no visible mapping. If DESIGN.md doesn't include exactsrc:paths per family, STOP and ask the main agent to add them; never pair an arbitrary.woff2file with a family name from memory.
Easing — pick per intent
Do NOT default to power2.out on everything.
| Intent | GSAP Ease | Use for |
|---|---|---|
| Snap (iOS feel) | power4.out | Hero text, UI elements |
| Whip overshoot | back.out(1.7) | Numbers, badges, impact |
| Soft land | expo.out | Per-word reveals, gentle entrances |
| Mechanical | power1.out | Terminal text, code typing |
| Bounce settle | elastic.out(1, 0.5) | Counters, CTA buttons |
| Dramatic | expo.inOut | Full-screen statements, hero reveals |
| Drift | "none" | Parallax, Ken Burns, camera drift |
Staggered items: power4.out with stagger: 0.08 to 0.15.
HyperFrames — Complete Capabilities Inventory
Everything possible in HyperFrames as of today's workspace, synthesized from direct source reads of all 7 packages, 16 skills, and the full registry.
How to read this file. Scan the Table of Contents below first. Do NOT read this file linearly — it is a 700+ line inventory; reading top-to-bottom every session wastes context. When the storyboard or a specific beat needs a particular capability (HTML-in-Canvas, shader transitions, audio-reactive, dynamic counters, etc.), jump straight to that section.
You are NOT limited to what was captured from the website. You can create shaders from scratch, search for and download registry blocks, build Three.js scenes, write custom WebGL effects, use any web API — anything a browser can render.
For implementation patterns (working code), see techniques.md. This file is the WHAT; techniques.md is the HOW.
Essential Rules
- Deterministic: No
Math.random(), noDate.now(), norequestAnimationFrame, norepeat: -1. The render engine seeks to exact timestamps. - Timeline contract:
window.__timelines["composition-id"] = tlmust be set synchronously. The timeline length defines the composition duration. - Sub-compositions: External
.htmlfiles loaded viadata-composition-src. Auto-nested timelines, scoped CSS, scoped scripts. - Linter: 60+ rules. Run
npx hyperframes lintbefore render. Catches missing timelines, overlapping clips, broken paths, GSAP errors.
Table of Contents
| # | Section | What it covers |
|---|---|---|
| 1 | Composition fundamentals | Data attributes, timeline contract, resolution presets (1080p, 4K, portrait, square, custom) |
| 2 | Animation engines (6 adapters) | GSAP + 15 plugins, Anime.js v4, CSS @keyframes, WAAPI, Lottie (lottie-web + dotlottie), Three.js (hf-seek event) |
| 3 | Shader transitions (14 WebGL) | domain-warp, ridged-burn, whip-pan, sdf-iris, ripple-waves, gravitational-lens, cinematic-zoom, chromatic-split, swirl-vortex, thermal-distortion, flash-through-white, cross-warp-morph, light-leak, glitch — plus custom GLSL |
| 4 | CSS scene transitions (30+) | Push/slide, scale/zoom, radial/clip, 3D flip, blur, dissolve, cover/blinds, light leak/burn, distortion/glitch, mechanical/shutter, grid dissolve, destruction/burn, VHS/gravity/morph — 6 timing presets |
| 5 | Visual effects + textures | Text markers (highlight, circle, burst, scribble, sketchout), grain/noise, light leaks, film burn, vignette, glow, paper texture, shimmer sweep |
| 6 | Caption techniques | Per-word karaoke, intensity tiers, 5 exit styles, 6 tone mappings, per-word styling triggers, 7 audio source formats, positioning helpers |
| 7 | Audio-reactive animation | Bass→scale, mid→shape, treble→glow; any GSAP property; band extraction script; banned patterns |
| 8 | HTML-in-canvas | Live DOM as GPU texture (drawElementImage), Three.js planes, WebGL shaders on HTML, 7 VFX blocks (iPhone/MacBook device, liquid, glass, magnetic, portal, shatter, text cursor) |
| 9 | Three.js / WebGL custom scenes | Full 3D: AnimationMixer, custom GLSL, post-processing, GLTF models, lights, cameras, materials — all deterministic via hf-seek |
| 10 | SVG / canvas / variable fonts | SVG path drawing, Canvas 2D procedural art, CSS 3D card, per-word type, variable font axes, character typing, velocity-matched cuts, MotionPath |
| 11 | Media: video, audio, TTS | Video compositing + frame injection, audio mixer (multi-track), Kokoro TTS (54 voices, 9 languages), Whisper/Groq/OpenAI transcription, background removal (u2net) |
| 12 | Registry (51 blocks + 4 components + 8 examples) | Social overlays (8), showcases (5), data viz (2), logo branding (1), 3D/VFX (7), shader transitions (14), transition galleries (13), components (grain, shimmer, pixelate, texture-mask), 8 starter examples |
| 13 | CLI (25 commands) | init, add, catalog, play, preview, publish, render (MP4/WebM/MOV/PNG, HDR, GPU, parallel), lint, validate, inspect, snapshot, capture, tts, transcribe, remove-background, doctor, and more |
| 14 | Linter (60+ rules) | Core, media, GSAP, captions, composition, adapters, textures, fonts — plus async URL checks |
| 15 | Player web component | <hyperframes-player> with seek/play/pause API, 11 events, media mirror, runtime auto-inject |
| 16 | Engine + Producer | MP4/WebM/MOV/PNG output, HDR (PQ/HLG), transparency (ProRes), GPU encoding (NVENC/VideoToolbox/VAAPI/QSV), parallel rendering, video frame injection |
| 17 | Studio (in-browser NLE) | Timeline editor, drag/resize clips, asset browser, render queue, lint modal, caption editor, element picker |
| 18 | Determinism guarantees | No Math.random, no Date.now, no RAF, no repeat:-1, no callbacks, synchronous construction |
| 19 | Variables / parameterization | Typed runtime variables (string, color, number, boolean, enum), CLI override, strict validation |
| 20 | Sub-compositions | External file or inline template, auto-nested timelines, scoped CSS, scoped scripts, variable inheritance |
| 21 | Global runtime APIs | 25+ window globals for timelines, player, variables, adapters, hooks |
| 22 | Skills (16) | hyperframes, cli, media, registry, contrast, animation-map, website-to-hyperframes, remotion, gsap, animejs, css-animations, waapi, lottie, three, tailwind, contribute-catalog |
| 23 | References (15 docs) | transitions, css-patterns, dynamic-techniques, motion-principles, typography, narration, captions, audio-reactive, transcript-guide, techniques, beat-direction, visual-styles, and more |
| 24 | Documentation (27 pages) | Guides + package docs covering rendering, HDR, html-in-canvas, performance, prompting, troubleshooting, etc. |
---
1. Composition fundamentals
Data attributes recognized by the runtime
- Root composition:
data-composition-id,data-start,data-duration,data-width,data-height,data-composition-src(external sub-comp),data-composition-duration,data-composition-variables(JSON),data-variable-values(override) - Every clip:
id,data-start,data-duration,data-track-index,class="clip", optionaldata-media-start,data-volume,data-playback-start - Sub-composition host:
data-composition-id,data-composition-srcOR inline<template id="${compId}-template"> - Parser also reads:
data-type(composition|text),data-end,data-keyframes(JSON),data-x|y|scale|opacity,data-color|font-size|font-weight|font-family|text-shadow|outline|highlight*,data-layer(z-index, deprecated for timeline but used for audio mixer layers),data-resolution,data-composition-width|height
Timeline contract
gsap.timeline({ paused: true })registered onwindow.__timelines["<composition-id>"]- Master clock (TransportClock + WebAudioTransport) drives the timeline via
tl.totalTime(t, false)ortl.seek(t, false) - Framework auto-nests sub-comp timelines
- Duration sourced from
data-durationon root, not from GSAP length - Synchronous timeline construction required (no async/await/setTimeout)
- Looping handled by
<hyperframes-player>, not GSAPrepeat: -1
Resolution presets
VALID_CANVAS_RESOLUTIONS: 1920×1080 default, 1080×1920 portrait, 1080×1080 square, 4K, 1440×2560, plus normalizeResolutionFlag for --resolution CLI flag.
---
2. Animation engines (6 deterministic frame adapters)
The runtime registers these adapters in order; each implements discover() / seek({time}) / pause / play? / revert:
| Adapter | What it drives | How to load | Notable |
|---|---|---|---|
| GSAP (createGsapAdapter) | The primary timeline + all tweens registered on window.__timelines[<id>] | CDN https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js | Plugins via standard GSAP register; HyperFrames does NOT patch THREE.Clock (uses __hfThreeTime instead) |
| Anime.js v4 (createAnimeJsAdapter) | Anime instances pushed to window.__hfAnime | CDN animejs@4.0.2/lib/anime.iife.min.js or ESM | Adapter multiplies composition seconds by 1000 for ms |
| CSS animations (createCssAdapter) | Any element with computed animation-name | Declarative @keyframes | Falls back to negative animation-delay when WAAPI unavailable |
| WAAPI (createWaapiAdapter) | All Animation objects on document | element.animate() | Uses document.getAnimations() |
| Lottie (createLottieAdapter) | window.__hfLottie array; supports lottie-web + dotlottie-web | CDN lottie.min.js + @lottiefiles/dotlottie-web | goToAndStop(time*1000) or setCurrentRawFrameValue / seek(%) |
| Three.js (createThreeAdapter) | window.__hfThreeTime + dispatches CustomEvent("hf-seek", {detail:{time}}) | ESM CDN three@0.181.2/+esm | Composition's render loop listens to hf-seek; pattern: mixer.setTime(time) |
GSAP plugins (documented patterns)
- TextPlugin — text mutation in
tl.call(skills/gsap/references/effects.md) - MotionPathPlugin — curve-constrained tweens (skills/hyperframes/references/techniques.md)
- CustomEase — bezier eases imported from Remotion-style timing
- ScrollTrigger / Flip / SplitText / Draggable / Inertia / Observer / ScrambleText / CustomWiggle / CustomBounce / ScrollSmoother / GSDevTools — work natively if loaded and tweens are on the registered paused timeline, but no special HyperFrames adapter
- Producer injects ScrollTrigger CDN automatically when needed (packages/producer/src/services/htmlCompiler.ts)
---
3. Shader transitions — @hyperframes/shader-transitions
14 named WebGL fragment shaders. All share the same uniforms: u_from, u_to, u_progress, u_resolution, u_accent, u_accent_dark, u_accent_bright.
The 14 shaders
| Name | Visual | Notes |
|---|---|---|
| domain-warp | Multi-octave FBM warps both scenes oppositely; organic dissolve edge with accent flash | Uses NQ noise bundle |
| ridged-burn | Ridged multifractal mask reveals B; accent → bright → white burn ramp; sparks | NQ |
| whip-pan | 10-sample horizontal motion blur + lateral crossfade | No noise |
| sdf-iris | Aspect-corrected circle SDF expansion + accent-tinted glow rings | — |
| ripple-waves | Radial standing-wave UV displacement + tinted crossfade | — |
| gravitational-lens | Pinch pull toward center + R/B chromatic separation | — |
| cinematic-zoom | 12 RGB-offset radial zoom blur samples (chromatic zoom streak) | — |
| chromatic-split | R/B radial channel shift outward / inward; G fixed | Distinct from CSS chromatic aberration |
| swirl-vortex | CCW swirl with FBM noise; reciprocal on incoming | NQ |
| thermal-distortion | Vertical sin + FBM horizontal displacement; warm haze | NQ |
| flash-through-white | Fade through white midpoint — a visible white flash between scenes | No accent. Use only when the brand specifically calls for a white-flash beat boundary; this is NOT a neutral "default" transition. |
| cross-warp-morph | FBM vector field displaces both scenes; third FBM biases irregular wipe | NQ |
| light-leak | Fixed off-frame leak with exponential falloff + accent warmth + ridge flare | Hard-coded leak anchor |
| glitch | Line displacement + RGB lateral split + scan modulation + posterization + flicker | Deterministic |
Public API
HyperShader.init({
bgColor: "#0b0f14",
accentColor: "#f59e42",
scenes: ["scene1", "scene2"],
transitions: [{ time: 3, shader: "sdf-iris", duration: 0.65, ease: "power2.inOut" }],
timeline: gsap.timeline({ paused: true }),
compositionId: "main",
previewCaptureFps: 30,
});- Capability probe:
isHtmlInCanvasCaptureSupported()(Chrome layoutSubtree/drawElementImage) - Tuning:
?__hf_shader_capture_scale=(0.25–1),?__hf_shader_loading=(internal|player|none) - Cache: IndexedDB for PNG snapshots; max 2 textured transitions live at once
- Fallback (
applyFallbackTransition): smoothstep opacity tween when capture / texImage2D fails - Engine mode skips GL/capture when
window.__HF_VIRTUAL_TIME__set (producer uses metadata)
You can also write custom GLSL shaders from scratch — any fragment shader works with the standard uniforms.
---
4. CSS scene transitions (30+ named patterns)
Documented in skills/hyperframes/references/transitions/ across 14 category files. All GSAP-driven, none mixable with shader transitions in same composition.
By category
| Category | Patterns |
|---|---|
| Push / slide (css-push.md) | Push slide, vertical push, elastic push, squeeze |
| Scale / zoom (css-scale.md) | Zoom through, zoom out, scale-up swap |
| Radial / clip (css-radial.md) | Circle iris, diamond iris, diagonal split |
| 3D (css-3d.md) | 3D card flip, hinge door |
| Blur (css-blur.md) | Crossfade, blur crossfade, focus pull |
| Dissolve (css-dissolve.md) | Color dip (gap-to-black), staggered color blocks (2-block, 5-block) |
| Cover (css-cover.md) | Horizontal blinds, vertical blinds (variable strip counts: 6 / 12 / 20) |
| Light (css-light.md) | Light leak overlays, overexposure burn, film burn |
| Distortion (css-distortion.md) | Glitch (CSS — RGB layer jitter), chromatic aberration, ripple |
| Mechanical (css-mechanical.md) | Shutter (two-half), clock wipe (9-point rotating wedge) |
| Grid (css-grid.md) | Grid dissolve (12 or 120 cells), grid pixelate wipe |
| Destruction (css-destruction.md) | Page burn (SVG clip-path + canvas char rim) |
| Other (css-other.md) | VHS tape (strip-based seeded jitter), gravity drop, morph circle, blur through, directional blur |
| Rejected | Star iris, tilt-shift, lens flare (don't use — non-CSS-realistic) |
Timing presets
| Preset | duration | ease |
|---|---|---|
| snappy | 0.2s | power4.inOut |
| smooth | 0.4s | power2.inOut |
| gentle | 0.6s | sine.inOut |
| dramatic | 0.5s | power3.in → power3.out |
| instant | 0.15s | expo.inOut |
| luxe | 0.7s | power1.inOut |
---
5. Visual effects + textures
Marker/emphasis patterns (css-patterns.md)
| Mode | What it does | Implementation |
|---|---|---|
| highlight | Yellow bar wipes behind text | CSS bar + GSAP scaleX 0→1 |
| circle | Hand-drawn red ring around word | CSS border ellipse + back.out scale |
| burst | 12 radial spikes from word center | DOM line array, --len/--angle vars |
| scribble | Wavy underline drawn over time | SVG <path> quadratic + stroke-dash GSAP |
| sketchout | Cross-hatch x-out over text | Two 2px rotated lines |
Grain / noise
- grain-overlay (registry component): SVG feTurbulence data-URL + CSS keyframe jitter,
steps(1), default opacity 0.15 - Layered radial-gradient grain (preferred pattern): no SVG, no canvas-taint, fast everywhere
Light / film
- Light leak transitions (CSS + shader variants)
- Overexposure burn —
brightness()ramp + flash overlay - Film burn — multi-layer amber/orange/red radials
- Vignette — radial-gradient overlay
- Paper texture (in registry/examples/warm-grain/)
Glow
- Caption text glow: textShadow radius keyed to treble bands (always on active words only, never parents)
- Radial glow backgrounds: CSS gradients / blurred blobs
---
6. Caption techniques
Animation styles
- Baseline: per-word karaoke highlight (every energy level)
- Intensity tiers: accent + glow + 15% scale (high energy) → 3% scale (low energy)
- Exits by energy (dynamic-techniques.md): scatter, drop, collapse, fade+slide, fade
- Tone mappings (captions.md): scale-pop
back.out(1.7), fade+slidepower3.out, typewriter, bounce,elastic.out, word-by-word
Per-word styling triggers
- Brand/product names
- ALL CAPS
- Numbers / stats
- Emotional keywords
- CTAs
- Marker highlight modes (5 listed above)
Audio sources for caption timing
| Source | Format | Granularity |
|---|---|---|
| hyperframes transcribe (local whisper.cpp) | JSON | Word-level |
| OpenAI verbose_json | JSON | Word-level |
| Groq verbose_json | JSON | Word-level |
| Manually authored | JSON | Word-level |
| SRT | text | Phrase-level only |
| VTT | text | Phrase-level only |
| hyperframes tts → transcribe chain | wav→json | Word-level |
Positioning helpers
- Landscape: bottom 80–120px centered
- Portrait: ~600–700px from bottom
window.__hyperframes.fitTextFontSize(text, {maxWidth, fontFamily, fontWeight})for dynamic sizing
---
7. Audio-reactive animation
Data shape
window.AUDIO_DATA = {
fps: 30,
totalFrames: 900,
frames: [{ bands: [0.42, 0.18, ...] }] // bands normalized 0–1 per band across track
};Index 0 = bass, higher = treble. Bands range 0–1, normalized across full track length.
Mappings documented
| Band | Property |
|---|---|
| Bass (bands[0–1]) | scale (pulse) |
| Mid (bands[4–8]) | borderRadius, width |
| Treble (bands[12–14]) | textShadow, boxShadow (glow) |
| Overall amplitude | opacity, y, backgroundColor |
Any GSAP-tweenable property is fair game — including clipPath, filter, SVG attrs, CSS variables.
Extraction
python3 .../extract-audio-data.py audio.mp3 --fps 30 --bands 8Pre-extracted only — no Web Audio at render time.
Banned in audio-reactive
EQ bars, spectrum UI, generic waveforms, note clip-art, generic particles, rainbow cycling, white strobe on beats, abstract pulsing orbs.
---
8. HTML-in-canvas
Documented in skills/hyperframes/references/html-in-canvas-patterns.md (504 lines).
Capability
- Chrome's experimental
layoutSubtree+drawElementImagerasterizes live DOM into canvas - Feature detection:
isHtmlInCanvasCaptureSupported() - Used by shader-transitions for scene textures
- Combined with Three.js:
CanvasTexture+ post-processing
Available patterns
- HTML on a Three.js plane (displacement, distortion, liquid sim)
- HTML in shaders (texture sampling for VFX)
- Recursive HTML-in-canvas-in-shader-in-HTML
Experimental VFX blocks using this
vfx-iphone-device(GLTF iPhone + MacBook, HTML screens)vfx-liquid-background(liquid sim displaces HTML)vfx-liquid-glassvfx-magneticvfx-portalvfx-shattervfx-text-cursor(chromatic edges, canvas post)
---
9. Three.js / WebGL custom scenes
Integration pattern
window.addEventListener("hf-seek", (e) => {
const time = e.detail.time;
mixer.setTime(time);
shaderUniforms.u_time.value = time;
renderer.render(scene, camera);
});- Load:
import * as THREE from "https://cdn.jsdelivr.net/npm/three@0.181.2/+esm" - Deterministic: every frame must derive from
time, neverrequestAnimationFrame/Date.now() - Includes: AnimationMixer, custom GLSL shaders, post-processing, GLTF models, lights, cameras, materials
---
10. SVG / canvas / variable fonts (other authored techniques)
(From skills/hyperframes/references/techniques.md)
| Technique | Mechanism |
|---|---|
| SVG path drawing | strokeDasharray + getTotalLength() + GSAP stroke offset |
| Canvas 2D procedural art | Seeded hash function + tl.to proxy {time} onUpdate |
| CSS 3D card | GSAP rotationY + perspective: 900 |
| Per-word kinetic type | GSAP timings array, sliding decay |
| Variable font axes | Animate CSS vars → font-variation-settings: "opsz" var(--opsz), "wght" var(--wght) |
| Character typing | tl.call text mutation + steps(1) cursor blink |
| Velocity-matched cuts | Match outgoing blur/translate velocity to incoming for seamless beats |
| MotionPathPlugin | gsap.registerPlugin(MotionPathPlugin) + path string |
---
11. Media: video, audio, TTS
Video compositing
<video muted playsinline data-start="..." data-duration="..." data-track-index="..." src="...">- HyperFrames extracts frames at render via videoFrameInjector (avoids unreliable headless
<video>playback) - Linter forbids
<video>with audio at the same time — split into separate<video muted>+<audio> - Video frame extraction uses FFmpeg
- HDR videos: PQ or HLG transfer detection + x265 with mastering metadata
Audio mixer
<audio id="..." data-start="..." data-duration="..." data-volume="0.8" data-track-index="2" src="...">- Multiple tracks mixed with
amix normalize=0+ per-track adelay + volume - Master audioGain from EngineConfig
- Output: AAC 192kbps
TTS (Kokoro-82M, local)
- 54 bundled voices with prefixes:
aAmerican EN,bBritish EN,eSpanish,fFrench,hHindi,iItalian,jJapanese,pBrazilian Portuguese,zMandarin - Default voice:
af_heart - Speed: 0.1–3.0 (default 1.0)
- Languages: en-us, en-gb, es, fr-fr, hi, it, pt-br, ja, zh (non-EN needs system espeak-ng)
- Output: WAV; no pitch/volume CLI flags
- No API key required
Transcription
- Whisper.cpp models: tiny, base, small, medium, large-v3, small.en, medium.en (default small)
- Groq API: whisper-large-v3 with word granularities
- OpenAI API: whisper-1 verbose_json
- Imports: SRT, VTT, JSON formats
- Quality gates: music-token detection, garbage cleaning, retry with medium.en
Background removal
- u2net ONNX models
- Devices: auto / cpu / coreml / cuda
- Quality presets: fast / balanced / best
- Outputs: transparent WebM, ProRes MOV, PNG sequence
- Optional dual output (foreground + extracted background)
---
12. Registry — 51 blocks + 4 components + 8 examples
Blocks by category
Social overlays (8): instagram-follow, tiktok-follow, yt-lower-third, x-post, reddit-post, spotify-card, macos-notification, blue-sweater-intro-video
Showcases (5): app-showcase (3D phones), north-korea-locked-down (map + annotation), apple-money-count (counter + SFX), vpn-youtube-spot (app-store scroll), nyc-paris-flight (map + plane path)
Data viz (2): data-chart (animated bar+line, NYT-style), flowchart + flowchart-vertical (decision tree with SVG connectors, typing correction)
Logo / branding (1): logo-outro (build + glow + tagline + URL pill)
3D / experimental VFX (8): ui-3d-reveal, vfx-iphone-device (GLTF), vfx-liquid-background, vfx-liquid-glass, vfx-magnetic, vfx-portal, vfx-shatter, vfx-text-cursor
Single shader transitions (14): one block per named shader — domain-warp-dissolve, ridged-burn, whip-pan, sdf-iris, ripple-waves, gravitational-lens, cinematic-zoom, chromatic-radial-split, glitch, swirl-vortex, thermal-distortion, flash-through-white, cross-warp-morph, light-leak
Transition galleries (13 showcase pieces): transitions-3d, transitions-blur, transitions-cover, transitions-destruction, transitions-dissolve, transitions-distortion, transitions-grid, transitions-light, transitions-mechanical, transitions-other, transitions-push, transitions-radial, transitions-scale
Components (4 reusable snippets)
- grain-overlay — SVG feTurbulence + CSS keyframes
- shimmer-sweep — Light sweep gradient mask on text
- grid-pixelate-wipe — Grid squares stagger fade scene wipe
- texture-mask-text — Luminance-masked letterforms with 66 mask PNGs (Masonry, Stone, Ground/Road, Wood, Metal, Organic/Soft texture categories)
Examples (8 starter projects)
warm-grain, play-mode, swiss-grid, vignelli, decision-tree, kinetic-type, product-promo, nyt-graph
Install: npx hyperframes add <name> for blocks/components, hyperframes init <dir> --example <name> for examples.
---
13. CLI — 25 commands
| Command | Purpose |
|---|---|
| init | Scaffold project from template/example (interactive or --non-interactive) |
| add | Install registry block / component |
| catalog | Browse registry blocks/components (--type, --tag, --json, --human-friendly picker) |
| play | Lightweight browser player (default port 3003) |
| preview | Studio dev server (port 3002; --force-new, --list, --kill-all) |
| publish | Zip + upload + return hyperframes.dev URL |
| render | Render to MP4 / WebM / MOV / PNG sequence — flags: --fps 24/30/60, --quality draft/standard/high, --workers, --docker, --hdr/--sdr, --crf, --video-bitrate, --gpu, --browser-gpu auto/software/hardware, --max-concurrent-renders 1-10, --variables JSON, --variables-file PATH, --strict-variables, --resolution preset |
| lint | Static lint (--json, --verbose) |
| validate | Bundle + headless Chrome + console + contrast (--contrast default true, --timeout 3000) |
| inspect / layout | Visual layout audit (overflow detection at N timestamps; --samples 9, --at, --tolerance 2, --max-issues 80) |
| info | Print project metadata |
| compositions | List compositions (root + sub-comps) |
| benchmark | 5 preset configs × N runs (--runs 3) |
| browser | Manage Chrome (ensure/path/clear) |
| remove-background | u2net + FFmpeg → transparent video |
| transcribe | whisper.cpp or import SRT/VTT/JSON |
| tts | Kokoro-82M (--voice, --speed, --lang, --list) |
| docs | Print bundled markdown topics (data-attributes, examples, rendering, gsap, troubleshooting, compositions) |
| doctor | Environment checklist (Node, CPU, memory, disk, FFmpeg, FFprobe, Chrome, Docker) |
| upgrade | npm update check + optional global install |
| skills | Run npx skills add heygen-com/hyperframes --all |
| telemetry | enable/disable/status |
| snapshot | PNG screenshots at timeline timestamps |
| capture | Capture URL → site assets + screenshots + design tokens (uses Puppeteer + optional Gemini vision) |
Website capture (hyperframes capture <url>)
Detects these libraries on captured sites (for context labeling): GSAP / ScrollTrigger, Three.js, Lottie, Anime.js, PixiJS, Babylon.js, Rive, Matter.js, Lenis, Framer Motion, Tailwind CSS, WebGL (shader fingerprinting). Captured outputs feed the website-to-hyperframes skill workflow.
---
14. Linter — 60+ rules
Across 8 files in packages/core/src/lint/rules/:
| Rule file | Catches |
|---|---|
| core | Missing composition-id, missing dimensions, missing timeline registry, registry mismatch, invalid script syntax, scoped-CSS issues, non-deterministic code |
| media | Duplicate media id, video missing muted, video nested in timed element, placeholder URLs, base64 prohibited, missing src/start/id, imperative play()/pause()/seek() |
| gsap | Overlapping tweens, exit missing hard kill, GSAP animating .clip element, unscoped selectors, CSS transform conflict, missing GSAP script, infinite repeat (repeat: -1), repeat ceil overshoot, scene layer visibility kill, audio-reactive single-tween-per-group |
| captions | Caption exit missing kill, text overflow risk, transcript not inline, parse error, container position, scale mismatch, textShadow on parent container |
| composition | File too large, dense tracks, missing class="clip", deprecated data-layer/data-end, split attribute selectors, external script deps, RAF in composition, invalid variable JSON |
| adapters | Missing Lottie script, missing Three script |
| textures | Drop-shadow on text, class missing base, text missing mask, unknown texture class |
| fonts | Google Fonts import (use @font-face), font-family without @font-face |
Plus async URL checks (lintMediaUrls, lintScriptUrls — HEAD probes).
validateCompositionGsap also forbids: Math.random, Date.now, new Date, setTimeout, setInterval, requestAnimationFrame, repeat: -1.
Note: onUpdate callbacks, tl.call(), and GSAP event callbacks (onComplete, onStart, etc.) are NOT banned by the linter — they are required for canvas/WebGL rendering and character-by-character typing patterns. The linter only catches the determinism violations listed above.
---
15. Player — <hyperframes-player> web component
Attributes
src, srcdoc, width, height, controls, muted, volume, poster, playback-rate, audio-src, shader-capture-scale, shader-loading (internal|player|none), loop, autoplay, speed-presets
Public API
seek(t) (synchronous when same-origin — uses iframe.contentWindow.__player.seek directly), play(), pause(), currentTime, duration, paused, ready, playbackRate, iframeElement
Events
ready, timeupdate, play, pause, ended, volumechange, ratechange, shadertransitionstate, playbackerror, error, audioownershipchange
Media mirror
Parent audio/video elements with data-start are proxied; _mirrorParentMediaTime corrects drift; _audioOwner promotes to parent if autoplay blocked.
Runtime auto-inject
Loads RUNTIME_CDN_URL (@hyperframes/core/dist/hyperframe.runtime.iife.js) if missing __hf/__player but timelines exist.
---
16. Engine + Producer — rendering pipeline
Output formats
mp4, webm, mov, png-sequence — with HDR (PQ / HLG / SDR / auto-detect), transparency (ProRes MOV / WebM / PNG), or standard 8-bit SDR
Encoding controls
--fps: 24 / 30 / 60--quality: draft / standard / high--crf: integer (mutually exclusive with--video-bitrate)--video-bitrate: e.g.8M--gpu: NVENC, VideoToolbox, VAAPI, QSV--browser-gpu: auto / software / hardware--workers: parallel render workers--max-concurrent-renders: 1–10 (setsPRODUCER_MAX_CONCURRENT_RENDERS)--resolution: preset (1080p, 4k, portrait, etc.)--docker: render inside Dockerfile.test image (reproducibility)--hdr/--sdr: force HDR or SDR pipeline
Engine subsystems
- Frame capture: BeginFrame on Linux headless-shell (fast, no alpha) or
Page.captureScreenshot(alpha + supersample) - Video frame injector: pre-extracts video to images, swaps
<video>for<img>during capture (LRU cache by path + byte budget) - Audio mixer: FFmpeg-based; per-track delay, volume, master gain, AAC 192k output
- Chunk encoder: H.264 / H.265 / VP9 / ProRes presets with optional GPU
- Streaming encoder:
streamingEncodeMaxDurationSecondsfor long renders - HDR compositing:
rgba16floatWebGPU readback (headed Chrome), PQ OETF helpers - Layer compositor: groups DOM by z-order; splits HDR elements into separate layers
- Alpha blit: matrix3d affine extraction,
blitRgba8OverRgb48le,blitRgb48leAffine - Parallel coordinator: concurrency, coresPerWorker, minParallelFrames, largeRenderThreshold
- Browser pool: optional with timeout configs
Producer-only
RenderConfigwithhdrMode(auto / force-hdr / force-sdr),outputResolutionmapped todeviceScaleFactor- File server injects
HF_EARLY_STUB,HF_BRIDGE_SCRIPT, virtual-time sowindow.__hfbridgeswindow.__player.renderSeek - HDR-aware shader transition compositing via
window.__hf.transitionsmetadata
---
17. Studio — in-browser NLE
Full editor in packages/studio/:
- NLELayout: NLE preview + timeline + controls
- Timeline: clip rendering, drag to move (
data-start), resize (data-duration),data-track-indexreassignment, asset drop, file drop - PlayerControls: scrub, play, pause, frame step (
stepFrameTime),STUDIO_PREVIEW_FPS - useTimelinePlayer: resolves
__player/__timeline/__timelines - LeftSidebar: compositions list, asset browser
- RenderQueue + useRenderQueue: queue multiple renders
- LintModal: in-app lint output
- MediaPreview + AudioWaveform: waveform rendering
- CaptionOverlay, CaptionTimeline, CaptionPropertyPanel: caption editor
- useCaptionSync: word-level sync
- useElementPicker: click-to-inspect picker mode
- Built with Tailwind v3 (separate from Tailwind v4 browser runtime used by compositions).
---
18. Determinism guarantees
- No
Math.random()(use seeded PRNGs; mulberry32 is the pattern in skills) - No
Date.now()/new Date() - No
setTimeout/setIntervalin timeline construction - No
requestAnimationFrame(timeline-driven; engine seeks per frame) - No
repeat: -1(calculate exact repeats:Math.ceil(duration / cycleDuration) - 1) - No
onComplete/onStart/onRepeatcallbacks (engine doesn't fire them). Exception:onUpdateandtl.call()ARE supported — they're required for canvas/WebGL rendering, character-by-character typing, and counter patterns. See §10 (Canvas 2D procedural art) for the documented pattern. - No
gsap.seton clips from later scenes (usetl.set(selector, vars, position)) - Synchronous timeline construction (no async)
- Master clock can clamp at composition end
---
19. Variables / parameterization
Compositions support typed runtime variables:
<html
data-composition-variables='[
{"name":"brand","type":"string","default":"Stripe"},
{"name":"primary","type":"color","default":"#635BFF"},
{"name":"duration","type":"number","default":15},
{"name":"darkMode","type":"boolean","default":false},
{"name":"layout","type":"enum","options":["hero","split","stacked"]}
]'
></html>Access via window.__hyperframes.getVariables(). Override at render time:
npx hyperframes render --variables '{"brand":"Linear","primary":"#5E6AD2"}'
npx hyperframes render --variables-file vars.json
npx hyperframes render --strict-variables # error if unused / mismatchedvalidateVariables() checks values against declarations at the CLI/tooling boundary.
---
20. Sub-compositions
Two loading mechanisms:
- External file:
data-composition-src="compositions/act-1.html"— fetched at runtime - Inline template:
<template id="<id>-template">— extracted byloadInlineTemplateCompositions
Each sub-comp:
- Has its own
data-composition-id - Has its own
window.__timelines[<id>] - Auto-nested into the root timeline
- Scoped CSS via
scopeCssToComposition([data-composition-id="<id>"]selector) - Wrapped scripts via
wrapScopedCompositionScript - Reads
data-variable-valuesmerged with own defaults intowindow.__hfVariablesByComp[<id>] - External scripts load with
EXTERNAL_SCRIPT_LOAD_TIMEOUT_MStimeout. Failed loads emitexternal_composition_load_failed/external_composition_script_load_issuediagnostics.
---
21. Global runtime APIs (window.*)
| Global | Purpose |
|---|---|
__hyperframes | { fitTextFontSize, getVariables } |
__timelines | { [compositionId]: GsapTimeline } |
__player | Internal player bridge: seek, play, pause, renderSeek, etc. |
__clipManifest | Computed clip array (for Studio) |
__playerReady | Resolved when player ready |
__renderReady | Resolved when ready for render capture |
__HF_PARITY_MODE | Engine parity flag |
__HF_FPS | Render FPS hint |
__HF_MAX_DURATION_SEC | Engine clamp |
__HF_VIRTUAL_TIME__ | Set by engine in render mode (shader-transitions switches paths) |
__hfThreeTime | Current time for Three.js adapter |
__HF_PICKER_API | Element picker hook for Studio |
__hfAnime | Array of Anime.js instances |
__hfLottie | Array of Lottie animations |
__hfVariables | Resolved root variables |
__hfVariablesByComp | Per-composition variable map |
__hfRuntimeTeardown | Cleanup function |
__tailwindReady | Tailwind browser build ready (gates capture) |
__hf.transitions | Shader transition metadata (read by producer for HDR-aware compositing) |
__beforeTimeline, __afterTimeline | Optional user hooks |
__afterRender, __beforeRender | Optional user hooks |
gsap, THREE, anime, lottie | Library globals |
Control bridge actions: play, pause, seek, set-muted, set-playback-rate, enable-pick-mode, disable-pick-mode, set-volume, set-media-output-muted.
---
22. Skills available — 16
| Skill | Purpose |
|---|---|
| hyperframes | Core framework skill (composition authoring) |
| hyperframes-cli | All CLI commands as an agent skill |
| hyperframes-media | Media workflows (TTS, transcribe, captions integration) |
| hyperframes-registry | Installing blocks/components |
| hyperframes-contrast | WCAG audit (scripts/contrast-report.mjs) |
| hyperframes-animation-map | Per-tween bbox + flags report |
| website-to-hyperframes | Capture → DESIGN.md → brief → storyboard+script → VO → build → validate |
| remotion-to-hyperframes | Migration patterns + API map + CustomEase |
| gsap | GSAP API + plugins reference |
| animejs | Anime.js v4 patterns |
| css-animations | @keyframes patterns |
| waapi | Web Animations API |
| lottie | Lottie integration |
| three | Three.js + deterministic seek |
| tailwind | Tailwind browser runtime v4 |
| contribute-catalog | Adding blocks to registry |
---
23. References inventory (skills/hyperframes/references/)
16 reference docs covering:
- text-effects.md — 24 named text animation effects (per-character, per-word, per-line, whole) — vocabulary reference for the separate
pixel-point/animate-textskill (load it vianpx skills add pixel-point/animate-textor/animate-text). Specs live in that upstream skill, not in this repo. - transitions.md + transitions/catalog.md + 14 category subfiles
- css-patterns.md (marker patterns)
- dynamic-techniques.md (caption animation)
- motion-principles.md (easing as emotion, choreography)
- typography.md (banned-font list, pairing rules, variable fonts)
- narration.md (script + VO pacing)
- captions.md (caption authoring contract)
- audio-reactive.md (band mappings + extraction)
- transcript-guide.md (whisper, Groq, OpenAI workflows)
- techniques.md (the big technique catalog — SVG, canvas 2D, 3D, kinetic, lottie, variable fonts, MotionPath, audio-reactive)
- beat-direction.md, design-picker.md, prompt-expansion.md, video-composition.md
- visual-styles.md (8 named visual styles: Swiss Pulse, Velvet Standard, Deconstructed, Maximalist Type, Data Drift, Soft Signal, Folk Frequency, Shadow Cut — available via the
visual-styleskill)
---
24. Documentation pages (docs/)
27 mdx pages in docs/guides/ and docs/packages/:
Guides: 4k-rendering, claude-design, common-mistakes, deploy, gsap-animation, hdr, html-in-canvas, hyperframes-vs-remotion, open-design, performance, prompting, remove-background, rendering, timeline-editing, troubleshooting, video-editor-cheatsheet, website-to-video, etc.
Packages: cli.mdx, core.mdx, engine.mdx, player.mdx, producer.mdx, studio.mdx
Step 0: Capture
The capture pipeline downloads the site and extracts structured data for the rest of the workflow to read. Step 0 is a single command plus a sanity check. All analysis (reading files, viewing contact sheets, deriving brand voice, picking assets) happens in Steps 1–3, not here.
Run the capture
No API keys required for the base capture. However, before running, ask the user:
"For the best results, it is recommended to set a Gemini API key — it gives me AI-powered descriptions of every captured image, which helps me choose the right assets for each scene. It costs about $0.001 per image. You can skip this if you want, but the video quality will be better with it. To set it up: addGEMINI_API_KEY=your-keyto a.envfile in the project root. You can get a free key at ai.google.dev."
If the user provides the key or already has one set, proceed. If they skip it, proceed anyway — the capture works without it, but asset-descriptions.md will have DOM-context descriptions only (position, size, alt text) instead of AI vision descriptions.
Create a project directory for your video if it doesn't exist yet, then capture the website into a capture/ subfolder within it:
npx hyperframes capture <URL> -o <project-dir>/captureExample: npx hyperframes capture https://stripe.com -o videos/stripe-launch/capture
Keeping capture artifacts (screenshots/, assets/, extracted/, AGENTS.md, CLAUDE.md) in a dedicated capture/ subfolder keeps them isolated from later build files (SCRIPT.md, STORYBOARD.md, DESIGN.md, compositions/, index.html, narration.wav, transcript.json, renders/, snapshots/), which all live at <project-dir>/ root.
For exploratory captures that aren't becoming a video yet, -o captures/<name> at the repo root is fine — the isolation convention only matters when you're building a video on top of the capture.
Confirm it succeeded
Wait for the capture to complete. Print one line summarizing what was captured:
"Captured N screenshots, M assets, K SVGs, F fonts. Ready for Step 1."
If the command exited non-zero, the counts are all zero, or required directories (extracted/, assets/, screenshots/) are missing, surface the error and stop — don't advance to Step 1 with a broken capture.
What lives in capture/ (reference table — DO NOT read these here)
Each downstream step reads only what it needs. Don't pre-fetch everything in Step 0; that bloats context and produces summaries that get stale by the time they're used.
| Path | First read in |
|---|---|
capture/extracted/tokens.json | Step 1 (DESIGN.md — colors / fonts) |
capture/extracted/design-styles.json | Step 1 (DESIGN.md — typography / components) |
capture/extracted/fonts-manifest.json | Step 1 (font identification) |
capture/extracted/asset-descriptions.md | Step 2 (brief grounding) and Step 3 (assets) |
capture/extracted/visible-text.txt | Step 2 (brief) and Step 3 (script) |
capture/assets/contact-sheet-*.jpg | Step 3 (asset picking) |
capture/assets/svgs/contact-sheet-*.jpg | Step 3 (SVG / logo picking) |
capture/screenshots/contact-sheet-*.jpg | Step 3 (visual mood reference) |
capture/extracted/animations.json | Step 3 / Step 5 (only if site has animations) |
capture/extracted/lottie-manifest.json | Step 3 (only if site uses Lottie) |
capture/extracted/video-manifest.json | Step 3 (only if site embeds video) |
capture/extracted/shaders.json | Step 3 / Step 5 (only if site has WebGL) |
capture/assets/<individual files> | Step 5 (only when placing a specific asset) |
Gate
Capture exits 0. Asset / screenshot / font counts non-zero. Proceed to Step 1.
Step 1: Write DESIGN.md (the brand-truth cheat sheet)
DESIGN.md is a brand-truth cheat sheet — colors and fonts you'll weave into your composed builds. It is NOT a layout spec, not a moodboard, not a 400-line design system audit.
DESIGN.md is the brand inflection sub-agents apply when building each beat: which color is "primary," which font is for headlines, what tone the brand carries — the load-bearing knobs they flip while building.
Target length: 250–350 lines. Step 5 sub-agents read DESIGN.md to brand each beat — the more precise the component CSS values you encode here, the more brand-faithful the result. Going under 200 lines tends to produce generic dark-cinematic output because sub-agents have no brand component DNA to work from; going over 350 means you're over-investing in prose.
Fast-pacing exception: For billboard-per-beat videos (short social ads where each beat is a single hero element on full-bleed background), a 50-line DESIGN.md with just colors + fonts + 3-5 do's/don'ts is enough. The Step 5 sub-agent prompt pastes brand values inline, so DESIGN.md depth only matters when the beats render full UIs.
User preferences always override brand rules. If the user says "make it bright even though the site is dark" or "use serif fonts even though the brand is sans" — follow the user. DESIGN.md describes the captured website. The video might deliberately break that.
Read these now — they're the inputs DESIGN.md is built from. Don't guess colors or sizes from screenshots:
capture/extracted/tokens.json— top brand colors (HEX) and font families with weight ranges.capture/extracted/design-styles.json— computed CSS values from the live DOM: typography hierarchy (font-size, weight, line-height, letter-spacing per text role), button variants (background, padding, radius, shadow), card/container/nav styles, spacing scale, border-radius scale, box-shadow values with usage counts. Primary data source for Sections 3–6 below.
Font availability check — do this before writing anything else. Read capture/extracted/fonts-manifest.json. The capture pipeline reads the OpenType name table embedded in every downloaded font file, so even hash-renamed Next.js/Webpack fonts are identified by their real family name (Inter, JetBrains Mono, Geist Mono, etc.). No guessing required.
The manifest gives you two views:
families[]— one entry per distinct family with the weights captured, whether it's a variable font, and the files belonging to itfiles[]— one entry per downloaded font with family, subfamily, weight, style, and any variation axes
How to use it:
- For each family you'll reference in DESIGN.md, name it by what's in
families[].family(e.g. "Inter", not "f266e704 hashed font"). The hashed filenames are the@font-face srcpaths — they stay as-is on disk; only the display name comes from the manifest. - If a family has
variable: trueandvariationAxesincludes"wght", you can use any weight 100-900 viafont-variation-settings: 'wght' <value>even if only one static weight appears in the captured files. Note this in DESIGN.md so sub-agents know they have the full weight range available. - If the manifest's
unidentified[]is non-empty, those files failed name-table extraction (rare — heavily subset fonts that strip metadata). Flag them asunknownin DESIGN.md and suggest a fallback rather than guessing. - Commercial fonts hosted on brand CDNs (GT Walsheim, Söhne, Graphik, Canela) won't be in the manifest because they aren't downloaded. Detect this by checking what the site uses (from
design-styles.json) against what's in the manifest — anything used but missing is a CDN-hosted font. Flag explicitly: "Söhne not in capture; use Inter 600 as substitute."
Sub-agents try to use the fonts you list. The manifest tells you exactly what's available — there's no excuse for claiming "Charlie Display 700" when no such file exists.
---
The 5 sections to write
## 1. Visual Theme (one paragraph)
3–5 sentences describing the brand's visual personality. Cover: dark-first or light-first, contrast strategy, dominant visual elements (gradients, illustrations, photography, UI mockups), overall mood, what makes it distinctive vs. generic.
This is the only prose section. Make it specific to _this_ brand — not template-filling. A sentence that could describe any well-designed website is not useful.
Example:
Stripe's visual language is light-first and clean, with deep navy (#061B31) and pure white as the foundation. The accent stack — Stripe Purple (#533AFD) for CTAs, Vibrant Orange (#FF6118) for energetic emphasis — keeps interactive elements unmistakable. Type is sohne-var Light (300) for display, weight 400 for body; the brand achieves hierarchy through size and weight, never color shifts. The mood is confident financial-tech — premium without theatrical drama. Distinctive: gradient overlays at 135° between purple and orange appear as subtle washes over white backgrounds, never as bold focal elements.
---
## 2. Quick Reference
A flat lookup of the values sub-agents grab while composing beats. Two sub-sections — keep them tight.
Colors
List 8–12 colors with brand-specific names + HEX + role. Not generic ("Accent 1") but evocative ("Stripe Purple", "Deep Navy", "Slate Border"). The name carries meaning; "blue 4" doesn't.
For each text-on-surface combination the brand uses, compute the WCAG AA contrast ratio and flag failing pairings explicitly. A real failure mode from prior runs: the brand's secondary-text color (#68686A) on its dark panel color (#18191B) = 3.16:1, which fails AA's 4.5:1 minimum. Sub-agents faithfully reproduced the brand's color choice and the result was unreadable. Encode the safe / unsafe pairings here so sub-agents pick text colors by surface context, not by "this is the brand's secondary text color." The /hyperframes-contrast skill audits ratios — run it before finalizing DESIGN.md.
Example:
#### Colors
- **Stripe Purple** (`#533AFD`): Primary CTA, interactive elements, focus rings — the brand's action signal
- On Pure White: 6.2:1 ✅ — On Deep Navy: 3.8:1 ⚠ AA-only-Large
- **Deep Navy** (`#061B31`): Primary text on light surfaces, also a dark surface tier
- As text on Pure White: 17.4:1 ✅ — As surface: see Slate-on-Navy pairings below
- **Pure White** (`#FFFFFF`): Page background, card surfaces
- **Light Gray** (`#F5F7FA`): Surface tier 2 (cards on white pages, alternating sections)
- **Slate Blue** (`#273951`): Secondary text on LIGHT surfaces
- On Pure White: 12.6:1 ✅ — On Light Gray: 11.8:1 ✅ — On Deep Navy: 1.4:1 ❌ DO NOT USE
- **Light Slate** (`#64748D`): Metadata, captions on light surfaces only
- On Pure White: 4.8:1 ✅ — On Light Gray: 4.5:1 ✅ — On Deep Navy: 3.0:1 ❌ — On Dark Panel: 2.9:1 ❌
- **For dark-surface metadata, use `#9A9A9E` instead: 6.4:1 on Deep Navy ✅, 6.1:1 on Dark Panel ✅**
- **Subtle Border** (`#D4DEE9`): Card borders, dividers (not text — borders don't need AA)
- **Vibrant Orange** (`#FF6118`): Energy accent — gradient endpoints, highlight bursts (never primary text)
- **Error Red** (`#FF0022`): Validation errors. On Pure White: 4.5:1 ✅
- **Success Green** (`#4CD963`): Confirmation states. On Pure White: 1.7:1 ❌ — must be paired with a darker outline or use as accent on dark surfacesWhere the brand's own palette fails WCAG, document the substitute (like the #9A9A9E override above). Sub-agents pick the safe color by surface — and if the deviation matters to the brand identity, the user can revisit at Step 6.
Fonts
List font families with their role AND the exact file path per family + weight from fonts-manifest.json. Sub-agents will copy the @font-face block verbatim — if you only name the family without the path, sub-agents have to guess which .woff2 file belongs to which family and get it wrong half the time (a real failure mode from prior runs: agents pointed @font-face for "ES Build Neutral" at the Inter .woff2 files and the wordmark rendered in Inter).
Example:
````markdown
Fonts
- Display:
"ES Build Neutral"— wordmarks, headlines - 600:
capture/assets/fonts/14d7ce3e41dcbb66-s.p.woff2 - 700:
capture/assets/fonts/e8b276476c0ac6fa-s.p.woff2 - Body:
"Inter"(variable 100–900, captured ✓) — body, labels, UI - 400:
capture/assets/fonts/9a8d3f06c4e89f2b-s.p.woff2 - 600:
capture/assets/fonts/1b0b3615811be75b-s.p.woff2 - Mono:
"JetBrains Mono"— code, metadata - 400:
capture/assets/fonts/c7d2e9f5a1b3c8d4-s.p.woff2 - Fallback stack:
-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif
`@font-face` block to paste in every composition (sub-agents copy this verbatim — do not invent file paths):
@font-face {
font-family: "ES Build Neutral";
src: url("capture/assets/fonts/14d7ce3e41dcbb66-s.p.woff2") format("woff2");
font-weight: 600;
font-display: block;
}
@font-face {
font-family: "Inter";
src: url("capture/assets/fonts/9a8d3f06c4e89f2b-s.p.woff2") format("woff2");
font-weight: 400;
font-display: block;
}
/* + any other family/weight combinations the storyboard's beats need */````
The brand uses size for hierarchy, weight for emphasis. Display 1: 48px/600, Display 2: 32px/600, Body: 14px/400. (Adjust to the actual brand's hierarchy.)
````
The exact @font-face block lets sub-agents copy verbatim instead of constructing one from inference. If a beat needs a weight that isn't in the manifest, flag it explicitly here: e.g., "ES Build Neutral 900 NOT in capture; use ES Build Neutral 700 as substitute, or fall back to Inter 700."
That's the whole typography section. If sub-agents need exact line-heights or letter-spacing, they read design-styles.json directly.
---
## 3. Component Stylings (the build-step's spec sheet)
This is the section sub-agents consult most when building beats in Step 5. Without exact per-component CSS, sub-agents fall back to generic "dark bg + glow + centered text" patterns regardless of brand — which is why every video starts looking the same. Encode the brand's actual component DNA here.
Target 6-12 distinct components. Document what the site actually uses; skip categories the brand doesn't have. For each component, name it descriptively ("Stripe Primary Button" not "Button 1") and provide exact CSS-level properties: background, text color, padding, border-radius, border, font size/weight, height, box-shadow, and any hover/active/disabled states.
Buttons (always required)
Cover every variant the site uses — typically Primary, Secondary/Ghost, and Icon. Example:
#### Primary Button (Stripe Purple)
- **Background:** `#533AFD`
- **Text color:** `#FFFFFF`
- **Font:** sohne-var 16px / 400
- **Padding:** `15.5px 24px 16.5px 24px`
- **Border radius:** `4px`
- **Border:** none
- **Height:** `48px` (with padding)
- **Box shadow:** none
- **Hover:** background `#4329E8`, opacity `0.95`
- **Active:** background `#3720D4`, scale `0.98`
- **Disabled:** background `#C9C3F0`, cursor `not-allowed`
#### Secondary Button (outline)
- **Background:** `#FFFFFF`
- **Text color:** `#533AFD`
- **Border:** `1px solid #533AFD`
- **Padding / radius / font:** same as Primary
- **Hover:** background `#F3F0FF`, border `#4329E8`
#### Ghost Button (text-only link)
- **Background:** transparent
- **Text color:** `#533AFD`
- **Font:** sohne-var 14px / 400
- **Padding:** `12px 0`
- **Hover:** background `rgba(83, 58, 253, 0.08)`, optional underlineCards & Containers (always required if the site uses any)
Document each distinct card type — Standard, Feature Highlight, Glass, Pricing, Testimonial — whatever this brand actually uses. Example:
#### Standard Card
- **Background:** `#FFFFFF`
- **Border:** `1px solid #D4DEE9`
- **Border radius:** `5px`
- **Padding:** `32px`
- **Box shadow:** `0 1px 2px rgba(0, 0, 0, 0.04)` (default), `0 4px 12px rgba(0, 0, 0, 0.08)` (hover)
- **Hover:** border `#B8CCDB`
#### Feature Highlight Card (gradient backdrop)
- **Background:** linear-gradient(180deg, rgba(83, 58, 253, 0.05) 0%, rgba(255, 97, 24, 0.03) 100%)
- **Border:** `1px solid #E5EDF5`
- **Padding:** `36px`
- **Box shadow:** noneDistinctive components (anything else the brand actually shows)
Logo marquees, testimonial carousels, pricing tables, gradient overlays, glassmorphism panels, bento grids, code blocks, terminal UIs, dashboard mockups — name and document anything visually distinctive. Sub-agents will reach for these specs when the storyboard calls for a beat featuring the X.
#### Glass Container (frosted overlay)
- **Background:** `rgba(255, 255, 255, 0.9)`
- **Border:** `1px solid rgba(255, 255, 255, 0.2)`
- **Backdrop filter:** `blur(8px)`
- **Use:** floating chat widgets, modal overlays, hero callouts only — the only place transparent fills appear in the systemThe rule: if a sub-agent in Step 5 has to invent CSS values for a component this brand actually uses, you under-documented this section. The values should be lookup-able, not guessable.
---
## 4. Spacing & Layout
The brand's rhythm. Three sub-sections, kept tight.
Spacing scale
Identify the base unit (typically 4px or 8px) and the full scale with usage context. Example:
**Base unit:** `4px`
| Token | Value | Used for |
| ----- | ------- | --------------------------------------------------------- |
| xs | `4px` | Inline icon gaps, tight badge padding |
| sm | `8px` | Button-group gaps, small component padding |
| md | `16px` | Card padding, form-field gaps, standard component spacing |
| lg | `32px` | Section vertical spacing, large card padding |
| xl | `60px` | Major section separation |
| 2xl | `100px` | Page-level rhythm, hero section padding |
Never use odd values (`13px`, `17px`) — the system only uses multiples of 4.Border-radius scale
Every radius the site uses, with what uses it.
- `0px`: Form labels, technical UI markers
- `4px`: Primary buttons, inputs, small badges
- `8px`: Standard cards, dropdowns
- `12px`: Feature cards, larger callouts
- `40px`: Icon buttons (square pill)
- `9999px`: Pill-shaped CTAs, status chipsWhitespace philosophy (one paragraph)
How does this brand use whitespace — generous and architectural? Tight and information-dense? Section gaps in the 60–100px range, or 20–40px? Document the brand's actual rhythm.
Generous whitespace as confidence. Section gaps are always `60–100px`. Content never touches viewport edges — minimum `40px` horizontal padding on mobile, `80–160px` on desktop. The brand uses negative space as active design, not emptiness.---
## 5. Iteration Guide (the load-bearing section)
5–10 numbered rules that encode the most important brand decisions. Each rule is a single actionable sentence stating what to do, with the specific values from this site. These are the "if in doubt, do this" rules sub-agents consult while composing beats.
The single most common failure mode is writing generic rules that could apply to any well-designed website. A rule that doesn't name a specific value, a specific color, or a specific component this brand actually uses is doing nothing.
Test for any rule you write: can you swap this brand for a different brand and have the rule still make sense? If yes, it's too generic. If no, ship it.
❌ Generic vs ✅ site-specific
| Generic (delete) | Site-specific (keep) |
|---|---|
| Use the primary brand color for CTAs. | All primary CTAs use Stripe Purple (#533AFD). Secondary actions use white background with #533AFD border + text. There is no third button color anywhere in the system. |
| Maintain visual hierarchy through color contrast. | Body text is #000000 on white, #FFFFFF on dark. Metadata uses #64748D on white only — never on dark. The brand has no mid-gray text on dark backgrounds. |
| Use clear typographic hierarchy. | All type is sohne-var. H1 48px/300, H2 32px/300, body 14px/400. Never use weights above 400 — this brand has no bold variant. |
| Use consistent spacing. | Spacing is from a fixed scale: 4, 8, 12, 16, 20, 24, 32, 40, 60 px. Section gaps are always 60–100px. Card padding is always 32px. No exceptions. |
| Buttons should have rounded corners. | Buttons are 40px tall minimum, 4px radius, 15.5px 24px padding. Pill-shape 9999px radius is reserved for the floating chat trigger only. |
One worked example (Framer — 5 rules)
### Iteration Guide
1. **All interactive elements use Framer Blue (`#0000EE`)** — links, primary buttons, active states, focus indicators. Secondary uses `#0099FF` for hover. **No other interactive color exists in the system.**
2. **Typography: GT Walsheim Medium for headings, Inter for body.** Hierarchy enforced through size only, never color. H2 `62px`, H5 `85px`, body `14px`, labels `12px`/500. Text defaults to `#000000` on white, `#FFFFFF` on dark.
3. **Spacing is base-4** — every margin / padding / gap is a multiple of `4px`. Section gaps `60–100px`. **Never use odd values like `13px` or `17px`** — the system has no place for them.
4. **Cards: white (`#FFFFFF`), `1px` border `#EFEFEF`, `8px` radius, `16–20px` padding, no shadow by default.** Dark-mode cards swap to `#1A1A1A` background with `#242424` border. Shadow only appears on hover.
5. **Glass containers** use `rgba(255,255,255,0.9)` background, `1px` border `rgba(255,255,255,0.2)`, optional `backdrop-filter: blur(8px)`. **These are the only place transparent fills appear** — everywhere else uses solid color.If your draft has a rule like "all interactive elements require visible focus states for accessibility" — delete it. Not wrong, just not load-bearing for _this_ brand.
---
Rules
- Use exact values from
capture/extracted/tokens.json. Cross-reference with screenshots when needed. - Name colors and components descriptively — "Stripe Purple" not "Accent 1."
- When you can't extract exact values, estimate from visual inspection and note it.
- No "Assets" section —
capture/extracted/asset-descriptions.mdis the asset index. - No "Motion" section — the storyboard specifies motion per-beat.
- No separate "Components" section — Quick Reference is where components live.
- No "Depth & Elevation" tables — shadow language is implied by the brand's mood (heavy shadows for premium, no shadows for flat/clean); sub-agents pick appropriate values without a table.
---
Quick User Check (before moving to Step 2)
30-second sanity check before Step 2:
"Here's what I extracted as [Brand Name]'s visual identity:
>
- Colors: [primary], [accent], [2-3 others with roles]
- Fonts: [headline font], [body font]
- Tone: [1 sentence on the brand feel]
>
Does this match how you want the video to feel? Any corrections or overrides before I start the storyboard?"
If the user has corrections ("use the blue, not the gray" / "ignore the dark mode" / "we just rebranded, use [these values] instead") — update DESIGN.md now. One minute here saves thirty minutes of rebuilding.
---
What makes a useful DESIGN.md
A sub-agent reading just your Quick Reference + Iteration Guide should be able to:
1. Pick the right color for any primary action, secondary action, body text, error state 2. Pick the right font/weight/size for any headline, body, metadata 3. Know which 2-3 rules they cannot break without losing the brand
That's the test. If they can answer those three questions from a 60–120 line doc, you've nailed it. If they need to read 400 lines of mood-board prose to find a color, you've buried the signal.