Claude Design → “Send to HyperFrames” (Single-File Import)
This guide is fully self-contained: everything needed to author a valid “Send to HyperFrames” composition is inline here. Do not rely on fetching any other document — produce the composition from this guide alone.Your medium is a HyperFrames composition: plain HTML + CSS + a paused GSAP timeline. When the user clicks “Send to HyperFrames”, that composition is imported into a hosted HeyGen project, previewed in HyperFrames, and rendered to MP4 in the cloud. You produce a valid first draft — a HyperFrames motion-design agent enhances it afterward (sound, media, polish). You are not producing the final video.
How this path differs from download-ZIP
There are two ways a Claude Design composition can reach HyperFrames. This guide is only about “Send to HyperFrames.”
The single most important consequence: there is no file tree on the other side. A relative path or sibling-file reference (
fonts/…, uploads/…) simply does not arrive. Every asset must be resolvable: an inline data: URI (preferred — the only fully self-contained form) or a publicly-fetchable absolute URL, never a relative path or local-file variable (see Asset & fidelity rules).
The workflow you’re feeding into
- You (Claude Design) — author a valid HyperFrames composition as a single self-contained HTML.
- Send to HyperFrames — one click. The importer fetches your HTML, validates it, and creates a hosted HeyGen project. Import is free.
- Enhance in HyperFrames — a motion-design agent adds what your export can’t: sound effects, background music, and (later) HeyGen media. This is the paid step.
- Render — the cloud pipeline produces the MP4.
What you send: a single self-contained composition HTML
One.html file. Everything the composition needs to display its own content must live inside that file:
- All CSS inline (in
<style>or style attributes). - All brand assets (fonts, images, logos) inlined as base64
data:URIs. - The GSAP timeline defined synchronously at page load and registered on
window.__timelines.
cdn.jsdelivr.net/npm/@hyperframes/..., gsap@…) — the render pipeline fetches those at render time by design. GSAP is version-pinned (gsap@3.14.2); the @hyperframes packages are served unversioned (latest published runtime), matching what a real import ships. Do not inline the runtime.
Do NOT send a bundled / splash-loader artifact. If your tooling wraps the composition in a loader that assembles the real content at runtime (a <template> splash, a base64 asset manifest, a window.__resources blob), the importer cannot read it and rejects it. Send the raw composition HTML whose real content is in the live DOM.
The composition contract (the importer checks these — get them right or it 422s)
- One live root with
data-composition-id="main"on a real element in the DOM — not inside a<template>. (Elements inside<template>are inert and don’t count as the live root.) - Numeric
data-widthanddata-heighton that root (e.g.data-width="1920" data-height="1080"). - Numeric
data-durationon the root (total seconds) and on each scene, plusdata-startper scene. - Scenes tile end-to-end within the total duration (no gaps).
- The timeline is registered and keyed to the composition id:
window.__timelines["main"] = tl. - The timeline is a paused GSAP timeline, built synchronously at load (the framework drives playback — see Determinism).
Asset & fidelity rules (this is where Send-to differs most)
Every asset must be resolvable from the single HTML file — an inlinedata: URI (preferred; the only fully self-contained form) or a publicly-fetchable absolute URL. A relative path, sibling-file ref, or local-file variable is simply missing on import and renders blank.
Inline a font like this (base64 elided):
- Inlining the brand font is what preserves fidelity. If the font isn’t inlined, it falls back to a system font and the brand look is lost. Do NOT use a Google Fonts
<link>for a Send-to composition — inline the@font-faceinstead. - Prefer inline; never a dead or expiring ref. Inline images/logos as
data:URIs when practical — the only fully self-contained form. If an asset is too large to inline, use a publicly-fetchable absolute URL; never a relative path, a local-file variable, or a short-lived/private host (a signed URL that expires,s3:///presigned URLs) — those 404 by render time. (The runtime/GSAP/shader scripts are the only network fetches by design.) - Watch for injected loader cruft. If assets are fetched through a proxy/CDN that injects its own beacon scripts (e.g. a Cloudflare
cdn-cgi/challenge-platformsnippet), strip them — they 404 in the renderer and add noise. - No placeholder assets (
placehold.co, lorem-ipsum). Ship real brand content. - Preserve substance; adapt form. A composition is a rebuild of the design into a timed video, so the FORM changes: a static page becomes multi-scene motion, page nav/footer chrome is dropped or folded in, and a single hero may expand into a short narrative arc. But the brand’s SUBSTANCE must survive verbatim: the real headline and copy, the exact palette and fonts, named products and features, real metrics and data points, and signature visual elements (a chart, a waveform, a product shot). Keep distinctive specifics specific: do NOT genericize them into vague phrases (e.g. turning a real metric like
2.4M signals/secinto “streaming now”, or a named feature into a generic label), and do NOT invent copy or numbers the brand never provided. When you expand a hero into extra scenes, build them only from the brand’s own language and facts.
Building the composition
1. Choose dimensions and scene count by video type
2. Fill brand identity on :root, and avoid the monoculture
- Banned defaults: Inter, Inter Tight, Roboto, Open Sans, Noto Sans, Lato, Poppins, Outfit, Sora, Fraunces, Playfair Display, Cormorant Garamond, EB Garamond, Syne, Cinzel, Prata, Bodoni Moda, Nunito, Source Sans, PT Sans, Arimo.
- Banned pairings: Fraunces + JetBrains Mono, Inter + anything, Playfair + Lato.
- Question these: gradient text, cyan-on-dark, pure
#000/#fff, identical card grids, left-edge accent stripes, everything centered with equal weight. - Use dramatic weight contrast (300 vs 900). Minimum sizes: 60px+ headlines, 20px+ body, 16px+ labels. Put
font-variant-numeric: tabular-numson number columns.
3. Fill each scene — content, entrance, mid-scene activity
Put content inside the.scene-content wrapper; keep decoratives (glow, grain) outside it, directly in the scene div.
- Entrance: one
tl.from()per element, animating FROM offscreen/invisible TO the CSS position. Offset the first tween 0.1–0.3s into the scene (zero-delay entrances feel like jump cuts). - Mid-scene activity (this is what separates video from slides): every visible element must keep moving after its entrance. Use ≥2 patterns from the catalog below per scene. A still element on a still background is a JPEG with a progress bar.
- Vary eases — use ≥3 per scene:
power2.out(smooth),power4.out(snappy),back.out(1.6)(bouncy),expo.out(dramatic),sine.inOut(dreamy).
Hard ceiling 5s/scene unless you have a specific reason. When you change a
data-duration, update data-start on later scenes and the root’s data-duration so everything stays tiled.
4. Transitions — mostly hard cuts, at most one shader moment for a first draft
~95% of professional scene changes are hard cuts (no transition code — the new scene’s entrance does the work). Reserve a shader transition for a single hero reveal, energy shift, or CTA. A shader on every cut is like bolding every word. For a Send-to first draft, use at most ONE contiguous shader chain. The simplest is a single anchor pair — 2 scenes, 1 transition (e.g.["s4","s5"]), which is one boundary. A longer contiguous chain like s3→s4→s5 is also fine — 3 scenes, 2 transitions (two adjacent boundaries). A single HyperShader.init() takes one scenes array whose adjacent entries each get a transition, so a contiguous chain of N anchors has N-1 transitions (scenes.length === transitions.length + 1). Two disjoint shader moments (e.g. s2→s3 AND s6→s7) are not one contiguous chain and do NOT fit a single init — that needs care the importer/renderer won’t forgive if you get it wrong. Keep to one contiguous chain here; additional shader moments are better added later in the enhance step.
Shader names to pick from: 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. Match energy: calm → cross-warp-morph/light-leak; professional → cinematic-zoom/whip-pan/sdf-iris; aggressive → glitch/chromatic-split; ethereal → gravitational-lens/ripple-waves. Minimum transition duration 0.3s; sweet spot 0.5s. Transition time = scene_boundary - (duration / 2).
The two bugs that cause “invisible middle scenes” — you must handle both:
- Non-anchor scenes (not bracketing a shader) use
style="visibility:hidden;"and need explicittl.settoggles withautoAlpha(notvisibility):autoAlpha:1at the scene’s start,autoAlpha:0at its end. Scene 1 starts visible (no inline style) and only gets a hide at its end. - The first anchor scene in each shader group uses
style="opacity:0;"and needstl.set("#sN", { opacity: 1 }, <start-time>)— HyperShader does not auto-show it.
autoAlpha, not visibility: when a shader fires, HyperShader blanks all .scene elements to opacity:0. A visibility-only toggle leaves the scene visible but opacity:0 (invisible). autoAlpha sets both, overriding the reset. Anchor scenes get no autoAlpha toggles — HyperShader owns their opacity. The only timeline write allowed on an anchor container is the required first-anchor tl.set("#sN", { opacity: 1 }, <start>); do not otherwise tl.set/tl.to an anchor container. Invariant (per contiguous chain): scenes.length === transitions.length + 1.
Complete self-contained skeleton (copy, then fill)
A 1920×1080, 8s, 5-scene launch/stat reel — hard cuts with one shader at the outro reveal. Fonts inlined; runtime from CDN; timeline registered. This is a full, importable composition once you fill the@font-face base64, the :root identity, and the scene content.
Animation pattern catalog (copy-paste)
The last argument of everytl.to/tl.from is the ABSOLUTE start time on the timeline (in seconds). These snippets use a SCENE_START placeholder — replace it (and every selector) with your scene’s real data-start, and keep each tween’s full active span inside its scene window: start + total_duration ≤ SCENE_START + data-duration, where total_duration accounts for repeat, yoyo (each yoyo cycle doubles it), repeatDelay, and stagger spread. Do not paste the placeholder numbers literally.
Counter — animate a number up (see the skeleton’s c2/c3/c4):
<span class="char">, then:
stroke-dasharray="440" stroke-dashoffset="440" on the path, then:
background: linear-gradient(var(--accent),var(--accent)) no-repeat 0 85% / 0% 30%;, then:
Determinism & media rules (universal — violating these renders wrong or blank)
The cloud renderer seeks the timeline frame-by-frame. Non-deterministic or self-driven animation fails.
Note: this is the one place the download-ZIP guidance is reversed — for Send-to, base64
data: URIs for fonts/images are required, not banned.
Keep the original design as a reference
A composition is a rebuild of your design into a timed video, so it adapts the form (see Preserve substance; adapt form above), but it should never lose the brand’s substance. Preserve the original design as a separate reference (not as the composition that drives the video) so a later agent can pull back any distinctive content or styling the rebuild dropped despite that rule. This turns a first draft into a fixable one rather than a dead end.What “enhance” adds after import (in-flight — intent, not a stable contract)
Your export is silent and visual-only. Sending it to HyperFrames levels it up with things Claude Design can’t add:- Sound — background music and sound effects timed to the animation (the immediate win).
- Motion polish — easing, timing, transition refinement via a purpose-built motion-design agent.
- HeyGen media (later) — images/icons, and eventually avatars/voice.
Self-review checklist (run before “Send to HyperFrames”)
Import will accept it:- Exactly one live root with
data-composition-id="main", not inside a<template> - Numeric
data-width/data-height/data-durationon the root - Every scene has numeric
data-start+data-duration; scenes tile with no gaps -
window.__timelines["main"] = tlpresent and keyed to the composition id - Not a bundled/splash-loader artifact (no
<template>splash, no base64 asset manifest, nowindow.__resources)
- Every brand font inlined as a base64
@font-facesrc (no Google Fonts<link>) - Every image/logo inlined as a base64
data:URI — no external/relative URL - No
s3:/// presigned / private / expiring asset URLs - No injected beacon/loader cruft (e.g. Cloudflare
cdn-cgiscripts) - Original design preserved as a separate reference
- Every scene has
class="scene clip"+ a.scene-contentwrapper - Non-anchor scenes:
visibility:hidden+autoAlphaset/reset toggles; anchor scenes:opacity:0, first anchor explicitly shown -
scenes.length === transitions.length + 1; each shader boundary sits inside its window; no transition < 0.3s - No exit tweens except on the final scene
- No
Date.now(), unseededMath.random(),setInterval/Timeout,requestAnimationFrame,repeat: -1 - Timeline built synchronously; paused; framework owns playback
- Colors and fonts exact; brand substance preserved: real headline/copy, distinctive figures/stats/data points, product names, and signature visuals carried over verbatim (form adapted, never genericized or invented); no placeholders; ≥2 animation patterns + mid-scene activity per scene