--- name: scimotion-video description: | Create animated explainer/science videos from script to final MP4 using HTML+SVG+GSAP animation rendered via Puppeteer. No image generation APIs — all visuals are code-driven (SVG line art, CSS, GSAP timelines). Use when: making explainer videos, science videos, animated narration videos, 科普视频, converting a script/topic into a video, or any "text-to-video" task using code-based animation. Covers: TTS generation (Azure Speech), animation HTML authoring, frame-accurate rendering, and audio+video muxing. --- # SciMotion Video Skill Produce narrated animated videos entirely from code. No image generation — every visual is SVG + CSS + GSAP. ## Workflow ``` 1. Script (JSON/Markdown) 2. TTS → Audio per scene + manifest.json (durations & start times) 3. Animation HTML (single self-contained file, GSAP master timeline) 4. Render → Puppeteer seek-per-frame → ffmpeg encode → MP4 ``` **Core rule: TTS drives timing.** Generate audio first; derive all animation cue times from audio durations. ## Step 1 — Script Accept a topic or script. Structure it into scenes with `id`, narration `text`, and `visual` description. See `references/script-format.md` for the JSON schema and markdown format. Each scene: 7–20s narration, one clear visual concept. ## Step 2 — TTS Generation Run the bundled script: ```bash AZURE_SPEECH_KEY= node scripts/generate-tts.js script.json audio/ ``` Output: `audio/scene01.mp3 ... sceneNN.mp3` + `audio/manifest.json` with per-scene `duration` and `startTime`. Default voice config (override via script.json): - Voice: `zh-CN-YunjianNeural` - Style: `narration-relaxed`, degree 1.70 - Rate: +15%, Volume: +50% - Format: MP3 48kHz 192kbps ⚠️ `pitch="0st"` causes Azure 400 error — omit pitch when zero. ## Step 3 — Animation HTML Build a single HTML file (1920×1080) containing: 1. **All scenes** as `
` containers (absolute positioned, opacity 0) 2. **Inline SVG** for all visual elements (sketch/line-art style, dark background `#0a0a1a`) 3. **GSAP master timeline** driving all animations with absolute time positions 4. **Subtitle bar** synced to narration cues 5. **Audio** elements (base64-embedded or external src) 6. **Play hint** overlay (space key triggers playback) Key implementation details: - Read `manifest.json` to get `DUR[]` and `STARTS[]` arrays - Each scene: fade in at `STARTS[i]`, animate during `DUR[i]`, fade out before `STARTS[i+1]` - Gap between scenes: 0.6s (configurable) - Subtitle cues: estimate from text length, or align manually to audio See `references/animation-patterns.md` for reusable code patterns: template HTML, draw-on SVG, bounce-in, slide-in, counter, stickman SVG, subtitle helper, playback controller. ### Visual Style Guidelines - Dark background: `#0a0a1a` - Line art / sketch style: `stroke="#ddd"`, `stroke-width="2-3"`, `fill="none"` - Accent colors: gold `#eab308`, green `#22c55e`, red `#ef4444`, blue `#3b82f6` - Simple geometric shapes, no complex illustrations - Draw-on animation for SVG paths (`stroke-dasharray` + `stroke-dashoffset`) ## Step 4 — Render to MP4 ```bash node scripts/render-video.js animation.html audio/manifest.json output.mp4 [--fps 30] [--crf 18] ``` Requires: `puppeteer` (npm), `ffmpeg`, `ffprobe`. The renderer: 1. Opens HTML in headless Chrome (1920×1080) 2. Hides play hint, mutes audio, pauses GSAP timeline 3. For each frame: `master.seek(t)` → screenshot PNG 4. Merges scene audio files at correct timestamps via ffmpeg `adelay`+`amix` 5. Encodes frames + audio → H.264 MP4 Performance: ~100ms/frame → ~7 min for a 2.5-min video at 30fps. ## Quick Start Example ```bash # 1. Write script cat > script.json << 'EOF' { "title": "沉没成本", "scenes": [ {"id": "scene01", "text": "你花80块买了张电影票..."}, {"id": "scene02", "text": "大多数人会说:都花钱了,看完吧..."} ] } EOF # 2. Generate TTS AZURE_SPEECH_KEY=$KEY node scripts/generate-tts.js script.json audio/ # 3. Build animation HTML (this is the creative step — agent writes the HTML) # Use manifest.json durations to set GSAP timeline cues # 4. Render npm install puppeteer # first time only node scripts/render-video.js animation.html audio/manifest.json output.mp4 ``` ## File Structure ``` project/ ├── script.json # Scene definitions ├── audio/ │ ├── manifest.json # TTS manifest (durations, start times) │ ├── scene01.mp3 │ └── ... ├── animation.html # Self-contained animation (or with audio src refs) └── output.mp4 # Final video ```