| name | vid-product-launch |
| description | Generates a cinematic product launch video (MP4) from a product description and launch context. 5-section narrative arc — Tease, Build, Reveal, Proof, CTA — rendered as HTML/CSS in headless Chromium via Playwright, assembled with FFmpeg. 4 tone presets. 30/60/90 second durations. Trigger when user says "product launch video", "launch video", "product reveal video", "cinematic product video", "product announcement video", or "launch day video". |
| compatibility | ["claude-code","gemini-cli","github-copilot"] |
| author | OpenDirectory |
| version | 1.2.0 |
vid-product-launch
Generates a narrative MP4 product launch video from a product description and launch context.
Pipeline: HTML/CSS animations → headless Chromium (Playwright, frame-by-frame) → FFmpeg (H.264 MP4).
No React. No AI video APIs. No Python. Zero runtime cost beyond Playwright + FFmpeg.
Critical Rules (read before every generation)
-
The tagline is not optional. Do not skip it. It is the product's entire promise in 4–6 words. If the user did not provide it, derive one from the description — a sharp, active-voice distillation. Never write "[tagline here]" or leave it blank.
-
The reveal moment must feel earned. The tease and build sections exist to create tension. If the product name appears in the first 20% of the video, the narrative collapses. Never reveal the product name before the Reveal section.
-
product_name font size: minimum 120px for 16:9, minimum 80px for 9:16. The name must be the largest element at the reveal moment. If it isn't dominant, the reveal fails.
-
One proof stat. Not a list. If the user provides multiple stats, pick the strongest one. A list of 5 numbers destroys the punch. One oversized number creates it.
-
Use window.renderFrame(t) — no CSS @keyframes for scene transitions. CSS currentTime seeking is silently ignored for backward seeks in Chromium. The renderFrame function computes element styles directly from milliseconds. Playwright calls it once per frame.
-
No animation-delay on ANY element. Not needed with renderFrame. If you write animation-delay, stop — you are using the wrong architecture.
-
window.__videoReady = true only inside document.fonts.ready.then(...). Never set synchronously.
-
Expose window.__stopPreview(). The rAF preview loop races with Playwright's evaluate/screenshot calls. The capture script calls __stopPreview() before the frame loop.
-
Use t < startMs (not t <= startMs) in scene boundary checks. t <= 0 at frame 0 makes scene 1 black.
-
Body = exact pixel dimensions. Width and height are integers. No %, vw/vh, rem.
-
Read references/scene-library.md AND references/style-presets.md before generating ANY HTML.
-
Never dump HTML in chat. Save to file. Show summary only.
-
Film grain canvas MUST be 240×135, not W×H. Set width="240" height="135" on the canvas element, then stretch with style="width:[W]px;height:[H]px". Full-resolution grain at 1920×1080 is 64× slower — 8MB of ImageData per frame — and will make 1800-frame exports take hours.
-
The tease section MUST be dark (#000 or near-black), regardless of tone. White backgrounds in the tease section read as demo slides, not product launch videos. The dark-to-light narrative arc (dark tease → dark build → product reveal) is how launch videos create drama. Even the minimal and energetic presets should use background: #000 for tease-problem and tension-build scenes.
-
Each tease word must be its own beat at 200px+ font size. Word-by-word on a single line is not punchy enough. Each problem word (e.g. "Research." / "Write." / "Outreach." / "Repeat.") gets 1500–1800ms of screen time at font-size: 200px; font-weight: 900 centered, one at a time. See scene-library.md tease-words pattern.
-
Build section must show content that narrates the problem — not just particles. Use the terminal-card pattern: 3 cards appear sequentially with a typewriter animation showing the manual work being done. Cards have border: 1px solid rgba(255,255,255,0.07), blue dot accent, monospace text. Particles alone for 20 seconds is empty screen time.
-
Dot-grid CSS background on dark scenes. Add background-image: radial-gradient(circle, rgba(255,255,255,0.035) 1px, transparent 1px); background-size: 60px 60px; to build, reveal, proof, and CTA scenes. It costs zero compute and adds depth.
Step 1: Intake
Required:
product_name — the name of the product or feature being launched
product_description — 2–3 sentences: what it does, who it's for, key benefit
Optional parameters and defaults:
| Parameter | Default | Description |
|---|
| tagline | auto | 4–6 words — the product's core promise |
| problem_statement | auto-inferred | 1 sentence for tease section (the pain the product solves) |
| proof_stat | auto-inferred | Single metric (e.g. "500+ teams", "10× faster", "$2M saved") |
| cta | auto | URL or action phrase (e.g. "opendirectory.dev", "Join the waitlist") |
| launch_date | — | ISO date string (enables countdown-card scene) |
| tone | cinematic | cinematic / energetic / minimal / emotional |
| duration | 60 | 30 / 60 / 90 (seconds) |
| aspect_ratio | 16:9 | 16:9 (1920×1080) / 9:16 (1080×1920) |
| letterbox | false | Cinematic 2.35:1 black bars — 16:9 only |
| music | — | Path to audio file (mp3/m4a/wav) |
| fps | 30 | Frames per second (24, 30, or 60) |
If product_name or product_description is missing, ask exactly:
"To create the launch video, I need two things:
- Product name — what is the product called?
- Product description — 2–3 sentences: what does it do, who is it for, what is the key benefit?
Optional: tagline (4–6 words), proof stat (one number), CTA (URL or action phrase), tone (cinematic / energetic / minimal / emotional), duration (30 / 60 / 90s)."
If both are present → proceed to Step 2 immediately.
Step 2: Internal Architecture (never shown to user)
1. Derive missing params:
- tagline (if not provided): Write one from product_description. 4–6 words. Active voice. No filler ("The future of…", "Introducing…"). Derive the sharpest possible promise.
- problem_statement (if not provided): Infer from product_description. 1 sentence stating the pain. Never mentions the product name. Written to make the audience nod in recognition.
- proof_stat (if not provided): Infer from product_description. If no stat is available, use a credible scale indicator ("Trusted by 500+ teams", "From days to minutes").
- cta (if not provided): Use product domain if inferable from product_name, or "Learn more" as action phrase.
2. Calculate section timing from duration:
| Section | 30s | 60s | 90s | Start formula |
|---|
| Tease | 0–5s | 0–10s | 0–15s | 0ms |
| Build | 5–12s | 10–30s | 15–40s | Tease end |
| Reveal | 12–20s | 30–45s | 40–60s | Build end |
| Proof | 20–25s | 45–55s | 60–75s | Reveal end |
| CTA | 25–30s | 55–60s | 75–90s | Proof end |
Convert every boundary to milliseconds. Assign to constants:
TEASE_START_MS, TEASE_END_MS
BUILD_START_MS, BUILD_END_MS
REVEAL_START_MS, REVEAL_END_MS
PROOF_START_MS, PROOF_END_MS
CTA_START_MS, CTA_END_MS
3. Select scenes per section:
- Tease: Always starts with
blackout-opener (1500ms). Then tease-words (each problem keyword as its own 1600–1800ms beat at 200px+). If launch_date provided, optionally add countdown-card at end of tease.
- Build:
terminal-card sequence (3 cards showing manual tasks being typed in) + optional closing accent line. Duration ≥ 30s gets all 3 cards. Duration = 30s uses 2 cards. Never use bare particles-only for more than 5s — the screen must show content that narrates the problem.
- Reveal:
reveal-hero — always first in this section. Optionally followed by tagline-card if duration ≥ 60s.
- Proof:
proof-stat if a numeric stat is available. Otherwise feature-bullet.
- CTA:
cta-card — always, always last.
Typical scene counts: 30s = 4 scenes, 60s = 6–7 scenes, 90s = 7–8 scenes.
4. Calculate per-scene timing:
Within each section, divide time equally across scenes in that section.
Exception: blackout-opener always gets exactly the first 1500ms of Tease.
5. Determine pixel dimensions:
16:9 → W=1920, H=1080
9:16 → W=1080, H=1920
6. Letterbox calculation (if enabled, 16:9 only):
- 2.35:1 content height = W / 2.35 = 1920 / 2.35 ≈ 817px
- Bar height = (H - 817) / 2 = (1080 - 817) / 2 ≈ 132px
- Top bar:
top: 0; height: 132px
- Bottom bar:
bottom: 0; height: 132px
7. Cinematic effects flags (by preset):
cinematic: film-grain=ON (canvas 240×135, opacity 0.025), vignette=ON, light-leak=ON (warm gold), dot-grid=ON on dark scenes
energetic: film-grain=OFF, vignette=OFF, dot-grid=ON, white flash at reveal
minimal: film-grain=ON (canvas 240×135, opacity 0.022), vignette=ON (subtle, 0.5 radial), dot-grid=ON, no light-leak — accent color = #4B9FFF (electric blue)
emotional: film-grain=ON (canvas 240×135, opacity 0.018, warm tint blend), vignette=ON (soft), light-leak=ON (copper warm), dot-grid=OFF
All presets: tease and build scenes ALWAYS use dark background (#000 or near-black), regardless of preset.
8. Embed window.__sfxTimeline in the HTML (always — even when no music is provided):
The export script reads this array, synthesizes each SFX type natively with FFmpeg aevalsrc/anoisesrc, and places events at exact millisecond offsets using adelay. No external audio files required.
SFX type reference:
| Type | Sound | Duration | Notes |
|---|
word-hit | Sub punch (50Hz) + transient click (2.2kHz) + noise burst | 180ms | One per tease word — 3 layers via amix |
type-sequence | Mechanical keyboard clicks at 15Hz (sin^30 pulse envelope) | 1.9s | One per card — runs for full TYPE_DUR |
whoosh | Two-band noise sweep (1.1kHz body + 4–8kHz air) | 700ms | At BUILD_CLOSE transition |
tension-riser | Low rumble (250Hz) + sub tone growing with t/2.8 ramp | 2.9s | Start at BUILD_CLOSE + 700ms — peaks at REVEAL_START |
reveal-boom | Sub (45Hz) + body (90Hz) + shimmer (4.5–11kHz) + 85ms echo | 900ms | Exactly at REVEAL_START — THE hit |
counter-tick | Harmonic click (880Hz + 440Hz + 1760Hz + 2640Hz) | 80ms | 3 beats — decrescendo 0.32 → 0.26 → 0.20 |
cta-chime | A major chord (440 + 554 + 659 + 880Hz) + aecho bell shimmer | 1.2s | Exactly at CTA_START |
SFX timing map — compute ms values from section constants, never hardcode:
window.__sfxTimeline = [
{ ms: WORD_BEATS[0].start, sfx: 'word-hit', vol: 0.55 },
{ ms: WORD_BEATS[1].start, sfx: 'word-hit', vol: 0.55 },
{ ms: WORD_BEATS[2].start, sfx: 'word-hit', vol: 0.55 },
{ ms: WORD_BEATS[N].start, sfx: 'word-hit', vol: 0.65 },
{ ms: CARDS[0].start + 180, sfx: 'type-sequence', vol: 0.28 },
{ ms: CARDS[1].start + 180, sfx: 'type-sequence', vol: },
{ : []. + , : , : },
{ : , : , : },
{ : + , : , : },
{ : , : , : },
{ : , : , : },
{ : + , : , : },
{ : + , : , : },
{ : , : , : },
];
Place this block immediately before the preview loop, after window.renderFrame.
Step 3: HTML Generation
Read references/scene-library.md AND references/style-presets.md before writing any code.
Use the exact CSS class names, HTML structure, and renderFrame patterns from those files.
Required HTML skeleton:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
[font CDN link from style preset]
<style>
:root {
[all CSS tokens from style preset]
}
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: [W]px; height: [H]px;
overflow: hidden;
background: var(--bg);
font-family: var(--font-body);
position: relative;
}
.scene {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80px;
opacity: 0;
will-change: opacity, transform;
}
.scene-inner {
width: 100%;
max-width: 1200px;
}
[paste CSS from each scene type in scene-library.md]
[if film-grain: ON]
[end if]
[if vignette: ON]
[end if]
[if light-leak: ON]
[end if]
[if letterbox: ON]
[end if]
[scene HTML from scene-library.md templates, one per selected scene]
Design quality rules:
product_name font size: ≥120px for 16:9, ≥80px for 9:16. Non-negotiable.
- Headline letter-spacing:
var(--tracking-tight) — always.
- One accent color highlight per scene.
transform-origin: center center on every element that uses transform.
- Padding inside
.scene: minimum 80px — text must never touch viewport edges.
- Cinematic preset: all colors from
-- token variables. No free hex except on canvas fillStyle (replace [ACCENT_HEX_FROM_PRESET] with literal hex from preset).
- Never write placeholder text ("Your tagline here", "TBD", "[INSERT STAT]").
Step 4: Self-QA (fix every failure before Step 5)
Narrative structure:
renderFrame correctness:
Readiness signal:
Layout:
Cinematic effects:
Design:
Sound effects:
Step 5: Export
Determine slug from product_name (kebab-case, ≤30 chars):
mkdir -p launch/[slug]
Save HTML: launch/[slug]/product-launch.html
Open for browser preview:
open launch/[slug]/product-launch.html
Run export (replace [skill-root] with path to this skill's directory):
bash [skill-root]/scripts/export-video.sh \
launch/[slug]/product-launch.html \
launch/[slug]/product-launch.mp4 \
--duration [total_duration] \
--fps [fps] \
--width [W] \
--height [H] \
[--music path/to/audio.mp3]
First run installs Playwright (~200MB Chromium, cached after first use) and verifies FFmpeg.
Step 6: Output Summary
## Launch Video: [product_name]
Date: [YYYY-MM-DD] | Tone: [tone] | Duration: [N]s | Aspect: [ratio]
Sections: Tease [0–Ns] → Build [N–Ns] → Reveal [N–Ns] → Proof [N–Ns] → CTA [N–Ns]
Narrative
Tease: [problem_statement — 1 sentence]
Reveal: [product_name] — "[tagline]"
Proof: [proof_stat]
CTA: [cta]
Files
Source: launch/[slug]/product-launch.html
Output: launch/[slug]/product-launch.mp4
Checklist
- [ ] Product name does not appear before reveal section
- [ ] Reveal moment feels distinct — flash/materialise visible
- [ ] Tagline is present and legible
- [ ] Proof stat counter animates
- [ ] CTA URL readable at final frame
- [ ] No blank frames
Prompt Tips (show when user asks for guidance)
"Write the tagline yourself. It is the most important 4–6 words in the video — don't AI-generate it lazily."
"The reveal moment is everything. Everything before it builds tension; the reveal must feel earned."
"One benefit in the proof section. Trying to show 5 features kills launch video pacing."
"Match tone to your market: cinematic for B2C premium / Series A+, energetic for dev tools and SaaS, minimal for design-forward products, emotional for consumer / mission-driven."
Good: "Product launch video, 60 seconds. Product: OpenDirectory. Description: A library of pre-built AI agent skills for Claude, Codex, and Gemini. Covers GTM, content, research, and developer tools. Tagline: 'AI skills, ready to install.' Tone: minimal. Proof: '52+ skills, zero setup.' CTA: 'Browse skills at opendirectory.dev.' Aspect: 16:9."
❌ Bad: "launch video for our new product"