diff --git a/docs/guides/claude-design-send-to-hyperframes.md b/docs/guides/claude-design-send-to-hyperframes.md new file mode 100644 index 0000000000..ba94a346e3 --- /dev/null +++ b/docs/guides/claude-design-send-to-hyperframes.md @@ -0,0 +1,426 @@ +# 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."** + +| | Download-ZIP flow | **Send to HyperFrames (this guide)** | +| --- | --- | --- | +| Wire format | multi-file ZIP (`index.html` + `fonts/` + README) | **one self-contained HTML file** | +| How it travels | user downloads the ZIP | **the importer fetches one HTML** | +| Assets | referenced from sibling files (`fonts/…`) | **resolvable from the one file: inline `data:` URIs (preferred) or a publicly-fetchable absolute URL** | +| Next step | a coding agent polishes locally, renders via CLI | **enhanced inside HyperFrames; rendered in the cloud** | + +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 + +1. **You (Claude Design)** — author a valid HyperFrames composition as a single self-contained HTML. +2. **Send to HyperFrames** — one click. The importer fetches your HTML, validates it, and creates a hosted HeyGen project. **Import is free.** +3. **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. +4. **Render** — the cloud pipeline produces the MP4. + +Your job is step 1: a composition that imports cleanly and is a strong on-brand starting point. + +--- + +## 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 ` + + +
+ +
+
+

Brand

+

Your tagline

+
+
+ + + + + + + + +
+
+
0
+
Metric three
+
+
+ + +
+
+

Closing line,

+

not another.

+
+
+
+ + + + +``` + +--- + +## Animation pattern catalog (copy-paste) + +**The last argument of every `tl.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`): +```js +var c = { v: 0 }; +tl.to(c, { v: 1900000000000, duration: 0.9, ease: "power2.out", + onUpdate: function () { document.getElementById("s3-stat").textContent = "$" + (c.v / 1e12).toFixed(1) + "T"; } }, /* SCENE_START */ 3.3); +``` + +**Character stagger** — wrap each char in ``, then: +```js +tl.from(".char", { y: 60, autoAlpha: 0, duration: 0.5, ease: "power3.out", stagger: { each: 0.12, from: "start" } }, /* SCENE_START + 0.1 */ 0.1); +``` + +**Bar-chart fill:** +```js +["#bar1", "#bar2", "#bar3", "#bar4"].forEach(function (sel, i) { + tl.from(sel, { scaleY: 0, transformOrigin: "bottom", duration: 0.6, ease: "expo.out" }, /* SCENE_START */ 3.3 + i * 0.15); +}); +``` + +**Breathing float (mid-scene activity)** — note the yoyo+repeat doubles the span (0.6 × 2 = 1.2s), so from 4.9 it ends at 6.1, inside s4's 4.8-6.4 window: +```js +tl.to("#s4-logo", { y: -5, duration: 0.6, ease: "sine.inOut", yoyo: true, repeat: 1 }, /* SCENE_START */ 4.9); +``` + +**SVG stroke draw** — `stroke-dasharray="440" stroke-dashoffset="440"` on the path, then: +```js +tl.to("#s2-line", { strokeDashoffset: 0, duration: 1.0, ease: "power2.out" }, /* SCENE_START */ 1.7); +``` + +**Highlight sweep** — `background: linear-gradient(var(--accent),var(--accent)) no-repeat 0 85% / 0% 30%;`, then: +```js +tl.to("#s5-headline", { backgroundSize: "100% 30%", duration: 0.6, ease: "power2.out" }, /* SCENE_START */ 6.6); +``` + +**CSS grain** (never SVG-filter grain — it taints html2canvas and breaks shaders in a cross-origin iframe): +```css +.grain { position:absolute; inset:0; pointer-events:none; z-index:50; opacity:0.18; mix-blend-mode:overlay; + background-image: radial-gradient(rgba(255,255,255,.08) 1px, transparent 1.2px), radial-gradient(rgba(0,0,0,.18) 1px, transparent 1.2px); + background-size: 3px 3px, 5px 5px; background-position: 0 0, 1px 2px; } +``` + +--- + +## 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. + +| Never | Use instead | +| --- | --- | +| `Math.random()` | seeded PRNG (only if truly needed) | +| `Date.now()`, `performance.now()` | hard-coded timing or `tl.time()` in `onUpdate` | +| `setInterval`, `setTimeout` | timeline tweens + `onUpdate` | +| `requestAnimationFrame` | GSAP tweens | +| `repeat: -1` | `repeat: Math.ceil(duration / cycle) - 1` | +| `stagger: { from: "random" }` | `from: "start"`, `"center"`, or `"end"` | +| async timeline construction | build synchronously at page load | +| `video.play()` / `audio.play()` | the framework owns playback | +| `