Que fait Opus Visual Motion Engine ?
Un pipeline discipliné pour les livrables visuels : planifier → construire à partir du template → rendre → QA → corriger → exporter. La qualité provient des contrôles appliqués avant et après le rendu, pas du goût. Suivez les étapes dans l'ordre. Ne sautez pas une étape parce que la tâche "a l'air simple".
Les chemins ci-dessous sont relatifs au répertoire de cette compétence (la base skill_resources qui vous a été donnée). Appelez-la $SKILL.
0. Lire d'abord (inconditionnel)
Avant d'écrire du code, lisez les références qui correspondent au livrable. Elles sont faisant autorité pour leur sujet :
| Livrable | Lire |
|---|---|
| vidéo, motion graphique, explainer, vidéo produit | references/video-generation.md, references/motion-design.md, references/javascript-animation.md |
| typographie cinétique / pièce axée sur le texte | l'ensemble vidéo ci-dessus + references/typography.md |
| page d'atterrissage, tableau de bord, maquette, UI | references/visual-design.md, references/typography.md |
| diagramme, schéma, dataviz | references/visual-design.md §4 |
| décisions de cadrage / caméra | references/cinematography.md |
| durées et easing | references/animation-primitives.md |
| musique ou voix à synchroniser | SKILL.md §10 + references/video-generation.md §6 |
Des exemples concrets se trouvent dans examples/*.md. Lisez celui qui se rapproche le plus du brief.
1. Planifier avant le code (écrire le plan)
Écrivez le plan dans votre réponse (ou dans un fichier PLAN.md à côté du résultat) avant de créer le HTML. Un plan qui n'existe que dans votre tête ne compte pas.
UI / page
- Message principal : une phrase. Que doit comprendre le spectateur en 3 secondes ?
- Direction nommée : un nom, 4 à 6 tokens hexadécimaux nommés avec leurs significations attribuées, polices avec rôles, traitement de forme. Dérivez-la du secteur du sujet, des matériaux et du langage. Un magasin de jouets et un tableau de bord fintech ne partagent jamais une direction.
- Hiérarchie : étiquetez chaque bloc PRIMARY / SECONDARY / TERTIARY. Seul PRIMARY obtient le geste audacieux.
- Progression des sections : une ligne par section, donnant son travail et comment elle passe la main à la suivante.
- Mathématiques pré-rendu : largeur du texte ≈
chars × 0.55 × font-size(sans) ou× 0.5(serif) ; largeur de la boîte =max(title_chars × 8, subtitle_chars × 7) + 24à 14px ; ajustement des lignes = Σlargeurs + écarts ≤ conteneur. Tracez chaque connecteur par rapport à chaque boîte. S'il en traverse une, tracez un chemin en forme de L.
Vidéo / motion graphique
- Message central : une phrase. S'il en faut deux, il y a deux vidéos.
- Temps forts : 3 à 7. Chaque temps fort répond à "que sait maintenant le spectateur ?"
- Fiche scène par temps fort : début/fin (s), composition, caméra, tailles de police, primitives (2–4), sortie. Utilisez le modèle dans
references/motion-design.md§2. - Moment signature : nommez l'UNIQUE image où le mécanisme est montré, pas étiqueté (par exemple, le bruit filtré en signal). Si l'histoire ne fonctionne qu'avec ses légendes, reconcevez le temps fort.
- Chronologie globale : horodatages absolus. La SORTIE d'une scène se termine avant que la scène suivante ne réclame l'espace. Définissez
DURATIONsur la vraie fin (10–20s par défaut ; 30–45s uniquement avec narration).
Une scène sans fiche n'a pas sa place dans la vidéo.
2. Construire à partir du modèle (vidéo)
Copiez assets/composition-template.html à côté du livrable et remplissez les blocs EDIT. Ne réécrivez pas les blocs ENGINE. Ils fournissent déjà :
- une
#stage1920×1080 (changezSTAGE_W/Hpour 1080×1920 ou 1080×1080), adaptée à n'importe quelle fenêtre ; window.DURATION,window.seek(t),window.render(t),window.SCENES: le contrat de capture/export (le QA capture chaque scène à partir deSCENES) ;at(),ease.*,lerp,mulberry32(PRNG ensemencé), etlayer()pour la visibilité des scènes plus EXIT ;- les assistants audio
audioAt(t, "rms"|"low"),since(t, list),hit(t, list, dur)qui lisentwindow.AUDIO(§10) et renvoient 0 sans lui ; - un lecteur de prévisualisation (espace, ←/→, Home/End) qui se désactive automatiquement en mode capture et mouvement réduit ;
- des tokens de couleur dans les 3 couches requises (clair, protégé par
prefers-color-scheme,[data-theme]).
Pour les pages, écrivez un fichier HTML unique normal suivant §6.
3. Temps déterministe (vidéo)
render(t)est le seul moteur d'état. Chaque position, opacité, taille, nombre et dashoffset est calculé à partir deten secondes.seek(x)rend l'image identique, quel que soit ce qui a été rendu avant.test en secondes. Limiteztà[0, DURATION], jamais à[0, 1]: limiter les secondes à 0..1 fige silencieusement toute la chronologie.- Interdit :
setTimeout,setInterval, animations/transitions CSS comme chronologie,Math.random()non ensemencé,Date.now()dans l'état.requestAnimationFramene peut avancer que l'horloge de lecture et appelerrender(clock). Le modèle fait déjà cela. - Aléatoire : générez au moment de la construction à partir de
mulberry32(seed), jamais à l'intérieur derender. - Le dessin SVG nécessite les trois :
pathLength="1",stroke-dasharray="1", etstroke-dashoffsetde 1 à 0. Sans dasharray, la ligne apparaît entièrement dessinée dès l'image 0. - Les spans
inline-blockde mots absorbent les espaces de fin. Mettez l'espacement dansmargin-right.
4. Grammaire du mouvement
Cinq mouvements, chacun avec un seul travail. Si un mouvement n'est aucun de ceux-ci, supprimez-le.
- ENTER : une séquence orchestrée par page ou scène, dans l'ordre de lecture, les frères et sœurs décalés de 60 à 120ms. Un fondu vers le haut sur chaque section est le signe révélateur de l'IA.
- EMPHASIS : un élément à la fois : une augmentation d'échelle (≤1.2), une lueur ou un changement de couleur qui a du sens.
- TRANSITION : un changement d'état. La partie modifiée bouge ; tout le reste reste immobile. 0.2–0.6s.
- CAMERA : une échelle/translation au niveau de la scène, au maximum une fois par scène, 0.8–1.2s, jamais pendant une lecture de texte.
- EXIT : 0.3–0.5s, entièrement terminé. Aucun élément ne survit à mi-état à travers une limite de scène, sauf un point d'ancrage de continuité délibéré qui persiste de manière identique ou morphose (le même objet changeant d'état, qui est la transition la plus forte disponible).
Les transitions ont une signification : coupe franche = changement de catégorie, morphose = même chose qui change, panoramique = traversée. Jamais un fondu enchaîné par défaut partout. Les durées et les easings se trouvent dans references/animation-primitives.md. Les voisins qui bougent ensemble reçoivent des durées différentes ou un décalage.
5. Décision de pile
| Situation | Pile |
|---|---|
| micro-mouvement de page | Transitions CSS sur transform/opacity, 0.2–0.6s ; boucles ≤2s dans @media (prefers-reduced-motion: no-preference) |
| vidéo / motion graphique / typo cinétique | modèle + vanilla render(t) (par défaut) |
| >6 pistes décalées ou orchestration de morphose SVG | GSAP UMD épinglé, UNE chronologie maîtresse en pause, seek(t) → tl.seek(t) |
| graphiques | SVG en ligne (par défaut) ou Chart.js UMD avec animation:false, couleurs hex (le canvas ne peut pas lire les variables CSS) |
| art génératif | p5.js avec randomSeed/noiseSeed |
| 3D, uniquement quand la 3D est le message | Three.js UMD épinglé ; caméra scriptée dans render(t) |
Utilisez la pile la plus simple qui atteint le résultat. Les scripts externes doivent être des versions épinglées exactes provenant de cdnjs.cloudflare.com ou cdn.jsdelivr.net. Tout le reste en ligne. Chaque police obtient une vraie pile de secours.
6. Base technique (non négociable)
- Un seul fichier
.htmlavec CSS et JS en ligne, sous 16 Mo, images/données sous forme d'URIdata:. - Couleurs en tant que tokens
:root; couche sombre sous@media (prefers-color-scheme: dark)protégée par:root:not([data-theme="light"])et répétée sous:root[data-theme="dark"]; fondbodyexplicite. meta viewportavecviewport-fit=cover; remplissageenv(safe-area-inset-*);height:100%sur html/body, jamais100vh; les blocs larges défilent à l'intérieur de leur propreoverflow-x:auto; les grilles utilisentminmax(0,1fr).- Les nombres calculés affichés passent par
Math.round,toFixedouIntl.NumberFormat. Les compteurs arrivent exactement sur la valeur finale. - Accessibilité : contraste ≥4.5:1 (≥3:1 pour un texte ≥24px), focus visible,
prefers-reduced-motionrespecté (le modèle montre l'image finale stabilisée). - Typographie lisible : dans une image vidéo 1080p, aucun texte en dessous de 20px (H/54). Sur les pages mobiles, aucun texte de corps en dessous de 14px.
- Le stockage du navigateur ne contient jamais l'état de la chronologie. Là où le stockage est utilisé, enveloppez-le dans un try/catch.
7. Éviter le design générique de l'IA
Là où le brief laisse la liberté, ne la dépensez jamais sur ces valeurs par défaut (qa.mjs en signale plusieurs automatiquement) :
| Signe | Correction |
|---|---|
crème #F4F1EA-ish + police serif d'affichage + accent terre cuite #D97757-ish | une palette tirée des propres matériaux du sujet |
| presque noir + un accent acide, utilisé indépendamment du sujet | autorisé uniquement lorsque la direction le justifie par écrit |
| cartes arrondies identiques, un rayon partout, la même ombre douce | tokens de rayon par hiérarchie ; carte uniquement pour les objets délimités |
| dégradés comme décoration, dégradés violets, glassmorphisme | remplissages plats ; un dégradé max, uniquement pour une propriété physique continue |
| sourcils tout en majuscules suivis, chaînes méta avec point médian, "→" sur les liens | casse de phrase ; supprimez les étiquettes qui répètent le contenu |
| Inter / police système par défaut | polices choisies pour ce sujet, avec une pile de secours |
| un seul mot accentué dans un titre | autorisé UNIQUEMENT lorsque la couleur d'accent est un token sémantique utilisé pour la même signification ailleurs dans la pièce (par exemple, corail = bruit) |
| héro générique : titre + sous-titre + CTA + capture d'écran | ouvrez avec la chose la plus caractéristique dans l'univers du sujet |
| tout centré, même rythme de grille à chaque section | un système d'alignement par section ; variez le rythme |
| tout anime en même temps, 300ms partout | la carte des durées + décalage (§4) |
Dépensez l'audace à un endroit, puis retirez un accessoire.
8. Rendu + QA (obligatoire avant la livraison)
Exécutez le QA automatisé. Il rend le fichier sans tête et écrit les preuves plus un verdict. Il nécessite Node ≥18. La première exécution dans une copie fraîche de la compétence nécessite npm install dans $SKILL/scripts (sautez-le si scripts/node_modules existe).
node "$SKILL/scripts/qa.mjs" chemin/vers/livrable.html
Dans DSH sur Windows, exécutez-le avec l'outil pwsh en utilisant la même commande. Options : --step 0.25 (une grille plus fine pour les scènes rapides), --out <dir>, --mode page|video.
Mode vidéo (automatique quand DURATION + seek existent) capture une image à chaque pas et vérifie : les API interdites, le déterminisme sur les revisites dans le désordre, une chronologie figée, le silence, les étendues presque vides, une image de fin instable, les collisions de texte, le texte sur d'autres graphiques, le clipping, le contraste, la taille de police minimale, les lignes dash-offset qui ne se dessinent jamais, les boucles en mouvement réduit, les erreurs de console/page et les signes du modèle.
Mode page rend le bureau 1440, le mobile 390 et le thème sombre, et vérifie les mêmes règles de mise en page plus le débordement horizontal mobile.
Les résultats atterrissent dans qa-<nom>/ : report.md (verdict et résultats), report.json (couverture d'encre par image et changement), contact-sheet.png (toutes les images sur une seule image), moments.png (l'entrée, le milieu et la fin de chaque scène, pris à partir de window.SCENES), key_000..100.png et frames/.
- Passez le moment signature et tout point de synchronisation explicitement :
--at 5.4,9.1. - La superposition délibérée (typographie cinétique empilée volontairement, un titre sur des images) va à l'intérieur d'un élément avec
data-qa-allow-overlap. Cela ignore les vérifications de collision et de texte sur les graphiques pour cette sous-arborescence uniquement. Ne l'utilisez jamais pour faire taire une collision accidentelle. - Un avertissement
fontsignifie que la police de première intention ne s'est pas chargée et que le texte est dans une police de secours. Corrigez-le avant de juger la mise en page, car toutes les largeurs sont erronées.
Code de sortie 0 = aucun ÉCHEC. L'ÉCHEC est bloquant. Chaque avertissement est soit corrigé, soit justifié en une ligne dans votre critique.
9. Boucle de critique
- Si votre modèle peut lire des images : ouvrez
contact-sheet.png, puis toutekey_*.pngqui semble erronée. Jugez les pixels, pas vos intentions. - Si votre modèle est textuel uniquement : utilisez
report.mdplus le tableau par image dansreport.json.coverage< 0.015 signifie que l'image est vide. Unediffde 0 sur une scène signifie que rien ne bouge. Un pic dansdiffmarque une coupe. Vérifiez les horodatages de chaque fiche de scène par rapport à ces nombres. - Répondez à chaque question avec une observation concrète, pas "✓" :
- Composition : à chaque image clé, où l'œil atterrit-il en premier ? Est-ce le message ?
- Hiérarchie : PRIMARY est-il indubitable ? Y a-t-il exactement une chose en mouvement qui compte par image ?
- Mécanisme : le moment signature montre-t-il l'idée avec les légendes retirées ?
- Continuité : toutes les transitions sont-elles propres, sans rien à mi-état à une limite et les ancres identiques ou en morphose ?
- Timing : y a-t-il des décalages, pas de durées par défaut, et un temps de lecture ≥1.2s pour 8 mots ?
- Typographie / contraste / densité : tailles selon le plan, ≤5 éléments concurrents par image, lisible en mode sombre ?
- Narratif : un spectateur pourrait-il répéter le message central après un visionnage ?
- Corrigez → réexécutez
qa.mjs→ re-critiquez. Répétez jusqu'à ce que le QA réussisse et que la critique n'ait aucun élément ouvert. - Question finale, répondue honnêtement : "Cela a-t-il l'air intentionnellement conçu, ou simplement généré ?" Si "généré" : nommez ce qui est générique, révisez le plan (§1) et reconstruisez. Un premier rendu n'est presque jamais le livrable. Attendez-vous à 2-3 générations.
10. Synchronisation audio (quand il y a de la musique ou une voix)
Ne devinez jamais le timing à l'oreille. Convertissez l'audio en données, et laissez render(t) les lire :
node "$SKILL/scripts/audio-data.mjs" music.mp3 --inject composition.html # écrit window.AUDIO dans le HTML
AUDIO.beats(une grille uniformément espacée au tempo estimé),AUDIO.onsets(frappes, notes, syllabes), et par imageAUDIO.rms/AUDIO.low(0..1 ;lowest la bande de kick/basse < 150 Hz).- Pilotez le mouvement avec les assistants du modèle :
hit(t, AUDIO.beats, 0.2)pour une impulsion de battement (EMPHASIS),audioAt(t, "low")pour une échelle réactive au kick,since(t, AUDIO.onsets)pour des révélations par frappe. Placez les coupes de scène sur les battements : choisissez les débuts de scène à partir deAUDIO.beats. - La synchronisation est exacte à l'image près par construction : une impulsion atterrit sur la première image à ou après son battement (≤1 image, ≤33 ms à 30 ips). Si l'audio commence plus tard que t=0, passez
--offset <s>. - Les niveaux sont vérifiés avant que quiconque n'écoute. Les échantillons écrêtés sortent avec le code 1. Un pic d'échantillon au-dessus de −1 dBFS avertit, car l'encodage AAC va l'écrêter. La sonie en dehors de −18..−12 LUFS est signalée (les plateformes normalisent à environ −14).
--bpmremplace...