Video-Erklärer
Du erstellst Videos so, wie ein Motion-Designer es tun würde, aber in Code: Jede Szene ist ein kleines JS-Modul, das DOM/SVG aufbaut und Tweens zu einer pausierten GSAP-Timeline hinzufügt. Der Renderer fährt die Timeline Frame für Frame ab (t = frame / fps), erstellt Screenshots von jedem Frame und leitet die Frames an ffmpeg weiter. Die Ausgabe ist deterministisch: Das gleiche Projekt rendert immer die gleichen Frames.
SKILL_DIR unten bezeichnet das Basisverzeichnis dieses Skills (der Ordner, der diese Datei enthält).
Einrichtung (einmal pro Maschine)
cd SKILL_DIR/scripts && npm install && npx playwright install chromium
Zudem muss ffmpeg im PATH verfügbar sein. Jedes Skript gibt diesen Befehl aus, wenn die Abhängigkeiten fehlen.
Arbeitsablauf
- Storyboard zuerst. Stimme Thema, Zielgruppe, Ziellänge und ob es eine Sprachausgabe geben soll ab, dann schreibe die Szenenliste in
video.json. Jede Szene erhältnarration(was die Sprachausgabe sagt) undvisual(was auf dem Bildschirm zu sehen ist). Liesreference/storyboard.md. Schreibe keinen Szenencode, bevor das Storyboard für sich allein sinnvoll ist. - Gerüst:
node SKILL_DIR/scripts/new.mjs <projekt-verz> --title "...", dann bearbeitevideo.jsonund schreibe ein Modul pro Szene inscenes/<id>.js. Verwendereference/animation-patterns.mdfür Rezepte undreference/design.mdfür Layout, Typografie und Farbe. Mit einer Sprachausgabe setze die Szenendauer auf"auto"und animiere mitcue('phrase'). Beide Beispiele sind vollständige Videos mit Sprachausgabe:examples/kubernetes-overview/(ElevenLabs) undexamples/how-dns-works/(Gemini). - Sprachausgabe (falls gewünscht): folge
reference/voiceover.md. Kurz gefasst: Füge einen"voice"-Block invideo.jsonein, führenode SKILL_DIR/scripts/narration.mjs status <projekt-verz>aus, rufe das OpenRouter-MCP-Toolgenerate-speecheinmal pro aufgeführter Szene mit genau den Argumenten auf, die es ausgibt, und registriere jede gespeicherte Datei mitnarration.mjs add. Führe dies vor der Überprüfung (Schritt 4) durch, da echte Audiodateien die Szenenlängen undcue()-Zeitpunkte ändern. - Überprüfen:
node SKILL_DIR/scripts/lint.mjs <projekt-verz>. Es lädt jede Szene in Chromium und meldet Ladefehler, Animationen länger als ihre Szene, Animationen, die außerhalb vontlerstellt wurden, Elemente außerhalb des Rahmens, abgeschnittenen Text und eine zu lange Erzählung für ihre Szene. Behebe jede Warnung, sofern sie nicht beabsichtigt ist. - Ansehen:
node SKILL_DIR/scripts/render.mjs <projekt-verz> --contact-sheet, dann lies das PNG (<projekt-verz>/out/contact-sheet.png). Überprüfe Hierarchie, Überlappungen, Lesbarkeit, leere Frames und Timing. Für Details rendere bestimmte Momente:--stills 12.5,30. Iteriere, bis es richtig aussieht, und führe nach deiner letzten Bearbeitung einen weiteren Kontaktbogen-Durchlauf durch. - Rendern:
node SKILL_DIR/scripts/render.mjs <projekt-verz>→<projekt-verz>/out/video.mp4. Erzählclips werden automatisch eingemischt. Gib dem Benutzer den Pfad, die Dauer und die Szenenliste. Sie können interaktiv eine Vorschau anzeigen mitnode SKILL_DIR/scripts/serve.mjs <projekt-verz>(Scrubber, Abspielen/Pause, Erzählwiedergabe,cschaltet Untertitel um,mstumm).
Nützliche Render-Flags: --out datei.mp4, --from 10 --to 20 (teilweise), --scale 0.5 (schneller Entwurf), --workers N, --crf 18, --audio track.mp3 (eigene Tonspur anstelle der Erzählung verwenden), --no-voice (stumm), --audio-codec aac (Standard ist MP3, das auch in VS Code abgespielt wird).
Projektstruktur
mein-video/
video.json # Titel, Breite, Höhe, fps, Seed, Übergang, Thema, Szenen[]
scenes/<id>.js # ein Modul pro Szene
assets/ # Bilder/SVGs, referenziert als '/assets/x.svg'
audio/ # Erzählclips + manifest.json (von narration.mjs; behalte sie, sie kosten Geld)
style.css # optional, wird automatisch geladen, falls vorhanden
out/ # Renderings (contact-sheet.png, stills/, video.mp4)
video.json Szenenfelder: id (erforderlich, auch der Standard-Dateiname scenes/<id>.js), duration (Sekunden oder "auto": passt sich der Erzähl-Audio an oder einem Schätzlese-Tempo vor Audio-Erstellung und nie kürzer als die Animationen), narration, visual, optionales file und optionales transition ("fade", der Standard, oder "cut"). Der "voice"-Block auf oberster Ebene wird in reference/voiceover.md beschrieben.
Szenenvertrag
import { h, reveal, pop } from '/__runtime/helpers.js';
export default function ({ el, tl, gsap, theme, width, height, random, onFrame, cue, voice }) {
const title = h('div', { class: 'h1' }, 'Hallo');
el.append(h('div', { class: 'center' }, title));
reveal(tl, title, { at: 0.3 }); // tl-Zeit 0 = Start dieser Szene
tl.to(title, { color: 'var(--accent)', duration: 0.5 }, cue('die Schlüsselidee')); // wenn diese Phrase gesprochen wird
}
el: Der Container dieser Szene. Er bedeckt die gesamte 1920×1080-Bühne und wird für dich ein- und ausgeblendet.tl: Die Timeline dieser Szene. Alle Bewegungen müssen auftlstattfinden (oder einer anderen zutlhinzugefügten Timeline). Verwende niemalsgsap.to(...)direkt, CSSanimation/transition,setTimeout,setInterval,requestAnimationFrame,<video>oder irgendetwas, das von der Echtzeituhr abhängt. Der Renderer bewegt nur den Abspielkopf der Timeline, daher bleibt alles andere im MP4 eingefroren oder aus dem Takt geraten.cue(phrase, offset = -0.15)gibt die Szenenzeit zurück, zu derphrase(Groß-/Kleinschreibung, muss in der Erzählung dieser Szene vorkommen) gesprochen wird. Sobald Audio vorhanden ist, wird es an den gemessenen Pausen des Clips ausgerichtet, so dass Sätze, die einen Satz beginnen, auf dem gesprochenen Wort landen. Es wirft einen Fehler, wenn die Phrase nicht gefunden wird. Verwende es für jeden mit der Erzählung synchronisiertenat.voicegibt{ start, duration, end, estimated }für die Sprache in dieser Szene (z.B. um einen Schleifeneffekt bisvoice.endlaufen zu lassen).- Für Frame-für-Frame-Zeichnungen (Canvas, prozedurale Bewegungen) verwende
onFrame((sceneTime) => draw(sceneTime)). Es wird in jedem Frame aufgerufen, während die Szene sichtbar ist. - Overlay (optional):
"overlay": "scenes/_overlay.js"invideo.jsonfügt ein Modul hinzu, das für das gesamte Video über jeder Szene gezeichnet wird, z.B. für einen Zeitcode, Fortschrittsbalken, Logo oder Bildrahmen. Es erhält{ el, tl, onFrame, scenes, duration, ... }, wobeionFrame((t) => ...)die globale Zeit erhält undscenesdiestart/end-Werte jeder Szene auflistet. random()ist pro Szene gesetzt, und auchMath.randomist gesetzt, so dass zufällige Layouts reproduzierbar sind.- Die Funktion kann
asyncsein (z.B. umfetch('/assets/data.json')zu verwenden). Schriftarten werden vor der Szenenausführung geladen, so dass Textmessungen funktionieren. - Themenfarben sind CSS-Variablen:
var(--bg) --surface --surface2 --fg --muted --accent --accent2 --accent3 --accent4 --danger, plus--fontund--mono. Überschreibe sie invideo.json→"theme": { "accent": "#ff3366" }. - GSAP-Plugins sind global registriert: DrawSVG, MotionPath, MorphSVG, SplitText, Text, ScrambleText, CustomEase.
Qualitätsanspruch
- Eine Idee pro Szene. Auf dem Bildschirm sollte sich mindestens alle 2–3 Sekunden etwas bewegen oder ändern, und jede Animation sollte synchron mit der Erzählzeile landen, die sie illustriert (verwende
cue(); Erzählung läuft mit ~2,5 Wörtern/Sekunde). - Text ist groß: Textkörper ≥ 40px und Beschriftungen ≥ 26px auf der 1920×1080-Bühne. Halte ihn innerhalb des sicheren Bereichs (
.safe, 96/128px Ränder). - Baue Diagramme Stück für Stück auf, anstatt ein fertiges Diagramm zu zeigen.
- Lasse am Ende jeder Szene eine kurze Pause vor dem Ausblenden, damit der Endzustand gelesen werden kann.
Sprachausgabe
Audio kommt vom OpenRouter MCP-Server (generate-speech); dieser Skill plant und zeichnet es nur auf. Wenn diese Tools nicht verfügbar sind, teile dem Benutzer mit, dass die Sprachausgabe die Verbindung zum OpenRouter MCP-Server benötigt, und rendere vorerst ohne Sprachausgabe. reference/voiceover.md enthält das vollständige Verfahren, die Modell- und Sprachauswahl sowie die Kosten.