Point at a spec, don’t describe a vibe
A HyperFrames project can carry a design spec:frame.md. Its frontmatter holds the machine-readable brand — exact hex values, font families, weight relationships. The prose below the frontmatter carries intent and the brand’s Do’s and Don’ts.
When a spec exists, name it:
Use the palette and type from frame.md. Build a 15-second feature announcement.
- Avoid:
make it feel on-brand and premium - Instead:
pull colors and fonts from frame.md; premium means generous spacing and one restrained accent
on-brand is a mood the agent has to guess at. A spec’s frontmatter is normative. The agent quotes your hex and font family verbatim instead of approximating, then reads the prose for intent.
If your brand lives somewhere else, attach it. A PDF brand guide, a screenshot, or pasted hex codes all read more reliably than a described impression.
Brand is truth for color and type — not for layout
A design spec says what the brand looks like. It does not say how to compose a video frame. Name what’s sacred and let the agent stage the rest:Colors and fonts are locked to the brand — keep the exact hexes and the display/body pairing. Layout, spacing, and motion are yours to compose for video.Why this works: web-scale brand values don’t survive video. A
1px border with a 0.06-opacity shadow is invisible after H.264 compression. A web body size vanishes on a 1080p frame.
So treat the spec in two halves:
- Strict — brand colors, fonts, weight relationships, and the background choice. If the brand is a light canvas, keep it light.
- Scaled up for the medium — type sizes, decorative opacity, border weight.
Use the site’s own palette and fonts
When there’s no spec but the brand is out there, point at it and let the agent extract:
Match this site’s look — pull its palette and fonts — and make a 20-second launch clip: https://…
For a well-known brand, naming it is often enough. The agent researches the palette and typography itself.
One caveat is worth stating. A single-page-app homepage often returns a near-empty shell. If the palette comes back thin, point the agent at a blog, press, or docs page instead.
This is the same brand-truth move. The site is the source instead of a file.
Bringing in a Figma frame, brand, or logo
If the brand lives in Figma, ask for it directly. The agent imports it rather than eyeballing a screenshot:
Bring in the brand tokens from this Figma file, then build the intro: https://figma.com/…
Import this Figma frame as the opening scene and this logo as an SVG: <links>
Figma import freezes each import as a local file with recorded provenance. Renders never call Figma, so they stay deterministic. Brand variables come in as composition brand tokens.
Two things are worth knowing when you phrase the ask:
- Import tokens before components. Say “brand tokens first, then the components.” That’s what lets imported component colors link to your brand variables instead of baking in duplicate hexes.
- Storyboard frames are states, not slides. If you point at a strip of scene frames, ask the agent to reconstruct the motion between them. A frame showing an element at four positions is one element animating, not four stills to flip through.
Keeping a multi-video series consistent
A series is a launch set, a weekly clip, or a per-region cut. Consistency comes from a shared source of truth, not from re-describing the brand each time:
All four videos share frame.md for palette and type. Only the headline and the stat change per video.
The constant parts come from that one authoring spec, or from one set of imported Figma tokens. The parts that vary become variables.
When frame.md changes, ask the agent to rebuild the affected compositions. That’s how the authored HTML picks up the new brand value.
Some compositions already exist and have to re-skin at render time. Declare the shared brand tokens as composition variables instead. Every scalar variable is applied as a --{id} CSS custom property on the composition root, so var(--id) in your CSS follows the override.
This is where design systems and templating meet. The brand is shared. The content is parameterized.
Supplying brand assets by path
Logos, fonts, textures, and product shots are inputs. Hand the agent the path — don’t ask it to draw them:Logo atPrefer an SVG logo over a raster one. It scales and it animates. State the paths explicitly. That way the agent wires up the real assets instead of generating stand-ins. It also keeps the render deterministic, because every asset is present locally before the render starts.assets/logo.svg, brand font files inassets/fonts/, product shot atassets/hero.png. Use them; don’t invent placeholders.
Supply brand inputs up front — don’t restyle afterward
Give the agent your palette, fonts, and logo (or aframe.md) before the build. Don’t override the composition after the fact.
- Avoid:
make the launch video, then restyle every scene to my colors afterward - Instead:
make the launch video with my palette, fonts, and logo as inputs up front
Templates and variables
The brand sections above cover what should never change per render. Templates and variables cover the parts that should. A card per customer. A stat per quarter. A name per recipient. When you know a composition will be reused, say so in the prompt. Name the parts that change. The agent turns them into declared variables — typed, labeled slots filled at render time instead of hardcoded into the HTML. The trigger phrase is simple. Call out the slots:Build a 6-second title card. Make the name, the logo, and the accent color variables; everything else stays fixed.Default variable values. The same composition re-rendered with
--variables overrides — different name, logo, and accent, zero re-prompting.
The agent declares data-composition-variables on the composition root, with
the right type for each slot. The name is a string. The accent is a color.
The logo is an image, and a plain URL is a valid value for it. A plain <img>
logo needs no timing attributes. Only <video> and <audio> variables involve
the media wiring described in variables. One composition,
many fills.
Say what type each slot is
There are seven variable types:string, number, color, boolean, enum,
font, and image. Each one validates differently at render time. In
the browser editor, boolean, enum, color, and number each get
their own control, while string, font, and image use a plain text input.
You don’t write the JSON yourself. But naming the type in the prompt removes a
guess:
Variables:plan(enum: Free / Pro / Enterprise),price(number, shown as$),featured(boolean — toggles the ribbon),headline(text).
- Avoid:
make the plan and price editable - Instead:
plan is an enum (Free / Pro / Enterprise); price is a number in dollars
enum with declared options is checked against that
list at render time, so enum-out-of-range gets caught. A number can carry
min, max, step, and a unit label, which is what gives the browser editor a real
slider instead of a bare text box. Say “editable” and you leave the agent to
pick a type. A mistyped value then surfaces much later.
Template, then render one per record
Once the varying parts are variables, the same source renders once per data row. This is a real batch mode, not a copy-paste-per-video loop. You author the composition once and feed it a list of value sets:Build this as a template withThe agent authors the composition, then runs a batch render. The batch input is a JSON array. Each row is one set of variable values, and each row produces one output file.nameandtitlevariables, then render one video per row of my data — output torenders/{name}.mp4.
{key} placeholders in the output path get filled from that row.
If your source is a CSV, say so. The agent converts it to the row array the
batch expects.
Add “fail on any undeclared or mistyped value” and it renders with
--strict-variables. A typo in a column name then stops the run instead of
silently rendering the default.
Everything shares one composition. So a design fix propagates to every output on
the next render. You are not editing a hundred near-duplicate files.
Personalization asks
Personalized-at-scale videos are the same pattern, with the value set coming from your data:
A 10-second welcome clip that greets each new signup by first name and shows their company logo. I’ll supply a list of { firstName, logoUrl } records.
firstName is a string. logoUrl is the image slot your composition binds to
an <img src>.
Pass assets as URL references, not inlined data. URL-shaped values travel
cleanly through both the local renderer and distributed
Lambda renders.
Wiring this behind your own product UI or an agent instead of the CLI? The
@hyperframes/sdk opens a base template and layers a sparse
override set per instance. The host then stores only each record’s delta.
Declare up front — don’t bake values in
The most common miss is describing the finished video with the values already fixed, then asking to “make it reusable” afterward:- Avoid:
Make a card that says "Acme — Pro plan — $49". Later I'll want other companies too. - Instead:
Make a plan card. Variables: company (text), plan (enum), price (number, $). Show "Acme / Pro / 49" as the default.
"Acme — Pro — $49" into the
markup and you get a composition with no slots. Reuse then means an edit pass
over hardcoded text for every variant. That is exactly what variables exist to
avoid.
Prove the template actually re-skins
A template that never re-skins can pass every gate you have.lint and check
verify structure. --strict-variables catches an undeclared or mistyped key.
Neither can tell you whether the values you passed ever reached the DOM.
The failure looks like success. The render completes, exits clean, and is
pixel-identical to the default.
So test it differentially. Render twice and compare:
--variables overrides are global by variable ID. The compiler
applies a matching override to CSS custom properties on the root and on
sub-compositions pulled in with data-composition-src. data-variable-values
is still the per-instance way to give two mounts different values.
Scoped JavaScript inside a sub-composition reads its own per-instance variable
table. So forward values at the mount point when that script calls
getVariables() instead of reading CSS.
The capstone keeps its variables on one root file for
simplicity, not because templates require one file. Sub-compositions work as
long as shared CSS-bound IDs are declared consistently. Use mount-point values
for instance-specific or JavaScript-read inputs. Its exact variable clause is
quoted at the end of this section.
What can’t be a variable
A few inputs are read once at compile time, and no variable can move them:- composition dimensions (
data-width/data-height) - the root composition’s total duration
- frame rate
- output format, codec, or quality
- Avoid:
make the video length a variable so each render can be a different duration - Instead:
author one composition per target length— or vary a clip’s duration, which is re-read from the live DOM
data-duration
per render, not a variable. See
what can’t be a variable for the
full list and the compile-time-vs-live-DOM rule behind it.
An authored CSS custom property always wins over a same-named variable. Say
your composition already defines its own
:root { --accent: ... } as a
hand-written theme token. A variable called accent never overwrites it — the
authored value stands. A render-time --variables override still wins over
both. So when you need to override an authored value per render, use
--variables, not a same-named declared variable.Related
Variables (concept)
The mechanics: declaring types, per-instance overrides, batch renders, precedence.
@hyperframes/sdk
Template + sparse-override editing behind your own product UI or agent.
The specification dial
How much to specify — and why naming the type is cheap precision.
Brand, templates and storyboards
Brand tokens as variables that re-skin every reuse from one value.
Variables: exposeground(default#0a0a0a) andink(default#3CE6AC) as composition variables on the single root file, bound via CSS custom properties everywhere (including the duotoned mural), so one--variablescall re-skins the entire journey. It will be rendered twice: the default brand palette, and a second full render with{"ground":"#0d1420","ink":"#c8ff3d"}. Architecture constraint (technical): single composition file — oneindex.html, one variable scope. […] Nodata-composition-srcsub-files.
Storyboards
Variables and templating reuses one composition across many renders. Storyboards are the other axis of scale: one film with many scenes. Past a handful of beats, describing each scene from a blank page is the slow way. “Then frame 2 shows X, then frame 3 shows Y.” It also drifts, because nothing ties the frames to each other. Prompt the plan once instead — the throughline, the job each frame does, the rule that paces reveals. Then let the build put frames against it. This narrative vocabulary is a writing discipline, not extraSTORYBOARD.md schema. The workflow translates your plan into the smaller machine-readable shape the build consumes.
Asking for a storyboard before the build means you review the plan before anything is built. Either way, the plan this page teaches is what the build works from.
Prompt the plan, not the scenes
A storyboard is a short, structured document that sits above the individual frames. It holds three things:- one arc
- one direction block that every frame inherits
- a light per-frame spec — not a full description — for each key moment
Storyboard a 3-frame piece: hook → substance → landing, silent, ~15 seconds, with a callback that pays off the opening motif.Everything below is the vocabulary that turns “storyboard” from a loose word into a plan the build can execute in one pass.
State the film’s shape once
Before any frame, fix four things that every frame will be judged against:- Message — the one-sentence thesis the whole film has to prove. If a frame doesn’t serve it, cut the frame, not the message.
- Arc — the beat sequence, named plainly.
Hook → Substance → Landing, orHook → Problem → Solution → Proof → CTA. Use a shape word like “listicle” when the frames are parallel entries rather than a rising sequence. - Audience — who it’s for, in a phrase. It calibrates tone and jargon for every frame at once.
- Mood — one music or energy descriptor, like “tense synth pulse, resolving to warm”. Every frame’s pacing should agree with it, even in a silent piece.
Set the direction once, apply it to every frame
A storyboard’s direction block is the set of rules every frame obeys without restating them. Four are worth naming explicitly. Two-color discipline. Name a ground color and one ink color. Then say the rule out loud: nothing ever gets a second hue for emphasis. A bigger moment gets bigger through inversion, weight, scale, or density.- Avoid:
use the brand colors, plus a highlight color for the important bits - Instead:
ground: deep navy; ink: warm white. Emphasis = invert, scale up, or go denser — never a third color.
no purple-blue AI gradients, no bokeh, no browser chrome, no drop-shadow cards, no infinite loops or randomnessSwap in whatever clichés are wrong for your film. The point is naming them before a frame drifts into one.
Give each frame a job
The direction block covers everything shared. So each frame’s own prompt only needs to say what’s different about it:persuasion and beat. Without them, a frame is “a scene that shows the stat.” With them, it becomes “a scene that proves the stat, and here’s how it feels to land.”
A frame with a named persuasion device and beat gives the build a reason for every choice. A frame with only a visual description gives it none.
The callback
Introduce a motif early: a shape, a mark, a phrase, a piece of color. Have it return later, denser or fuller, as a deliberate payoff. Say both halves in the plan — where the motif is planted, and how it changes when it returns.A single thin accent dot appears top-right in frame 1 at low weight. In the landing frame, that same dot expands and fills into the full logo lockup — same motif, now complete.State the return explicitly. Otherwise a rebuild is free to treat the early motif as throwaway texture. The callback only works if the plan says the second appearance is the same element, not a new one that resembles it.
Worked example: a silent 3-frame storyboard
Storyboard a 3-frame, ~15-second, 1920x1080 piece. Silent — no narration, no VO track. Message: “Fernwell gives you back the hours other tools take.” Arc: Hook → Substance → Landing. Audience: small-team operators evaluating a new tool. Mood: tense synth pulse resolving to warm. Direction for every frame: ground color deep navyRendered from the prompt above, unedited — no audio track, exactly as asked.#0b1220, ink color warm off-white#f4efe6. Nothing else gets a hue — emphasis is inversion, scale, or density only. Reveals stage on internal timestamps, since the piece is silent and there’s no spoken cue. At each frame’s t=0 only its first element is on screen. The rest arrive on the timestamps below. Holds stay fully still, no drift or breathing. No purple-blue AI gradients, no bokeh, no browser chrome, no drop-shadow cards, no infinite loops or randomness. Frame 1 — Hook (0.0–4.0s), type: hook, persuasion: counterexample, beat: recognition + tension, focal: the headline. At 0.0s: bold ink headline “Most tools slow you down.” slams in, centered. At 1.5s: a single thin accent dot (ink color, small, low weight) fades in top-right — the motif, planted quietly. Hold from 3.0–4.0s. Frame 2 — Substance, the breather (4.0–10.0s), type: benefit_highlight, persuasion: numbered enumeration, beat: aha, focal: the stat. This is the one deliberately calmer, more static frame in the piece. Everything else develops continuously. This one mostly holds. At 4.0s: the accent dot from frame 1 carries over, now larger, sitting quietly left-of-center. At 5.0s: a big stat “3.2 hrs / week” fades in beside it, no motion after it lands. Static hold 6.0–10.0s. Frame 3 — Landing (10.0–15.0s), type: cta, persuasion: callback + distillation, beat: resolve + inevitability, focal: the completed motif. At 10.0s: the accent dot from frames 1–2 expands and fills into the full Fernwell wordmark lockup — same motif, now complete, denser and larger. At 12.0s: tagline “Fernwell. Built for flow.” stamps in below it. Hold 13.5–15.0s.
Related
Anatomy of a one-shot prompt
The six-part skeleton a single beat uses — the same discipline, one frame at a time.
Recreating something you saw
Transcribing motion frame by frame — the same rigor a storyboard’s per-frame timestamps need.
Brand, templates and storyboards
The two-color discipline and brand tokens a storyboard’s direction block draws from.
How a HyperFrames project works
STORYBOARD.md as a production artifact — where storyboard plans land, downstream of BRIEF.md.
The clip card — the <div class="clip"> typed in the opening travels the whole journey: it slides onto the wire as a clip chip after being typed, rides ahead of the camera between regions (handing itself off — visible leaving one region and arriving in the next), and is the thing that finally renders at the end. It is the protagonist.
That clause paying off, rendered — the protagonist chip arriving at the render slot after a full minute on the wire.
Related
Figma Import
Import brand tokens, assets, components, and motion from a Figma file.
Variables and templating
Turn brand tokens into variables that re-skin a whole series from one value.
The specification dial
How pinning exact hexes and type direction removes drift.
Claude Design
Attach a brand guide or screenshot to seed a first draft from your look.
Design system: the providedThat clause paying off, rendered. The closing lockup is where the brand file’s display face and its one sanctioned gradient finally appear together.frame.mdis the single source of brand truth — read it first and obey it exactly. It is the real HyperFrames brand (by HeyGen), dark-first: flat near-black ground […] mint#3CE6ACused sparingly as the single accent — mint means go / primary / active. The mint→cyan gradient (#3CE6AC → #00E3FF) appears in exactly one place in the whole film: the final CTA lockup.