diff --git a/.gitignore b/.gitignore index 24a7d2c79a..13c0d0c239 100644 --- a/.gitignore +++ b/.gitignore @@ -95,6 +95,7 @@ captures/ cursor-tests/ basecamp-video/ launch-video*/ +!skills/launch-video/ ab-test/ compositions/ video-6-2-patched/ diff --git a/skills/launch-video/SKILL.md b/skills/launch-video/SKILL.md new file mode 100644 index 0000000000..74177055fe --- /dev/null +++ b/skills/launch-video/SKILL.md @@ -0,0 +1,271 @@ +--- +name: launch-video +description: Build a high-energy launch/promo video using the billboard-per-beat pattern — one focus element per frame, fast cuts, distinct visual worlds per beat. Use when the user wants a polished launch video, product demo, trailer, or promo clip. +--- + +# Launch Video — Billboard-Per-Beat Pattern + +Every frame is a billboard. One thing, all the attention, gone before you can get bored. Speed creates energy. The cut IS the transition. + +## Core Principles + +1. **One focus per beat.** A frame is not a webpage. You cannot read 8 things at once in video. Each beat shows ONE visual or ONE statement. Nothing else. + +2. **Each beat gets its own visual world.** Different background, different color, different energy. Warm cream → dark terminal → pink accent → full-bleed image. No two consecutive beats should look alike. + +3. **Speed creates urgency.** Most beats last 0.7–1.8 seconds. Category/keyword beats fire at 0.7s. Statements get 1.2–1.8s. Hold the close for 2–4s. + +4. **Full-bleed images, not thumbnails.** One product screenshot filling the entire 1920×1080 frame says "this is real, this is quality" more than a grid of small cards ever will. + +5. **The cut IS the transition.** No zooms, no blurs, no spatial travel between beats. `tl.set(el, { opacity: 1 })` on the new beat, `tl.set(prev, { opacity: 0 })` on the old. The contrast between beats creates the energy. + +6. **Scale pulse on entry.** Every beat snaps in at 1.012× scale and eases to 1.0 in 0.25s. Invisible consciously, felt subconsciously. + +7. **Motion is mandatory.** Subtle reads as static at 30fps. Every beat should have at least one moving element — a word rising into place, a gradient shifting, a scale settling. + +## Architecture + +### Stacked Beats + +All beats are full-frame divs stacked at position (0,0). GSAP controls visibility via opacity. + +```html +
+
+
The Product
+
+
+
is here.
+
+
+ +
+ +
+``` + +```css +.beat { + position: absolute; + inset: 0; + width: 1920px; + height: 1080px; + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + overflow: hidden; +} +``` + +### Beat Sequencing + +```javascript +var beats = [ + { id: "b01", at: 0, dur: 1.8 }, // Statement + { id: "b02", at: 1.8, dur: 1.0 }, // Emphasis + { id: "b03", at: 2.8, dur: 0.9 }, // Full-bleed image + // ... +]; + +beats.forEach(function (b) { + var el = document.getElementById(b.id); + tl.set(el, { opacity: 1 }, b.at); + gsap.set(el, { scale: 1.012 }); + tl.to(el, { scale: 1, duration: 0.25, ease: "power2.out" }, b.at); + tl.set(el, { opacity: 0 }, b.at + b.dur); +}); +``` + +## Beat Types + +### Statement Beat (1.2–1.8s) + +One sentence or phrase, giant serif/sans text, centered. + +```css +.mega-text { + font-size: 140px; + letter-spacing: -0.04em; +} +``` + +### Image Beat (0.8–1.2s) + +Full-bleed product screenshot or rendered block. No text overlay. The visual speaks. + +### Number Beat (1.5–2.0s) + +Giant statistic. "52" at 280px with a small label beneath. + +### Step Beat (1.0–1.2s) + +Numbered step. Giant monospace number + one-line description. One step per beat — NOT all steps on one frame. + +### Category Beat (0.6–0.8s) + +Single word, giant weight, accent color, tinted background. Fire rapidly. + +### Command Beat (1.5–2.0s) + +Terminal command in monospace. Dark background. + +``` +$ npx hyperframes add liquid-glass +``` + +### CTA Beat (3–4s, hold to end) + +Logo, URL, or call-to-action. Sits on screen for the viewer to absorb. + +## Visual Palette + +Each beat should feel like a different world: + +- **Warm editorial**: `radial-gradient(ellipse at 50% 45%, #fcf6e7, #f0e8d0)` + Instrument Serif +- **Dark terminal**: `radial-gradient(ellipse at 50% 40%, #14131f, #0c0c14)` + JetBrains Mono +- **Accent tints**: `radial-gradient(ellipse at 50% 50%, rgba(color, 0.08), #fdf8ee 70%)` + Inter Black +- **Full-bleed images**: product screenshots, no background needed + +### Typography + +- **Instrument Serif** — editorial headlines, thesis statements +- **Inter** — UI text, bold statements, categories (use weight 700-900) +- **JetBrains Mono** — terminal commands, step numbers, code + +### Special Effects + +- **Gradient shimmer** on key words: `background-clip: text` with sweeping `background-position` +- **Staggered check reveals**: each line slides in 0.15s apart +- **Scale pulse**: 1.012 → 1.0 on every beat entry + +## Workflow + +### 1. Plan the Beats + +Write a beat sheet before touching HTML. One line per beat: + +``` +B01 1.8s dark "The HyperFrames Registry" +B02 1.0s warm "is open." (italic, indigo) +B03 0.9s image liquid-glass full-bleed +B04 0.9s image x-post full-bleed +... +``` + +### 2. Build the HTML + +One `
` per beat. Keep each beat dead simple — one element. + +### 3. Sequence with GSAP + +Use the beats array pattern. Set opacity in/out, add scale pulse. + +### 4. Generate Contact Sheet + +Render, then extract frames at 3fps and montage into a review grid: + +```bash +ffmpeg -i output.mp4 -vf "fps=3,scale=480:270" frame_%04d.png +montage frame_*.png -tile 10x -geometry 480x270+4+4 -background '#0a0a0a' contact-sheet.png +``` + +### 5. Review and Iterate + +Check the contact sheet for: + +- Any beat with more than one focus element (split it) +- Consecutive beats with similar backgrounds (change one) +- Beats that feel too long (shorten) or too short to read (lengthen) +- Dead frames (blank during transitions) + +## Easing — The Motion Designer's Toolkit + +Generic `power2.out` on everything is the hallmark of AI-generated video. Pro motion designers choose easing per intent. + +### When to Cut vs When to Ease + +- **Hard cuts** for impact moments: image reveals, category rapid-fire, scene changes. No easing — `tl.set()`. +- **Tuned easing** for build-up sequences: numbered steps accumulating, checkmarks appearing, words assembling a sentence. The motion tells the story. + +### Easing Vocabulary + +| Intent | GSAP Ease | Feel | +| ----------------- | ------------------------ | ---------------------------------------------------------------- | +| Snap (iOS-like) | `power4.out` | Fast start, crisp stop. Hero text landing. | +| Whip overshoot | `back.out(1.7)` | Overshoots target, settles. Numbers, badges. | +| Soft land | `expo.out` | Very fast start, long gentle tail. Per-word text reveals. | +| Mechanical | `power1.out` or `"none"` | Linear feel. Terminal text, code typing. | +| Bounce settle | `elastic.out(1, 0.5)` | Spring bounce. Stats, counters, CTA pills. | +| Dramatic entrance | `expo.inOut` | Slow start, explosive middle, soft land. Full-screen statements. | +| Subtle drift | `"none"` | Constant speed. Background parallax, camera drift. | + +### Applying Per Beat Type + +```javascript +// Statement beats — dramatic entrance +tl.from(text, { y: 60, opacity: 0, duration: 0.5, ease: "expo.out" }, t); + +// Number beats — whip overshoot for impact +tl.from(num, { scale: 0.5, opacity: 0, duration: 0.4, ease: "back.out(1.7)" }, t); + +// Step beats — snap in from left, stagger builds tension +tl.from(step, { x: -40, opacity: 0, duration: 0.3, ease: "power4.out" }, t + i * 0.12); + +// Check beats — mechanical typewriter feel +tl.from(check, { x: -15, opacity: 0, duration: 0.15, ease: "power1.out" }, t + i * 0.2); + +// Category rapid-fire — no easing, hard set for speed +tl.set(cat, { opacity: 1, scale: 1.012 }, t); + +// CTA pill — bounce settle, last thing viewer sees +tl.from(pill, { scale: 0.8, opacity: 0, duration: 0.5, ease: "elastic.out(1, 0.5)" }, t); +``` + +### Scale Pulse Variations + +Not every beat gets the same pulse. Match the scale to the energy: + +```javascript +// Impact beats: larger pulse (1.03) +gsap.set(el, { scale: 1.03 }); +tl.to(el, { scale: 1, duration: 0.2, ease: "power4.out" }, t); + +// Statement beats: standard pulse (1.012) +gsap.set(el, { scale: 1.012 }); +tl.to(el, { scale: 1, duration: 0.25, ease: "power2.out" }, t); + +// Quiet beats: no pulse, gentle fade +tl.from(el, { opacity: 0, duration: 0.3, ease: "power1.out" }, t); +``` + +## Anti-Patterns + +| Mistake | Fix | +| ----------------------------------- | ------------------------------------ | +| Grid of cards | One card per beat, full-bleed | +| Multi-step layout | One step per beat | +| Slow camera pan between scenes | Instant cut with scale pulse | +| Blur/zoom transitions eating frames | Every frame is content | +| Same font/color across beats | Each beat gets its own world | +| Dense info layout | If you can't read it in 1s, split it | + +## Checklist + +- [ ] Beat sheet written before HTML +- [ ] One focus element per beat — no exceptions +- [ ] No two consecutive beats share the same background +- [ ] Full-bleed images for product shots (not thumbnails) +- [ ] Scale pulse (1.012→1.0) on every beat entry +- [ ] Contact sheet generated and reviewed +- [ ] No dead/blank frames between beats +- [ ] CTA beat holds for 3+ seconds at the end +- [ ] Deterministic — no Math.random(), no Date.now() +- [ ] Paused timeline registered to `window.__timelines`