Communitygithub.com

lleadbet/video-explainer-skill

Créez des vidéos explicatives informatives (MP4) en écrivant des scènes 2D animées en HTML/SVG/CSS avec GSAP et en les rendant image par image avec Chromium headless + ffmpeg.

Qu'est-ce que video-explainer-skill ?

Cette compétence permet de générer des vidéos explicatives de haute qualité sans avoir recours à un logiciel de montage vidéo traditionnel. Elle fonctionne en écrivant des scènes animées en 2D sous forme de modules JavaScript exploitant GSAP (GreenSock Animation Platform), que le moteur de rendu capture image par image via un navigateur Chromium headless, puis assemble en fichier MP4 avec ffmpeg. Le résultat est déterministe : un même projet produit toujours les mêmes images. Elle prend en charge les voix off via OpenRouter (ElevenLabs ou Gemini), les transitions, les overlays, et propose un workflow structuré : storyboard, développement des scènes, vérification, prévisualisation interactive, et rendu final. Idéal pour les démos, les diagrammes animés, ou les vidéos de formation sans recourir à l'IA générative ou au montage vidéo classique.

Compatible avec~Claude Code~Codex CLI~Cursor✓Gemini CLI
npx skills add https://github.com/lleadbet/video-explainer-skill/tree/HEAD

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

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

  1. 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 contient narration (ce que dit la voix off) et visual (ce qui est affiché à l'écran). Lisez reference/storyboard.md. N'écrivez pas le code des scènes avant que le storyboard ait du sens tout seul.
  2. Structure : node SKILL_DIR/scripts/new.mjs <project-dir> --title "...", puis éditez video.json et écrivez un module par scène dans scenes/<id>.js. Utilisez reference/animation-patterns.md pour les recettes et reference/design.md pour 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 avec cue('phrase'). Les deux exemples sont des vidéos complètes avec voix : examples/kubernetes-overview/ (ElevenLabs) et examples/how-dns-works/ (Gemini).
  3. Voix off (si souhaitée) : suivez reference/voiceover.md. En résumé : ajoutez un bloc "voice" à video.json, exécutez node SKILL_DIR/scripts/narration.mjs status <project-dir>, appelez l'outil generate-speech du MCP OpenRouter une fois par scène listée avec exactement les arguments qu'il affiche, et enregistrez chaque fichier sauvegardé avec narration.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 de cue().
  4. 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 de tl, 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.
  5. 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.
  6. 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 avec node SKILL_DIR/scripts/serve.mjs <project-dir> (curseur, lecture/pause, lecture de la narration, c active les sous-titres, m coupe 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 sur tl (ou une autre timeline qui lui est ajoutée). N'utilisez jamais directement gsap.to(...), les animation/transition CSS, 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 chaque at synchronisé avec la narration. voice donne { 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" dans video.json ajoute 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 et scenes liste start/end de chaque scène.
  • random() est basé sur une graine par scène, et Math.random est aussi basé sur une graine, donc les mises en page aléatoires sont reproductibles.
  • La fonction peut être async (par exemple pour fetch('/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 --font et --mono. Surchargez-les dans video.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.

Skills associés