Créer des vidéos courtes avec du code
L'image est uniquement déterminée par le temps : la page expose window.render(t), on lui donne un nombre de secondes et elle dessine l'image de cet instant. Le script de rendu utilise un Chrome sans tête pour l'appeler image par image et faire des captures d'écran, puis ffmpeg assemble les captures et la voix off en mp4. Modifier l'image revient à modifier le code, il suffit de relancer le rendu.
Cette approche provient d'un article de xilo sur X (2026-09-29, https://x.com/xilo2991/status/2104912748794589515). Ce skill ajoute l'alignement de la voix off, les couvertures, la vérification des images fixes et un ensemble de règles pour écrire des scripts et dessiner les images.
Pour commencer
Nécessite Node 20+, uv, ffmpeg et Google Chrome installé localement. La police par défaut est Songti, PingFang et SF Mono de macOS ; sur d'autres systèmes, remplacez-les dans brand.json sous fonts par des polices disponibles localement.
- Copiez tout le dossier
template/de ce skill dans un nouveau répertoire de projet. - Dans le nouveau répertoire, exécutez
npm install(installe seulementplaywright-core). - Modifiez
brand.json:seriesest le nom de la série en haut à gauche de la couverture,handleest le nom pour la couverture et le générique,avatarest le chemin relatif de l'image d'avatar (peut êtrenull),colorsetfontssont les couleurs et polices. - Exécutez l'exemple :
uv run tools/voice.py demo # Voix off, musique, timeline -> episodes/01-demo/build/
node tools/render.mjs demo --stills auto # Image fixe à la fin de chaque plan
node tools/render.mjs demo # Rendu complet -> episodes/01-demo/out/video.mp4 et cover.png
sh tools/covers.sh demo # Couvertures 4:3 et 3:4
sh tools/sheet.sh demo # Couverture + dernière image de chaque plan -> build/sheet.png
Dans les commandes, on utilise uniquement le slug ; le script trouve lui-même episodes/<numéro>-<slug>/. La voix off envoie le texte du commentaire au service en ligne TTS de Microsoft.
Structure du projet
brand.json: Marque. Modifiez ici, pas dans le moteur.src/lib.js: Outils de dessin, attachés àwindow.E.src/run.js: Planification de la timeline. L'image 0 est la couverture, elle reste 0,3 seconde avant de glisser vers le premier plan ; les plans sont disposés horizontalement, reliés par des glissements ; les sous-titres et la barre de progression sont également dessinés ici.src/stage.html: Page de rendu.tools/voice.py: Synthèse ligne par ligne de la voix off, utilise les timestamps mot par mot pour déterminer le début de chaque phrase, ajoute de la musique de fond et des bips sonores, normalise le volume à -14 LUFS. En cas d'échec de synthèse, réessaie ; après trois échecs, signale une erreur.tools/render.mjs: Quatre pages en parallèle pour capturer les images, encodage ffmpeg, 30 images par seconde.tools/covers.sh,tools/cover-vertical.mjs: Couverture horizontale 4:3 (couverture 16:9 avec fond de couleur étendu) et couverture verticale 3:4 avec mise en page séparée.tools/sheet.sh: Extrait la couverture et la dernière image de chaque plan de la vidéo finale pour créer une fiche de contact.episodes/<numéro>-<slug>/: Un répertoire par épisode, contenantscript.json,shots.js, et les dossiers générésbuild/etout/.
Déroulement d'un épisode
Suivez les étapes dans l'ordre ; ne passez pas à l'étape suivante si la précédente n'est pas terminée.
Le contenu et les faits sont de votre responsabilité ; ce skill commence à partir de la rédaction du script.
- Script : Créez
episodes/<numéro>-<slug>/, écrivezscript.json, et écrivez le numéro danscover.no. - Visuels : Écrivez
shots.jsdans le même répertoire ; le nombre de plans doit correspondre au nombre de lignes de commentaire. - Voix off :
uv run tools/voice.py <slug>. Relancez si vous modifiez le commentaire, la voix ou le texte de la couverture. - Vérification des images fixes : Voir la section "Vérification des images fixes" ci-dessous.
- Vidéo finale :
node tools/render.mjs <slug>, puis exécutezcovers.shetsheet.sh, et vérifiez la fiche de contact. - Livraison : Voir la dernière section de ce document.
Lorsque vous ajustez le numéro d'épisode, modifiez à la fois le préfixe du nom du répertoire et cover.no, puis relancez la voix off et le rendu. Si vous modifiez uniquement le texte de la couverture, pas besoin de relancer la voix off : modifiez cover dans script.json, remplacez cover dans build/timeline.js par le même contenu, puis relancez le rendu et covers.sh.
script.json
{
"title": "Cette vidéo est dessinée par du code",
"cover": { "no": "01", "lines": ["Cette vidéo", "est dessinée par du code"], "hot": 1, "tag": "Pas de logiciel de montage, ni de modèle vidéo" },
"voice": "zh-CN-YunxiNeural",
"rate": "+6%",
"lines": [["Chaque image que vous voyez, ", "est dessinée par du code."], ["La page n'expose qu'une fonction : ", "donnez-lui un temps, ", "elle dessine l'image de cet instant."]]
}
- Une ligne est un plan, chaque segment à l'intérieur d'une ligne est un rythme visuel. Les segments peuvent être écrits comme
[texte parlé, texte des sous-titres]: parlez "soixante-quinze" mais affichez "75%" ; utilisez également cette méthode pour corriger la prononciation de termes anglais difficiles. - Couverture :
linesdeux lignes maximum,hotest la ligne qui utilise la couleur principale,tagest un crochet avec un nombre ou une conclusion spécifique,noest un numéro d'épisode à deux chiffres. Le titre de la couverture, le titre de la plateforme et la première phrase du commentaire disent la même chose.
Comment écrire le script
- Pour les vidéos courtes vues verticalement sur mobile, une durée de 75 à 90 secondes avec 8 à 9 plans est appropriée. Avec un débit vocal de +6%, environ 4,8 caractères par seconde (y compris les pauses), le commentaire fait environ 380 à 430 caractères (caractères chinois plus mots anglais). Si le script dépasse, supprimez d'abord des caractères, puis faites la voix off.
- Les deux premières secondes doivent contenir l'élément le plus accrocheur de l'épisode. Avant d'écrire le script, demandez-vous : quel est l'élément vraiment captivant de cet épisode ? C'est généralement un nombre contre-intuitif, un résultat concret ou un problème que rencontre le public. Dans le modèle, le commentaire commence après 0,5 seconde (
HEADdanstools/voice.py), donc à 2 secondes, on ne peut dire que 8 à 9 caractères. Le premier segment doit donc être court, et les premiers mots doivent être cet élément ; le premier plan doit l'afficher en gros caractères avant 1,2 seconde ; letagde la couverture doit également dire la même chose. Utilisez les propres termes du public pour cet élément, avec des mots-clés thématiques ("Ne poussez pas l'intensité de raisonnement à fond", puis "De High à Max, coût 2,9 fois, score seulement 0,6 de plus"), puis entrez dans le vif du sujet. Pas de préambule, pas de présentation personnelle, pas de "Aujourd'hui, on va parler de", pas de "Qu'est-ce que X ?" seul, ni de "Le même A, le même B" comme introduction vide. Ceci est basé sur l'expérience de quelques vidéos, à considérer comme à vérifier. - Les ordres suivants peuvent servir de point de départ, adaptez-les au contenu.
- Tutoriel : Résultat final → Une étape par plan → Pièges courants → Une phrase pour conclure.
- Présentation de produit : Le problème qu'il résout → Deux ou trois fonctions principales, une fonction par plan → Comment commencer → Une phrase pour conclure.
- Explication de concept : Conclusion et chiffres → Ancienne méthode ou idée fausse courante → Problème → Nouvelle méthode → Mécanisme spécifique → Données de test ou officielles → Une phrase pour conclure.
- Résultat de recherche : Conclusion et chiffres → Idée reçue courante → Comment l'expérience a été menée → Résultats → Recherches ultérieures → Raison → Une phrase pour conclure.
- Pour la conclusion, d'abord résumez la conclusion ou donnez une manière de faire, puis posez une question à laquelle le public peut répondre par sa propre expérience ("Quel réglage utilises-tu d'habitude ?"), pour générer des commentaires. La question doit faire écho au commentaire épinglé. Cette règle est également à vérifier en fonction de l'évolution du nombre de commentaires. Ne faites pas de plan séparé pour les limites ; les conditions des données sont écrites dans la note de bas de page du plan de données.
- Ne dites pas "Dans le prochain épisode", "Dans l'épisode précédent" ; l'ordre de publication peut être modifié. Pour faire référence à un autre épisode, dites "L'épisode sur X".
- Lorsque la source donne une marge d'erreur, une différence dans la marge est décrite comme "presque inchangée". Les chiffres dérivés par vous-même doivent être mentionnés comme preuve faible lors de la livraison.
- N'utilisez pas d'URL dans le commentaire oral, ne dites pas "Allez voir sur X", "Cherchez Y" (la plateforme considère cela comme une redirection externe, voir le skill video-publish).
Comment dessiner les visuels
shots.js est une fonction immédiatement exécutée qui se termine par window.SHOTS = [...]. Chaque plan est (c, k) => {} :
cest le contexte 2D du Canvas en 1920×1080.k.at(i, delay, dur)retourne la progression ease-out de 0 à 1 pour le segment i, commençant aprèsdelaysecondes et durantdursecondes ;k.rawest la progression linéaire équivalente ;k.test le temps écoulé depuis le début du plan,k.dest la durée de cette ligne de commentaire,k.segssont les secondes de chaque segment par rapport au début du plan.- Les outils de dessin sont dans
window.E:title(titre du plan avec trait de marque),txt,measure,rise(fondu avec déplacement),pop(apparition),rrect,path(ligne brisée selon la progression),arrow,chip(bloc d'étiquette),code(bloc de code machine à écrire),stroke(trait de marque),mark(avatar),sign(générique de fin),fmt, couleursC, policesF. Lisez d'abord les signatures des fonctions danssrc/lib.jsavant de les utiliser. Les outils manquants sont écrits dans leshots.jsde l'épisode concerné ; ne modifiez paslib.jsetrun.jspour un seul plan.
Règles :
- Chaque segment correspond à un nouvel élément visuel ; il doit y avoir un changement toutes les 2 à 4 secondes. Un plan ne traite qu'un seul sujet, illustré graphiquement : flèches de processus, matrice de points, barres de comparaison, blocs de code, tableaux, pas de texte plein écran.
- Alignez à gauche, asymétrique. Évitez les arrière-plans foncés avec grands caractères centrés, ne faites pas apparaître tous les éléments en même temps, pas d'effet lumineux.
- La couleur d'accentuation principale marque "le nombre à regarder sur cet écran", ne représente pas le bien ou le mal ; la couleur d'accentuation secondaire est utilisée au maximum une ou deux fois par écran.
- Déterminez la taille de la police en fonction de l'affichage vertical du téléphone pour un écran horizontal :
txtrelève les tailles inférieures à 56 (24px dessinés en 34px, 30 dessinés en 38px), la valeur transmise pour les informations portées ne doit pas être inférieure à 30, les notes de bas de page de source peuvent être 24. La largeur estimée est calculée en fonction de la taille relevée ; chaque caractère d'une police à chasse fixe représente environ 0,6 fois la taille de la police. - Limites de mise en page : la barre de sous-titres est en dessous de y 932, une ligne contient environ 30 caractères pleine largeur ; la phrase d'accroche de la couverture touche l'avatar à environ 14 caractères pleine largeur ; le plan a un léger zoom avant de 1,025x, les éléments au bord doivent avoir une marge de 20px ou plus ; une note de bas de page de 24px contient environ 48 caractères pleine largeur par ligne.
- La police Songti Black n'a pas de "÷", le "×" sera dessiné comme une grande croix : utilisez PingFang pour les opérateurs, ou écrivez "divisé par" ou "fois". Dans un grand titre de 200px, les lettres anglaises descendantes peuvent écraser la ligne suivante.
- Lorsque le premier segment est très court, le début du plan peut être vide : placez la structure du graphique (axes, en-têtes de tableau, contours) dans le premier segment, et les données apparaissent avec les segments suivants.
- Les icônes qui ne sont pas des données réelles sont marquées "schéma". Les nombres lus à partir de graphiques de la source originale sont marqués en note de bas de page "lu à partir du graphique original". En bas du plan de données, écrivez la source (uniquement le nom, "Source : Documentation officielle de X"), pas de nom de domaine ni d'URL.
- Chaque plan utilise
E.titlepour le titre ; le dernier plan utiliseE.signpour le générique.
Vérification des images fixes
node tools/render.mjs <slug> --stills auto produit la dernière image de chaque plan ; --stills 0,t1,t2,… produit des images à des secondes spécifiques (0 est la couverture). Les images intermédiaires sont prises environ 0,4 seconde après le début de chaque ligne et environ 1 seconde après chaque segment ; les points temporels sont lus dans build/timeline.js de l'épisode. Chaque exécution de --stills efface le lot précédent, donc examinez par lots ou combinez les points temporels dans une seule commande. Lorsque le nombre d'images est élevé, utilisez ffmpeg pour les assembler en une fiche de contact ; examinez la couverture et la dernière image en taille réelle.
Vérifiez une par une :
- Le texte dépasse-t-il des cartes, blocs de code, colonnes de tableau, ou est-il masqué par la barre de sous-titres et la barre de progression ?
- Les nombres alignés à droite et les étiquettes de gauche se touchent-ils ? Les étiquettes d'axe et les étiquettes de courbe se chevauchent-elles ?
- La dernière image du plan est-elle la plus complète en termes d'informations ? Y a-t-il des éléments qui ne sont pas encore entrés ?
- Les deux lignes de titre de la couverture sont-elles trop petites ? La phrase d'accroche est-elle trop large ?
- Y a-t-il des images vides de 2 à 3 secondes sans rien de nouveau, surtout au début du plan ?
Si vous trouvez un problème, corrigez-le ; ne produisez pas de vidéo finale avec des débordements connus. Si vous modifiez uniquement les visuels, relancez simplement le rendu.
Limitations connues
- edge-tts utilise l'interface non officielle de Microsoft. La version 7.2.8 retourne 403 depuis octobre 2026 (rany2/edge-tts #490) ;
tools/voice.pyest fixé sur le commit de la PR de correction #491 ; après la sortie d'une nouvelle version officielle, remplacez paredge-tts==<nouvelle_version>. Si 403 réapparaît, consultez d'abord le dépôt upstream pour les issues. - L'agent n'entend pas la voix off : la prononciation et le débit des termes anglais doivent être vérifiés par une personne. Les interruptions de flux pendant la synthèse sont gérées par la vérification de durée dans
voice.py. - Pas de flou de mouvement, ni de montage sur le rythme musical.
Ce qu'il faut mentionner lors de la livraison
- La voix off n'a pas été écoutée par une personne.
- Le résultat de la vérification des images fixes, y compris les problèmes connus encore présents.
- Le chemin de la vidéo finale, des trois couvertures et de la fiche de contact, ainsi que la durée.