Skip to main content
“Make it on-brand” is the vaguest thing you can ask: the agent can’t know your brand, so it invents one. Give it a source of brand truth instead and name it in the prompt: a design spec, your live site, a Figma file, or a brand kit in the Studio app. This page also covers templates you reuse and plans that span many scenes.

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
Why this works: on-brand is a mood the agent has to guess at. A spec’s frontmatter is normative. The agent quotes your hex and font family verbatim instead of approximating, then reads the prose for intent. If your brand lives somewhere else, attach it. A PDF brand guide, a screenshot, or pasted hex codes all read more reliably than a described impression.

Brand is truth for color and type — not for layout

A design spec 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.
Over-specifying layout from a web design system fights this. Pin the palette and typography. Delegate the frame.

Use the site’s own palette and fonts

When there’s no spec but 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. 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 at assets/logo.svg, brand font files in assets/fonts/, product shot at assets/hero.png. Use them; don’t invent placeholders.
Prefer an SVG logo over a raster one. It scales and it animates. State the paths explicitly. That way the agent wires up the real assets instead of generating stand-ins. It also keeps the render deterministic, because every asset is present locally before the render starts.

Supply brand inputs up front — don’t restyle afterward

Give the agent your palette, fonts, and logo (or a frame.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
Why this works: the agent settles the look before the first scene, so colors, spacing, motion, and component treatments hang together as one system. Restyling scene by scene afterward pulls threads out of a design that was balanced as a whole. You spend more effort fighting it than handing over a spec would have cost.

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
The engine rationale: an enum with declared options is checked against that list at render time, so enum-out-of-range gets caught. A number can carry min, max, step, and a unit label, which is what gives 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 with name and title variables, then render one video per row of my data — output to renders/{name}.mp4.
The agent authors the composition, then runs a batch render. The batch input is a JSON array. Each row is one set of variable values, and each row produces one output file. {key} placeholders in the output path get filled from that row. 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.
The engine rationale: variables are runtime values a script applies to the live DOM. They resolve from declared defaults first, then per-instance overrides, then the CLI. Declare them up front and the reusable structure exists from the first render. The default is then just one more value set. Bake "Acme — Pro — $49" into the markup and you get a composition with no slots. Reuse then means an edit pass over hardcoded text for every variant. That is exactly what variables exist to avoid.

Prove the template actually re-skins

A template that never re-skins can pass every gate you have. lint and check verify structure. --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:
Two identical files mean the override never reached the property you expected. Check three things. Is the variable ID declared? Does the render command use that exact ID? Is the visible property actually bound to its CSS custom property or variable value? Render-time --variables overrides are global by variable ID. The compiler applies a matching override to CSS custom properties 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
So this doesn’t do what it reads like:
  • 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
If total length must differ per output, that is a different root data-duration per render, not a variable. See what can’t be a variable 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.

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.
This clause from a longer HyperFrames film produced the shot below. You can lift the wording for your own video:
Variables: expose ground (default #0a0a0a) and ink (default #3CE6AC) as composition variables on the single root file, bound via CSS custom properties everywhere (including the duotoned mural), so one --variables call re-skins the entire journey. It will be rendered twice: the default brand palette, and a second full render with {"ground":"#0d1420","ink":"#c8ff3d"}. Architecture constraint (technical): single composition file — one index.html, one variable scope. […] No data-composition-src sub-files.

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 extra STORYBOARD.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
The workflow reads the plan and builds each frame’s HTML sub-composition against it. Be precise about the shape of the film, and the frames come out already agreeing with each other on pacing, palette, and payoff. You never restate any of that per frame. The trigger is naming the arc and asking for a storyboard rather than a single scene:
Storyboard a 3-frame piece: hook → substance → landing, silent, ~15 seconds, with a callback that pays off the opening motif.
Everything below is the vocabulary that turns “storyboard” from a loose word into a plan the build can execute in one pass.

State the film’s shape once

Before any frame, fix four things that every frame will be judged against:
  • Message — the one-sentence thesis the whole film has to prove. If a frame doesn’t serve it, cut the frame, not the message.
  • Arc — the beat sequence, named plainly. Hook → Substance → Landing, or Hook → Problem → Solution → Proof → CTA. 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.
Say these four once, up front. Then no individual frame prompt has to re-justify its tone.

Set the direction once, apply it to every frame

A storyboard’s direction block is the 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.
VO-paced reveals. The rule itself lives in Add voice, music and sound. A storyboard is where you apply it per frame. At t=0, only what the narrator is saying is on screen, and each part arrives on its spoken cue. Pair it with a hold behavior. Say whether a held frame stays fully still or gets a subtle idle. Never ask for a slow drift or “breathing” — that reads as unfinished, not as a choice. If the piece is silent, keep the rule’s shape and swap the trigger. Reveals land on named timestamps instead of spoken clauses. The pacing still has to be deliberate. There’s just no VO to key it to. One breather. Across the whole film, name exactly one frame as the breather. It’s the deliberately calmer, more static beat, or the longest held read. Every other frame keeps developing continuously. Naming it prevents two failures. The build won’t over-animate the one frame that’s supposed to let the audience exhale, and it won’t under-animate the rest to match it. The negative list. One list of banned visual clichés, stated once. It’s a standing filter, not a fresh list per frame. Every frame gets checked against it as it’s built:
no purple-blue AI gradients, no bokeh, no browser chrome, no drop-shadow cards, no infinite loops or randomness
Swap in whatever clichés are wrong for your film. The point is naming them before a frame drifts into one.

Give each frame a job

The direction block covers everything shared. So each frame’s own prompt only needs to say what’s different about it:
Never skip persuasion and beat. Without them, a frame is “a scene that shows the stat.” With them, it becomes “a scene that proves the stat, and here’s how it feels to land.” A frame with a named persuasion device and beat gives the build a reason for every choice. A frame with only a visual description gives it none.

The callback

Introduce a motif early: a shape, a mark, a phrase, a piece of color. 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-mini below is deliberately small and silent: three frames, ~15 seconds, no narration. That makes the whole pattern checkable in one cheap render — arc, direction block, per-frame job, one breather, one callback. Do this before you write a longer, narrated storyboard.
Storyboard a 3-frame, ~15-second, 1920x1080 piece. Silent — no narration, no VO track. Message: “Fernwell gives you back the hours other tools take.” Arc: Hook → Substance → Landing. Audience: small-team operators evaluating a new tool. Mood: tense synth pulse resolving to warm. Direction for every frame: ground color deep navy #0b1220, ink color warm off-white #f4efe6. Nothing else gets a hue — emphasis is inversion, scale, or density only. Reveals stage on internal timestamps, 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.
Rendered from the prompt above, unedited — no audio track, exactly as asked.

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.
In a real film. This clause from a longer HyperFrames film produced the shot below. You can lift the wording for your own video:
The clip card — the <div class="clip"> typed in the opening travels the whole journey: it slides onto the wire as a clip chip after being typed, rides ahead of the camera between regions (handing itself off — visible leaving one region and arriving in the next), and is the thing that finally renders at the end. It is the protagonist.
That clause paying off, rendered — the protagonist chip arriving at the render slot after a full minute on the wire.

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.
In a real film. This clause from a longer HyperFrames film produced the shot below. You can lift the wording for your own video:
Design system: the provided frame.md is the single source of brand truth — read it first and obey it exactly. It is the real HyperFrames brand (by HeyGen), dark-first: flat near-black ground […] mint #3CE6AC used sparingly as the single accent — mint means go / primary / active. The mint→cyan gradient (#3CE6AC → #00E3FF) appears in exactly one place in the whole film: the final CTA lockup.
That clause paying off, rendered. The closing lockup is where the brand file’s display face and its one sanctioned gradient finally appear together.