--- name: vid-motion-graphics description: Generates motion graphics videos (MP4) from a content brief. Multi-scene HTML/CSS animations rendered frame-by-frame in headless Chromium via Playwright, assembled with FFmpeg. 1080×1080 default, 16:9 (1920×1080) and 9:16 (1080×1920) supported. 5 style presets. Trigger when user says "create a video", "motion graphic", "animated video", "make a reel", "create an explainer", "animated infographic", or "short video". compatibility: [claude-code, gemini-cli, github-copilot] author: OpenDirectory version: 1.0.0 --- # vid-motion-graphics Generates multi-scene motion graphics as MP4. Renders HTML/CSS animations in headless Chromium via Playwright (Web Animations API frame-seeking), assembles PNG frames with FFmpeg. No React, no AI APIs, no Python — zero new dependencies beyond the graphic-gif family. CDN fonts only. No external libraries in HTML. --- ## Critical Rules (read before every generation) 1. **Use `window.renderFrame(t)` — no CSS `@keyframes` for scene transitions.** CSS animation `currentTime` seeking is silently ignored for backward seeks in Chromium. The renderFrame approach: a pure JS function computes `opacity`/`transform` directly from milliseconds. Playwright calls it once per frame. Deterministic, race-free. 2. **No `animation-delay` on ANY element.** Not needed with renderFrame. If you catch yourself writing `animation-delay`, stop — you're using the wrong architecture. 3. **`window.__videoReady = true` only inside `document.fonts.ready.then(...)`.** Never set synchronously — fonts must load before Playwright captures frame 1 or text renders with fallback fonts. 4. **Expose `window.__stopPreview()`.** The browser's rAF preview loop races with Playwright's evaluate/screenshot calls. `capture-frames.mjs` calls `__stopPreview()` before the frame loop. Always include it. 5. **Use `t < startMs` (not `<=`) in scene boundary checks.** `t <= 0` at frame 0 makes scene 1 black. The correct guard is `if (t < startMs || t >= endMs) return hidden`. 6. **Body = exact pixel dimensions.** Width and height are integers (`1080px`, `1920px`). No `%`, `vw/vh`, or responsive units. 7. **No two scenes visible simultaneously** (except 10% enter overlap). All scenes `opacity: 0` outside their renderFrame window. 8. **Transitions use `opacity` only.** No `display` toggle, no `visibility` — GPU-composited opacity is frame-perfect. 9. **Never dump HTML in chat.** Save to file, show summary only. 10. **Title states the message, not the topic.** "3 Reasons Q4 Crushed Targets" not "Q4 2024 Performance Video". 11. **Read `references/scene-library.md` before generating ANY HTML.** Use exact HTML structure and CSS class names from that file. --- ## Step 1: Intake **Required:** `content_brief` **Optional parameters and defaults:** | Parameter | Default | Description | |---|---|---| | content_brief | — | Text describing what the video communicates (required) | | scenes | auto | Number of scenes (1–6). Auto = derived from brief. | | duration_per_scene | 3s | Duration per scene in seconds (1–8s) | | style | kinetic-dark | kinetic-dark / editorial-light / data-pulse / bold-type / minimal-clean | | aspect_ratio | 1:1 | 1:1 (1080×1080) / 16:9 (1920×1080) / 9:16 (1080×1920) | | fps | 30 | Frames per second (24, 30, or 60) | | music | none | Path to audio file for background track (mp3/m4a/wav) | | source | — | Source attribution shown in final frame footer | **If `content_brief` is missing, ask exactly:** > "To create the video, I need a content brief — what should the video communicate? > > Example: 'Show 3 reasons why Q4 revenue grew 85%: new enterprise deals, reduced churn, price increase. Use bold numbers. Style: data-pulse.' > > Optional: style (default: kinetic-dark), aspect ratio (default: 1:1), seconds per scene (default: 3s)" If `content_brief` is present → proceed to Step 2 immediately. --- ## Step 2: Internal Architecture (never shown to user) **1. Parse brief into scenes (max 6):** - Scene 1 = hook or title (always) - Scenes 2–N-1 = supporting points, metrics, or story beats - Scene N = CTA or closing summary (always, if more than 1 scene) - One key idea per scene — if brief has 7+ ideas, consolidate the weakest ones **2. Read `references/scene-library.md`** — choose scene type for each scene: - Hook/opening → `title-card` - Single metric → `stat-reveal` - List of 2–4 points → `bullet-list` - Before vs after / two values → `split-screen` - Testimonial / quote → `quote-card` - Final / CTA → `cta-card` **3. Read `references/style-presets.md`** — load CSS tokens + animation personality for chosen style. **4. Calculate timing:** ``` totalDuration = sceneCount × duration_per_scene (seconds) totalFrames = totalDuration × fps ``` Each scene occupies `(100 / sceneCount)%` of the total `@keyframes` range. | Scene | Start % | End % | |---|---|---| | 1 | 0% | (100/N)% | | 2 | (100/N)% | (200/N)% | | … | … | … | | N | ((N-1)×100/N)% | 100% | Within each scene's range: - Enter: first 10% of scene range - Hold: 10% to 85% of scene range - Exit: 85% to 100% of scene range **5. Determine pixel dimensions:** - `1:1` → W=1080, H=1080 - `16:9` → W=1920, H=1080 - `9:16` → W=1080, H=1920 --- ## Step 3: HTML Generation Read `references/scene-library.md` AND `references/style-presets.md` before writing any code. **Required HTML structure:** ```html
[font CDN link from style preset]