Que fait framewright ?
Vous allez réaliser une courte vidéo comme un graveur réalise une estampe : un seul fichier HTML avec un canvas, une liste de scènes appelées plaques (plates), et une fonction d'image (frame) qui dessine n'importe quelle image à la demande à partir de (numéro d'image, seed, largeur). Pas de séquences vidéo, pas de fichiers image, rien depuis un CDN. Les images sont rendues dans Chrome headless et assemblées par ffmpeg. Le son est synthétisé en dernier, sur les longueurs de scène verrouillées.
Parlez à l'utilisateur dans sa langue. Respectez les règles ci-dessous même lorsque l'utilisateur est pressé : chaque raccourci ici coûte plus qu'il ne rapporte.
SKILL_DIR ci-dessous désigne le dossier qui contient ce fichier (dans Claude Code c'est
${CLAUDE_SKILL_DIR} ; ailleurs c'est .agents/skills/framewright). Après l'étape 3, les
scripts résident aussi dans le dossier scripts/ du projet.
Non-négociables
- Une image dépend uniquement du numéro d'image, de la seed et de la largeur de sortie. Pas de
Math.random, pas de temps réel, pas d'animation CSS, pas derequestAnimationFramecomme source temporelle.scripts/check.mjsle prouve avant chaque rendu complet. - Pas d'image, vidéo, police ou fichier de bibliothèque, pas de base64, pas de CDN. Des polygones tracés depuis une photo par un script sont des données et sont acceptables. Les polices système suffisent.
- Vous vérifiez en regardant les images rendues, jamais en lisant du code et en imaginant. Toute correction est confirmée avec une image.
- Une scène à la fois. Écrivez une plaque, tirez trois images, regardez, corrigez, puis la plaque suivante.
- Les longueurs des scènes sont des multiples du temps (beat) du premier jet. Le son vient en dernier.
- Rendu complet seulement après qu'une planche contact (contact sheet) de toute la vidéo semble correcte.
- Ne commettez jamais
frames/,shots/,*.mp4,*.wavou les photos de l'utilisateur. - Demandez avant d'installer des paquets système. Ne publiez jamais l'image d'une personne réelle sans que l'utilisateur confirme avoir le droit de l'utiliser.
Aperçu du workflow
| Étape | Ce qui se passe | Point de contrôle avec l'utilisateur |
|---|---|---|
| 0 | vérification de l'environnement | seulement si quelque chose doit être installé |
| 1 | brief | questionnaire, un bloc |
| 2 | concepts | trois options, choisir une |
| 3 | storyboard + échafaudage | un « go » |
| 4 | scènes, une par une | planche contact toutes les 2–3 scènes |
| 5 | ressources (photos, logos) | aperçu du portrait tracé |
| 6 | planche complète, rendu, construction | aperçu MP4 |
| 7 | son | MP4 reconstruit |
| 8 | livraison et variations | final |
Temps typique : brief et concepts 10 minutes, échafaudage 10, chaque scène 10–20, rendu 2–4 minutes par minute de vidéo 1080p, son 15.
Étape 0. Environnement
bash SKILL_DIR/scripts/doctor.sh # rapport ; ajoutez --json pour un résumé lisible par machine
bash SKILL_DIR/scripts/doctor.sh --install # installe ce qui manque, demande avant chaque étape
Requis : Node 20+, npm, ffmpeg avec libx264, Puppeteer avec Chrome dans le projet.
Optionnel : Python 3 avec numpy, scipy, Pillow, seulement lorsqu'une photo sera tracée. Si le docteur
signale des outils manquants, dites à l'utilisateur ce qui sera installé et où, obtenez un oui, puis exécutez
--install (ajoutez --yes lorsque l'utilisateur a déjà accepté). Ne commencez pas l'étape 3 avec un docteur défaillant.
Étape 1. Le brief
Exécutez le questionnaire depuis references/questionnaire.md. Utilisez l'outil de questions structurées de
l'agent quand il existe (Claude Code : AskUserQuestion, jusqu'à quatre questions par appel,
devux à quatre options chacune, mettez l'option recommandée en premier et marquez-la). Sans un tel outil,
affichez les questions sous forme de liste numérotée avec des options lettrées et une valeur par défaut par
question, et acceptez des réponses concises comme 1b 2a 3c. Demandez toujours, dans cet ordre :
- Message : que doit comprendre ou ressentir le spectateur, en une phrase. Noms, dates, liens, phrases exactes qui doivent apparaître à l'écran.
- Où cela est diffusé et le format : 16:9, 9:16, 1:1, 4:5.
- Durée : 10–15, 20–25, 30–40, 45–60 secondes.
- Ton : ludique, impassible, chaleureux, épique, technique, sombre.
- Style : laissez proposer (par défaut) ou une famille depuis
references/styles.md. - Ressources : photos, logos, captures d'écran. Les fichiers doivent être placés dans le dossier du projet ; une image collée dans le chat ne peut pas être sauvegardée par vous. Demandez les droits pour les personnes réelles.
- Son : synthétisé, aucun, ou espace pour la piste de l'utilisateur (demandez son BPM).
- Livrables et qualité : brouillon ou final, coupes supplémentaires (vertical, GIF, image d'aperçu, variations de seed), une couverture comme première image du MP4 (les flux la montrent comme aperçu).
Si l'utilisateur dit « tu décides », prenez les valeurs par défaut, dites lesquelles vous avez prises, et passez à la suite.
Étape 2. Concepts
Proposez exactement trois concepts avant tout code. Construisez-les sous trois angles : littéral (montrez le
sujet), métaphore (un système visuel qui représente la situation : une mire TV pour l'attente, un plan pour
un projet, un terminal pour un processus), et parodie de genre (bulletin d'information, mode attente d'arcade,
carte de sécurité, bulletin météo, télétexte). Pour chaque concept donnez : un nom, un pitch d'une phrase,
le système visuel et la palette, trois à cinq scènes clés dans l'ordre, la fin, une esquisse sonore, pourquoi
cela correspond, et son principal risque. Présentez un tableau compact, recommandez-en un, et demandez à
l'utilisateur de choisir. La méthode et des exemples travaillés se trouvent dans
references/questionnaire.md. Un concept dont le pitch est un voyage sans coupure (un trait qui ne
se lève jamais, un vol d'un détail vers un paysage) est construit comme un monde continu
(references/world.md) ; dites-le dans sa ligne, car cela change la façon dont les scènes sont construites et modifiées.
Un concept qui est une vue dessinée, peinte et animée (un dessin au crayon qui devient une
peinture à l'huile vivante, styles 13–16) est construit comme un film peinture (references/painting.md) ; dites-le
aussi dans sa ligne : ses plaques sont des chapitres de temps sur une seule peinture, et la scène est le travail.
Étape 3. Storyboard et échafaudage
Remplissez storyboard.md (modèle dans assets/storyboard.md) : une ligne par plaque, longueurs en
mesures, la grille de temps (30 fps, 120 BPM : temps 15 images, mesure 60), la seconde de l'événement principal,
le total. Le total doit correspondre à la durée convenue ; lorsqu'une scène est ajoutée plus tard, une autre
scène cède une mesure. Affichez le tableau et demandez un seul go.
Ensuite, échafaudez :
bash SKILL_DIR/scripts/init.sh # index.html, scripts/, audio.mjs, storyboard.md, package.json
bash SKILL_DIR/scripts/init.sh . --world # idem, mais index.html est le squelette d'un monde unique (pas de coupures)
bash SKILL_DIR/scripts/init.sh . --painting # le squelette peinture ; audio.mjs et nature.mjs créent un son de nature
npm install # puppeteer (Chrome vient du cache ou est téléchargé une fois)
node scripts/look.mjs shot 0,30,60 1200 7
Ouvrez les PNG avec votre outil de visualisation d'images et regardez-les. La plaque de démonstration du squelette doit
être rendue avant que vous n'y touchiez. Remplacez ensuite la plaque de démonstration par votre première scène. Dans un film peinture,
l'image 0 est du papier blanc : tirez 30,82,200 (le dessin, la couleur qui arrive, la peinture vivante)
à la place, et remplacez le bloc de scène de la démo tandis que le kit de peinture reste tel quel.
Étape 4. Scènes
Lisez references/guide.md une fois avant la première scène. L'essentiel :
plate('nom', {len: 2*BAR}, (S, R) => { ... }).S.test la progression 0..1,S.il'image locale,S.fl'image globale,S.gle contexte 2D en coordonnées logiques (petit côté 1080).Rest un générateur stable pour toute la plaque ;S.bré-initialise la seed toutes les trois images et donne une ligne de vie ;S.nzchange à chaque image pour le bruit. Les seeds dérivent du nom de la plaque.- Toute la géométrie en unités logiques, centrée via
CX, CY. Gardez le contenu important à l'intérieur des 92 % centraux de l'image ; en 9:16, évitez aussi les 15 % du haut et les 20 % du bas, où se trouvent les boutons et les sous-titres de la plateforme. - Le temps à l'intérieur d'une plaque en temps :
Math.floor(S.i/BEAT),span(S.i, a, b),ease.out(...). Ne codez jamais en dur des numéros d'image globaux à l'intérieur d'une scène. Quelque chose qui continue à travers les coupures prend le temps d'une autre plaque par son nom,at('plaque', temps); un moment que le son doit atteindre va dansCUESet est lu aveccue('nom'). - Les aides vivent au-dessus du bloc de plaques, jamais entre les plaques.
- Les transitions sont dessinées par le moteur aux bords des plaques (indicateurs
cutIn/cutOut) ; les scènes ne les connaissent pas. - Un film monde (
--world) n'a pas de coupures. Les plaques sont des constructeurs,build(W, {pen, b, R}), qui ajoutent à une seule chronologie :b(x)est le temps x de la plaque, le stylo continue la ligne là où la plaque précédente l'a laissée,camKeydéplace une caméra,titleajoute du texte. Lisezreferences/world.mdavant le premier constructeur. - Un film peinture (
--painting) dessine une seule peinture fixe en trois médiums. Ses plaques appellent toutesdrawPainting(S); la vue vit dans l'objetSCENEet ses couches entre/* ===== scene ===== */et/* ===== end scene ===== */, et le kit de peinture au-dessus reste tel quel. Lisezreferences/painting.mdavant la première couche. Examinez d'abord le dessin seul, puis la révélation, puis la vie (references/painting.md, section 13).
Par scène : écrivez-la, tirez la première image, une au milieu, une cinq images avant la fin, regardez, corrigez, regardez à nouveau. Après toutes les deux ou trois scènes :
node scripts/look.mjs sheet 24 480 7 shots/sheet.png
FROM=240 TO=480 node scripts/look.mjs sheet 12 480 7 shots/sheet-b.png # une section, plus dense
Liste de vérification pour la planche, chaque élément avec vos yeux : chaque case se lit comme une image fixe en 1,5 seconde ; aucun texte n'est coupé ; les lignes et panneaux voisins ne se touchent pas ; deux cases adjacentes d'une même plaque ne se ressemblent jamais ; les coupures atterrissent là où prévu et ne mangent pas la première image d'une scène ; rien d'important ne se trouve dans les 8 % extérieurs ; la couleur n'est pas boueuse ; les lignes fines ne s'effritent pas ; l'événement principal se situe sur sa seconde prévue ; la dernière image ressemble à une fin ; jamais deux titres ne partagent l'écran au même endroit. Une scène qui échoue au test « 1,5 seconde » est reconçue, pas décorée. Une scène vide reçoit un objet plus grand ou une caméra plus proche, jamais plus de détail ; un gros plan chargé voit son décor atténué autour du sujet. Dans un film monde, tirez aussi la dernière image de chaque plaque à côté de la première image de la suivante : rien ne doit sauter.
Envoyez la planche à l'utilisateur à ces moments avec deux phrases de statut. Ne racontez pas le code.
Étape 5. Ressources
Les photos deviennent des polygones affichés, jamais des pixels :
bash scripts/portrait.sh photo.jpg --levels 0,14,0,28,0,42,0,56,0,70,0,84 --height 900
node scripts/look.mjs shot <image dans la scène portrait> 1200 7
Regardez shots/portrait_preview.png et l'image rendue. Ajustez les niveaux, le flou, le recadrage et le
seuil de fond selon references/photo.md. Tant que le fichier n'est pas encore dans le dossier, construisez la
scène sur le substitut synthétique fourni dans le squelette et dites-le.
Étape 6. Planche complète, rendu, construction
node scripts/look.mjs sheet 24 480 7 shots/sheet.png # regardez, corrigez, répétez
node scripts/check.mjs # déterministe et auto-contenu, doit dire OK
node scripts/render.mjs frames 7 1920 5 # dir, seed, largeur (omise : 1920 paysage, 1080 portrait), onglets
bash scripts/build.sh out.mp4 # frames (+ track.wav) -> mp4
La vérification rend cinq images de chaque plaque dans sept ordres (première image d'un nouvel onglet,
avant, arrière, mélangé, juste après l'image précédente, après une autre largeur, après un
balayage du film comme le ferait un onglet de rendu) et exige des pixels identiques. Elle scanne aussi
le source à la recherche de Math.random, Date et performance.now, échoue lorsque la page demande un
fichier, et affiche la grille de temps et le temps de rendu prévu. Corrigez chaque ÉCHEC avant de rendre ;
make.sh exécute la vérification et s'arrête en cas d'échec. Une image qui change avec l'ordre de rendu indique un
état qui survit à une image : un canvas mis en pool sans effacement, un global qu'une plaque modifie, une
clé de cache sans toutes ses entrées.
Vérifiez à partir du fichier, pas des images : le nombre d'images ffprobe est égal au total de
look.mjs info, la durée est égale aux images divisées par 30, et une mosaïque faite à partir du MP4 correspond à la
planche :
ffmpeg -i out.mp4 -vf "select='not(mod(n\,50))',scale=480:-1,tile=6x4" -frames:v 1 shots/out_sheet.png
Envoyez le premier MP4 à l'utilisateur dès qu'il existe, même avec des espaces réservés, et dites ce qui est un espace réservé.
Étape 7. Son
scripts/export-curves.mjs écrit curves.json depuis la page : débuts de plaques, les repères (cues), et pour
chaque image d'un film monde la vitesse et la position à l'écran de la tête de ligne, dans un film peinture
les parts révélées, la vie, la rafale (gust) et le zoom. audio.mjs prend sa chronologie de là, donc il ne
dérive jamais de l'image (une page sans RISO.curves() a besoin des débuts de look.mjs info
recopiés dans T à la main). Écrivez un bloc d'événements par plaque en utilisant la carte de repères dans
references/audio.md ; les frappes sur quelque chose que l'image marque prennent leur temps depuis les repères de la page,
cue('nom') (CUES, ou W.cues dans un film monde), pas depuis des numéros de temps copiés d'une plaque ;
dans un film monde, donnez à la ligne ou au personnage une voix qui suit les courbes (follow(),
references/audio.md, section 7). Une peinture ou un paysage reçoit une ambiance nature au lieu de
rythmes : vent, eau, bruissement et oiseaux depuis nature.mjs, synchronisés aux repères et à la rafale (gust) de la page
par le audio.mjs que --painting copie depuis assets/audio-nature.mjs (references/audio.md,
section 8, Ambiance nature). Ensuite :
node scripts/export-curves.mjs curves.json
node audio.mjs track.wav
ffmpeg -i track.wav -filter_complex "showwavespic=s=1800x300:split_channels=1" -frames:v 1 shots/wave.png
bash scripts/build.sh out.mp4
Vous ne pouvez pas écouter, donc lisez la forme d'onde : pas de silence accidentel dans les scènes actives, pas de scène collée au plafond. Chaque fois qu'une longueur de scène change, régénérez la piste.
Étape 8. Livraison
Livrez out.mp4 (1920×1080, H.264, AAC, ~0,7 Mo par seconde sur les styles complexes) et dites comment
reconstruire : bash scripts/make.sh [photo.jpg] [seed] [largeur]. Proposez, n'imposez pas :
une autre seed (une impression différente des mêmes plaques), une coupe verticale
(AR=9:16 node scripts/render.mjs frames-v 7 1080 5, puis vérifiez une planche avec AR=9:16 ; les
sous-titres inférieurs se situent à 70 % de la hauteur là, TITLE_Y, hors des boutons de la plateforme ; un
…