Video explainer
Vous créez des vidéos comme le ferait un motion designer, mais en code : chaque scène est un petit module JS qui construit le DOM/SVG et ajoute des animations à une timeline GSAP en pause. Le moteur de rendu parcourt cette timeline image par image (t = frame / fps), capture chaque image en screenshot, et les envoie à ffmpeg. La sortie est déterministe : un même projet produira toujours les mêmes images.
SKILL_DIR ci-dessous désigne le répertoire de base de cette compétence (le dossier contenant ce fichier).
Configuration (une seule fois par machine)
cd SKILL_DIR/scripts && npm install && npx playwright install chromium
Vous avez aussi besoin de ffmpeg dans le PATH. Chaque script affiche cette commande si les dépendances sont manquantes.
Workflow
- Storyboard d'abord. Accordez-vous sur le sujet, le public, la durée cible et la présence ou non d'une voix off, puis écrivez la liste des scènes dans
video.json. Chaque scène contientnarration(ce que dit la voix off) etvisual(ce qui est affiché à l'écran). Lisezreference/storyboard.md. N'écrivez pas le code des scènes avant que le storyboard ait du sens tout seul. - Structure :
node SKILL_DIR/scripts/new.mjs <project-dir> --title "...", puis éditezvideo.jsonet écrivez un module par scène dansscenes/<id>.js. Utilisezreference/animation-patterns.mdpour les recettes etreference/design.mdpour la mise en page, la typographie et les couleurs. Avec une voix off, réglez les durées des scènes sur"auto"et chronométrez les animations aveccue('phrase'). Les deux exemples sont des vidéos complètes avec voix :examples/kubernetes-overview/(ElevenLabs) etexamples/how-dns-works/(Gemini). - Voix off (si souhaitée) : suivez
reference/voiceover.md. En résumé : ajoutez un bloc"voice"àvideo.json, exécuteznode SKILL_DIR/scripts/narration.mjs status <project-dir>, appelez l'outilgenerate-speechdu MCP OpenRouter une fois par scène listée avec exactement les arguments qu'il affiche, et enregistrez chaque fichier sauvegardé avecnarration.mjs add. Faites-le avant la vérification (étape 4), car l'audio réel modifie les durées des scènes et les timings decue(). - Vérification :
node SKILL_DIR/scripts/lint.mjs <project-dir>. Il charge chaque scène dans Chromium et signale les erreurs de chargement, les animations plus longues que leur scène, les animations créées en dehors detl, les éléments hors du cadre, le texte tronqué, et la narration trop longue pour sa scène. Corrigez chaque avertissement sauf s'il est intentionnel. - Aperçu :
node SKILL_DIR/scripts/render.mjs <project-dir> --contact-sheet, puis Lisez le PNG (<project-dir>/out/contact-sheet.png). Vérifiez la hiérarchie, le chevauchement, la lisibilité, les images vides, le timing. Pour les détails, rendez des moments spécifiques :--stills 12.5,30. Itérez jusqu'à ce que ce soit correct, puis faites une dernière passe de contact-sheet après votre dernière modification. - Rendu :
node SKILL_DIR/scripts/render.mjs <project-dir>→<project-dir>/out/video.mp4. Les clips de narration sont mélangés automatiquement. Donnez à l'utilisateur le chemin, la durée et la liste des scènes. Il peut prévisualiser interactivement avecnode SKILL_DIR/scripts/serve.mjs <project-dir>(curseur, lecture/pause, lecture de la narration,cactive les sous-titres,mcoupe le son).
Drapeaux de rendu utiles : --out file.mp4, --from 10 --to 20 (partiel), --scale 0.5 (brouillon rapide), --workers N, --crf 18, --audio track.mp3 (utilisez votre propre piste audio au lieu de la narration), --no-voice (silencieux), --audio-codec aac (le défaut est MP3, qui se lit aussi dans VS Code).
Structure du projet
my-video/
video.json # titre, largeur, hauteur, fps, seed, transition, thème, scènes[]
scenes/<id>.js # un module par scène
assets/ # images/SVGs, référencés par '/assets/x.svg'
audio/ # clips de narration + manifest.json (provenant de narration.mjs ; conservez-les, ils coûtent de l'argent)
style.css # optionnel, chargé automatiquement s'il est présent
out/ # rendus (contact-sheet.png, stills/, video.mp4)
Champs de scène dans video.json : id (obligatoire, également le nom de fichier par défaut scenes/<id>.js), duration (secondes, ou "auto" : ajuste la durée du clip audio de narration, ou une estimation basée sur la vitesse de lecture avant que l'audio n'existe, et jamais plus courte que les animations), narration, visual, file optionnel, et transition optionnel ("fade", le défaut, ou "cut"). Le bloc "voice" au niveau supérieur est décrit dans reference/voiceover.md.
Contrat de scène
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' }, 'Hello');
el.append(h('div', { class: 'center' }, title));
reveal(tl, title, { at: 0.3 }); // tl time 0 = début de cette scène
tl.to(title, { color: 'var(--accent)', duration: 0.5 }, cue('the key idea')); // quand cette phrase est prononcée
}
el: le conteneur de cette scène. Il couvre toute la scène 1920×1080 et est affiché et masqué pour vous.tl: la timeline de cette scène. Tout mouvement doit être surtl(ou une autre timeline qui lui est ajoutée). N'utilisez jamais directementgsap.to(...), lesanimation/transitionCSS,setTimeout,setInterval,requestAnimationFrame,<video>, ou quoi que ce soit qui dépend du temps réel. Le moteur de rendu ne déplace que le curseur de la timeline, donc tout le reste reste figé ou désynchronisé dans le MP4.cue(phrase, offset = -0.15)renvoie le temps de la scène oùphrase(insensible à la casse, doit apparaître dans la narration de cette scène) est prononcée. Une fois l'audio existant, elle est alignée sur les pauses mesurées du clip, de sorte que les phrases qui commencent une proposition tombent sur le mot prononcé. Elle génère une erreur si la phrase n'est pas trouvée. Utilisez-la pour chaqueatsynchronisé avec la narration.voicedonne{ start, duration, end, estimated }pour la parole dans cette scène (par exemple, pour garder un effet de boucle jusqu'àvoice.end).- Pour le dessin par image (canvas, mouvement procédural), utilisez
onFrame((sceneTime) => draw(sceneTime)). Il est appelé à chaque image tant que la scène est visible. - Superposition (optionnelle) :
"overlay": "scenes/_overlay.js"dansvideo.jsonajoute un module dessiné au-dessus de chaque scène pour toute la vidéo, pour un code temporel, une barre de progression, un logo ou un cadre. Il reçoit{ el, tl, onFrame, scenes, duration, ... }, oùonFrame((t) => ...)reçoit le temps global etsceneslistestart/endde chaque scène. random()est basé sur une graine par scène, etMath.randomest aussi basé sur une graine, donc les mises en page aléatoires sont reproductibles.- La fonction peut être
async(par exemple pourfetch('/assets/data.json')). Les polices sont chargées avant l'exécution des scènes, donc la mesure du texte fonctionne. - Les couleurs du thème sont des variables CSS :
var(--bg) --surface --surface2 --fg --muted --accent --accent2 --accent3 --accent4 --danger, plus--fontet--mono. Surchargez-les dansvideo.json→"theme": { "accent": "#ff3366" }. - Les plugins GSAP sont enregistrés globalement : DrawSVG, MotionPath, MorphSVG, SplitText, Text, ScrambleText, CustomEase.
Barre de qualité
- Une idée par scène. Quelque chose à l'écran doit bouger ou changer au moins toutes les 2 à 3 secondes, et chaque animation doit atterrir en synchronisation avec la ligne de narration qu'elle illustre (utilisez
cue(); la narration tourne à environ 2,5 mots/seconde). - Le texte est grand : corps ≥ 40px et étiquettes ≥ 26px sur la scène 1920×1080. Restez dans la zone sécurisée (
.safe, marges de 96/128px). - Construisez les diagrammes pièce par pièce plutôt que de montrer un diagramme fini.
- Laissez une courte pause à la fin de chaque scène avant la transition, pour que l'état final puisse être lu.
Voix off
L'audio provient du serveur MCP OpenRouter (generate-speech) ; cette compétence ne fait que planifier et enregistrer. Si ces outils ne sont pas disponibles, dites à l'utilisateur que la voix off nécessite la connexion du serveur MCP OpenRouter, et faites le rendu sans voix en attendant. reference/voiceover.md contient la procédure complète, les choix de modèles et de voix, et les coûts.