DemoStudio — the playbook
You are building a premium product-demo motion-graphics film: one seamless timeline of
full-screen scenes telling the USER'S story (pick an arc that fits their product, e.g. problem →
product → payoff, before → after, or many inputs → one output; do not default to a feedback-capture
loop), with camera moves between beats. The whole video is ONE self-contained HyperFrames composition
(index.html = HTML + CSS + GSAP) that renders to MP4, then a single SFX track is muxed on.
Start from the example: example/index.html is a compact, commented, working scaffold that
shows the whole architecture (paused timeline, 3 scenes, a camera push, a flood, an endcard). Read
it first, then grow it into the real thing scene by scene.
0. When to use / not use
- Use for animated product demos, launch/hype videos, feature stories, flywheel/loop narratives. Any "show the product doing something, beautifully, scene by scene" film.
- Not for: single-concept explainer reels (one scene per spoken beat), talking-head/CapCut edits, or avatar/TTS videos. Different tools entirely.
1. Architecture (always the same)
- One paused timeline:
const tl = gsap.timeline({paused:true}); register it onwindow.__timelines["main"] = tl. HyperFrames seeks it per frame. - Scenes = absolutely-positioned full-frame
<div>s inside#stage, overlapping in the DOM; faded in/out by the timeline. Only the active one is visible at a given time. - Schedule by ABSOLUTE time — the last arg of every
tl.*call is the start time in seconds. Anchor scene offsets with named consts (e.g.S1,S2,END) so changing one shifts the rest. ADURconst + thedata-durationattrs (root, bg, stage) must all match the total length. - Deterministic only: no
Math.random()/Date.now()/ arglessnew Date()— they break the frame-seeked render. Use a seeded PRNG (see themul()/rnd()helpers in the example). - Build loop:
npx hyperframes lint # 0 errors required npx hyperframes render --output out/raw.mp4 -q high ffmpeg -i out/raw.mp4 -c:v libx264 -pix_fmt yuv420p -g 30 -keyint_min 30 \ -sc_threshold 0 -r 30 -crf 17 -movflags +faststart out/vNN.mp4 # ALWAYS re-encode ffmpeg -ss <t> -i out/vNN.mp4 -frames:v 1 /tmp/f.png # verify by viewing frames - gsap defaults:
gsap.defaults({ease:"power3.out", duration:0.6}). RegisterMotionPathPluginif used.
2. Brand tokens (re-skin per product) — see docs/BRANDING.md
Everything visual is driven by :root CSS vars. To make it your product's film, swap the tokens
and keep the system: one canvas colour, near-black ink, exactly ONE restrained accent, status
green/red/blue, a shadow style, and three fonts (display / body / mono). Keep one focal accent per
scene. Dark scenes use a deep navy + a warm radial glow, never flat black.
3. The technique library — copy these patterns
The full, code-level version is in docs/ARCHITECTURE.md. The set-pieces:
- Text: typewriter caption (per-char spans + stagger, flip the last word to the accent), kinetic
fly-in lines (
opacity/y/blur+ stagger), count-up numbers (tl.callsets text + a scale punch). - People as circular avatars: any square photo →
border-radius:50%; overflow:hidden+ a name + a role/credibility pill. Mix real faces with code-drawn identicons (deterministic 5×5 symmetric pixel grid) for the natural "not everyone has a face" look. - Floods & particles: scatter with jittered-y + brick-offset-x (don't read as rows), clamp on
screen; pop in
back.out; implode → click → burst on a ring. - Transitions: 3D fly-in (
rotationY/rotationX/z+ blur → flat), blur-out handoff, edge-on flip to swap content unseen, HUD-pill flip to change a label cleanly. - Camera (the signature): it's just
scale+x/yon the scene element. Push/pull, measured dolly across a tall element, and a camera that follows a moving element. - Element moves home: measure the translate to a target slot, leave a dashed ghost, recolor mid-flight, pop a code-diff chip beside it so it references what changed.
- "Context accumulates" web: SVG lines drawn on (
strokeDashoffset→0), a hub that grows a notch per connection,+Nbadges riding the lines in, a counter ticking up. - Explosion endcard: flash + orange disc + shock ring + ~22 shards + screen shake, revealing a value card underneath. Keep it tight (~3s).
- Realistic SaaS/GitHub UI in pure HTML/CSS: app windows, nav/subnav, PR rows, a release page with per-user credit avatars, diffs that tick green one-by-one. All divs + SVG.
4. Gotchas
translate(-50%,…)+ GSAP fight overtransform— center withxPercent/yPercentor flex.- Hidden-then-draw lines/dots: set them hidden at
t=0or they flash early. - Scope per-scene CSS under the scene id (
#capture .btn) to avoid cross-scene collisions. - Always re-encode after render (sparse keyframes → frozen still otherwise).
overwrite:"auto"on tweens retargeting the same property (successive camera scales).- SVG
transform-originis unreliable — preferstrokeDashoffsetdraw-ons / explicit origins.
5. Sound design — see docs/SOUND.md + sfx/ + templates/
The film renders silent; sound is a separate muxed layer you can re-roll independently.
- The repo ships a complete all-original SFX kit in
sfx/(25 sounds, every one synthesized — seetemplates/make_kit.py). Regenerate any time withpython3 templates/make_kit.py. - Place cues with
templates/build_audio_kit.py(edit theCUESlist of(time, token, dB, semitones)) →master.wav→ mux:ffmpeg -i video.mp4 -i master.wav -c:v copy -c:a aac -shortest out-sound.mp4. - Motion → SFX: pop=appear, click=click, whoosh=motion, wash=converge, coin cascade=counter, confirm(warm muffled)=saved/live, boom=explosion. Full mapping table + mixing rules in docs/SOUND.md.
6. Starting a NEW video
- Copy
example/index.htmlinto your project (ornpx hyperframes initand paste the pattern in). - Re-skin the
:rootbrand tokens to your product (docs/BRANDING.md). Keep the system. - Plan the scene beats as a story and lay anchor consts. One accent per scene, no em dashes.
- Build scene-by-scene, copying patterns from docs/ARCHITECTURE.md. Lint + render + frame-verify each scene before moving on. Re-encode.
- Generate any face photos up front so they bake while you build the timeline.
- Do the sound pass last: edit the cue list, build
master.wav, mux the-sound.mp4.


