Que fait motionkit ?
Un film est une page plus un script dont renderFrame(f) dessine l'image f (30 par seconde, 1920x1080) et rien d'autre. Chrome caché parcourt les images, ffmpeg encode (le mp4 est doublé à 60 images par seconde en haute qualité, environ 4 à 5 Mo par seconde de film), la bande-son est synthétisée.
Où se trouvent les éléments
- Moteur : le dossier
engine/à côté de ce fichier (mouvements, rendu, son, vérifications, polices). Appelez-le$Eet utilisez son chemin complet. Lisez-le ; n'y mettez jamais de films (l'installation ajoute son proprenode_modules, ce qui est normal). - Dossier de travail : le dossier dans lequel l'utilisateur travaille. Les films (
<nom>.html,<nom>.js),assets/,ref/etout-<nom>/y vont tous, et chaque commande est exécutée depuis ce dossier. Une page trouvelib.js,moves.jsetfonts/dans le moteur par elle-même. Si le dossier de travail est un dépôt git, ajoutezout-*/etref/à son.gitignore.
D'abord : configuration (une fois par machine)
- Exécutez
sh $E/setup.sh. Il vérifie seulement : bun, ffmpeg avec ffprobe, python3, uv, un navigateur Chromium (Chrome, Chromium ou Edge ;CHROME_PATHpermet de le surcharger) et le seul paquet du moteur. - S'il affiche
Prêt, continuez. Si seul lepaquet du moteurmanque (normal sur une nouvelle installation), exécutezsh $E/setup.sh --installsans demander : il ne récupère qu'un seul paquet dans le dossier de la compétence. Si un outil ou le navigateur estMANQUANT, montrez cette liste à l'utilisateur ainsi que les commandes d'installation affichées, et demandez une fois avant d'installer des logiciels sur sa machine. - En cas d'acceptation, exécutez
sh $E/setup.sh --install(Homebrew sur macOS, les installateurs officiels pour bun et uv), puis relancez la vérification. Les lignes indiquantexécutez ceci vous-mêmenécessitent le mot de passe de l'utilisateur (sudosur Linux) ou un téléchargement manuel : ne les exécutez jamais ; donnez la commande exacte à l'utilisateur et attendez. Si une installation échoue, donnez la commande à exécuter par l'utilisateur. - Prouvez que cela fonctionne : depuis le dossier de travail, exécutez
bun $E/build.ts helloet confirmez queout-hello/hello.mp4existe.
Testé uniquement sur macOS ; dites-le si l'utilisateur est sur Linux ou Windows.
Exemples de travaux à copier : $E/hello.js (un film sur une grille de battements, chaque son provenant de clips, décors dessinés en code), $E/title.js (un titre transparent à superposer à des images), $E/moves-demo.js (chaque mouvement une fois).
Construire un film : d'abord les clips
Déclarez chaque utilisation d'un mouvement une fois sous forme de clip. L'image, le son et la vérification du temps de lecture proviennent tous de cette seule ligne.
const M = window.MOVES, B = M.brand, G = M.grid(120); // 120 bpm : un battement fait 15 images, une mesure 60
const K = { line: M.clip("words", G.bar(1) + 2, 10, { text: "Copie le mouvement.", x: 960, y: 590, size: 110, fam: B.display, stretch: B.wide, weight: 600, color: B.paper, until: G.bar(2) }) };
// dans une scène : K.line.draw(X, f) K.line.on(f) est vrai tant qu'il doit être visible
window.timeline = () => ({ fps: 30, total: TOTAL, bpm: G.bpm, sections: [[0, "intro"], [4, "construction"], [8, "drop"], [24, "outro"]], cues: M.cues(), captions: M.captions() });
Pour un film de lancement ou produit, lisez d'abord « Films de lancement et produit » ci-dessous. Commencez un film avec bun $E/new.ts <nom> : cela écrit <nom>.html et <nom>.js dans le dossier de travail, copiés depuis l'exemple hello. Réécrivez les clips, les scènes et le texte. bun $E/build.ts <nom> fait ensuite, dans l'ordre : timeline, bande-son (sound.py), vérification du son, vérification du temps de lecture (lint.py), rendu, assemblage, vérification de la synchronisation (sync.py), et écrit out-<nom>/<nom>.mp4. Une timeline sans bpm reçoit une bande-son plus simple de audio.py (uniquement des coupures). Options de timeline : sound: "fx" (sons des mouvements uniquement, pas de musique), sound: "none", alpha: true (fond transparent, sortie .mov ; voir la section EditMax).
Anatomie d'un script de film
$E/hello.js est le modèle ; lisez-le une fois. Ce qu'est chaque partie :
- Globaux depuis
lib.js:W,H(1920, 1080),X(le contexte canvas),clamp,lerp,TAU,hash,noise,txt(ctx, text, x, y, size, weight, color, fam, italic, align),rr(rectangle arrondi),vignette(strength, innerRadius). M.clip(move, start, length, options):startetlengthsont en images ;lengthest la durée que met le mouvement à jouer, pas combien de temps il reste. Il retourne{ draw(ctx, f), t(f) (0..1 pendant le mouvement), on(f), o (les options) }. Après sa durée, un clip continue de dessiner son état final : il ne se cache pas. Cachez-le en terminant la scène, ou avecif (K.x.on(f)), oùonest vrai destartjusqu'àuntil(indéfiniment sansuntil). Pour afficher un mouvement de texte terminé à l'intérieur d'unfromd'une transition, dessinez-le àt = 1:M.words(ctx, 1, K.line.o). Les effets ponctuels (burst,glitchCut) nécessitent égalementK.x.t(f) < 1.- Formes d'appel à l'intérieur d'un clip : les transitions prennent
fromettodans les options ; les wrappers (punch,shake,sweep,smear) prennentdraw. Tout le reste (pathDraw,burst,bars,chart,callout,logoDraw,bounce,ticker) ne prend que des options.meshetparallaxsont des arrière-plans : appelez-les directement,M.mesh(ctx, f / 240, { colors }), oùtest juste un nombre qui croît lentement. - Options de texte :
xest le centre de la ligne (align: "center"par défaut),yla ligne de base.stretch: B.widedonne la coupe large,stretch: nullla désactive. Rien ne mesure ni n'enveloppe le texte pour vous : à taille 150 large, 13 caractères couvrent environ 1 340 px, donc effectuez un rendu fixe et vérifiez que chaque ligne se trouve à l'intérieur du cadre avec une marge. - Tableau des scènes :
[première image, draw(f), drawAfterFinish(f)].renderFrameréinitialise l'état du contexte (gardez cette ligne), dessine la scène actuelle, puis le grain et la vignette, puis le troisième emplacement pour tout ce qui doit rester propre (logos). window.ready: une promesse que le moteur de rendu attend :Promise.all([M.fonts(), M.levels("out-<nom>/levels.json"), M.load({ ... })]).M.levelsn'est nécessaire que pourM.eqet est sans danger avant que le fichier n'existe.- Timeline :
sectionssont[battement, nom]; un film court peut utiliser juste[[0, "intro"], [8, "drop"], [16, "outro"]], et chaque début dedropreçoit le demi-battement silencieux (gardez oumute:les clips là).syncliste les images où une coupe et un coup de basse doivent coïncider : utilisez la première image de chaque scène à partir du drop. Le « changement » est jugé par rapport à la différence image par image habituelle du film, donc une transition qui se termine exactement sur une image de sync ne laisse rien à changer et la vérification affichePAS DE CHANGEMENT: ajoutez un court flash ou démarrez le mouvement suivant sur cette image.cutscorrespond à chaque début de scène. - Couleurs : lisez tout depuis
M.brand(B).M.rgba(B.ink, 0.7)donne une couleur à une opacité,M.mix(a, b, t)une couleur entre deux.M.setBrandmodifieM.brandsur place ; appelez-le avant toute déclaration de clip et définissez aussiaccentOnDark(l'accent utilisé sur les fonds sombres ; il ne suit pasaccent). Pour une police sans coupe large définissezwide: null. - Durée : le temps de lecture fixe le minimum : une ligne de 3 mots nécessite environ 1,3 s après son apparition, donc une ligne par mesure à 120 bpm. La bande-son dure environ une demi-seconde après la dernière image et y est coupée ; terminez par une section
outropour que la coupe tombe dans sa queue. - Résultats de construction :
build.tsquitte avec 0 même en cas d'échec d'une vérification ; les échecs s'affichent sous forme de lignesATTENTION :avec le détail au-dessus. Traitez chaque avertissement comme une erreur : lisez la sortie (filtrez les lignes de progressionimage n/total), corrigez, reconstruisez jusqu'à ce qu'il n'y en ait plus. Les images fixes sont écrites sous formeout-<nom>/fNNNN.jpg; lisez-les.bun $E/pure.ts <nom>confirme que les images se rendent de la même manière seules et après d'autres images. sound.pya besoin de numpy et est toujours exécuté viauv run; les autres scripts Python n'utilisent que la bibliothèque standard.
Films de lancement et produit : montrer le produit
Un film composé de grands mots sur un fond semble basique. Les films de lancement qui fonctionnent gardent le produit à l'écran dans un monde visuel unique avec de la profondeur et une caméra qui se déplace lentement, utilisent un texte court et discret, et tiennent les plans pendant 2 à 6 secondes (quelques mesures), pas une coupe par battement. Pour un film de lancement ou produit, ne commencez PAS à partir de hello :
- Décidez d'abord du look : un monde d'arrière-plan, une couleur d'accent, une famille de polices. Dites-le à l'utilisateur en deux lignes.
- Storyboardez 6 à 8 plans de choses réelles : l'écran réel du produit ou sa sortie, des commandes réelles, du code réel, des résultats réels. Ne dessinez jamais une interface que le produit n'a pas, n'inventez jamais de chiffres et n'utilisez jamais le logo d'une autre entreprise. Les légendes sont de petites étiquettes (
txt, mono, en bas à gauche), pas des titres. - Construisez d'abord un plan héroïque, faites le rendu d'images fixes et comparez-le avec un vrai film de lancement avant de construire le reste.
La page doit charger stage.js après moves.js (les pages créées par new.ts le font). Commencez renderFrame par M.frame(); M.reset(X);. Pièces de scène (toutes sur MOVES) :
| pièce | description |
|---|---|
backdrop(ctx, f, { base, lights[], streak }) | quasi-noir avec des nappes de lumière lentes : le monde dans lequel tout se trouve |
glass(ctx, x, y, w, h, { r, top, bottom, rim, shadow }) | un panneau arrondi sombre avec un bord lumineux et une ombre douce |
camera(ctx, cam, draw) avec cam(a, b, t, ease) | regarde un point d'une scène plus grande : { x, y, zoom, rot } ; commencez en macro-rapproché et reculez, ou dérivez lentement |
tilt(ctx, draw, { depth, focus, blur, fog, top, height, width, cx, roll }) | dessine une scène plein cadre comme un plan incliné, le bord lointain plus petit, plus sombre et flou |
tiles(ctx, f, { cells: [{ draw(ctx, t), label, bg, len, hold }], cols, x, y, w, gap }) | une grille de petits écrans jouant chacun un mouvement en direct (ne mettez jamais de transition à l'intérieur d'une tuile) |
term(ctx, f, { x, y, size, lines: [{ text, at, speed, prompt, mark: "ok" ou "run", color }] }) | lignes de terminal qui tapent ou apparaissent sur leur image |
code(ctx, { lines[], x, y, size, lead, hot, shown }) | JavaScript avec une coloration syntaxique discrète et une ligne éclairée |
wave(ctx, { peaks, x, y, w, h, played, beats[], blocks[] }) | la forme d'une vraie bande-son avec une tête de lecture, des ticks de battement et des clips sur une piste |
Les pièces de scène ne produisent aucun son par elles-mêmes : ajoutez manuellement des signaux à la timeline, { f, kind, gain } avec des sortes comme type, tick, pop, ding.
Couper sur une vraie chanson
- L'utilisateur fournit le fichier musical et doit en avoir le droit d'utilisation ; dites-le, et mettez le crédit demandé par une licence sur le carton de fin. Ne téléchargez jamais une chanson depuis un site de streaming.
uv run $E/beats.py <fichier>affiche le tempo et le volume de chaque mesure. Choisissez un extrait qui commence sur une mesure avec un changement de section quelques mesures plus tard, puisuv run $E/beats.py <fichier> --from <secondes> --len <secondes> --out beats.jsonécritbeats.jsonetbeats.js.- Chargez
beats.jsdans la page avant le script du film. Écrivez chaque début de scène comme un numéro de battement :const b = (n) => window.BEATS.frames[n]. Coupez sur les mesures (tous les 4 battements), là où le morceau change. - Dans la timeline, définissez
music: { file, from },sound: "fx"(les sons des mouvements restent, discrètement, sous la musique) ou"none", unbpm, etsync: [images des coupes réelles]. La construction mixe la piste à -16 LUFS, atténue sa fin et vérifie que chaque coupe a un coup de basse à une image près. N'ajoutez pas de flashs juste pour passer cette vérification.
Motions pour EditMax (un clip sur la timeline d'un éditeur vidéo)
Utilisez ceci lorsque l'utilisateur édite dans EditMax et souhaite un mouvement dans la coupe : un titre animé sur des images, un carton d'intro ou d'outro, un battement de type cinétique entre les plans. Cela nécessite deux choses dans la même session d'agent : un shell (pour le rendu) et les outils MCP d'EditMax (inspect_timeline, import_media, ... ; configurés depuis l'onglet Agents d'EditMax). Le chat intégré d'EditMax refuse les commandes shell, il ne peut donc pas exécuter cette compétence : dites-le et demandez à l'utilisateur de connecter un agent tel que Claude Code. Si les outils EditMax manquent ou ne répondent pas, effectuez quand même le rendu du clip et donnez à l'utilisateur le chemin du fichier à glisser-déposer.
1. Choisissez la voie. Préférez les outils natifs d'EditMax lorsqu'ils couvrent le mouvement, car le texte reste modifiable dans l'application :
| souhaité | utiliser |
|---|---|
| un titre ou un tiers inférieur qui apparaît, glisse, surgit, tape ou se dissout | add_text / add_lower_third + set_text_animation |
| une transition de fondu, poussée, glissade, volet ou iris entre deux clips | set_transition |
| un clip qui bouge, se redimensionne ou s'estompe | set_keyframe, set_clip_transform, set_clip_fades |
| tout le reste : claque, scramble, split-flap, roue de mots, barré, surlignage, tracé ou dessin de logo, graphiques, compteurs, explosions, séquences multi-mouvements, une intro ou outro complète | effectuer le rendu d'un clip avec cette compétence |
2. Lisez le projet. inspect_timeline : notez la taille du cadre, la fréquence d'images, les pistes et le moment où le mouvement se produit. Ce moteur effectue le rendu en 1920x1080 à 30 images par seconde. Un projet 16:9 d'une autre taille le mettra à l'échelle proprement. Pour un projet vertical ou carré, dites clairement que le clip sera letterboxé ou devra être agrandi et recadré avec set_clip_transform, et qu'un titre natif pourrait être préférable.
3. Effectuez le rendu.
- Superposition (se place au-dessus des images) :
bun $E/new.ts <nom> --overlay, modélisé sur$E/title.js. La timeline aalpha: trueetsound: "fx"; la construction écritout-<nom>/<nom>.mov(ProRes 4444 avec fond transparent). Règles : effacez le cadre (X.clearRect), ne le remplissez jamais ; pas degrain,vignette,mesh,parallaxni de transitions plein cadre ; mettez une ombre douce ou un fond sous le texte pour qu'il soit lisible sur n'importe quelle image ; gardez le texte dans les 90 % centraux du cadre ; estompez-le à la fin. - Plein cadre (intro, outro, un carton entre les plans) :
bun $E/new.ts <nom>, modélisé sur$E/hello.js; la construction écritout-<nom>/<nom>.mp4. - Son (définissez
sound:dans la timeline) : laissez-le de côté pour la bande-son complète (une intro ou outro seule) ;"fx"pour uniquement les sons que les mouvements produisent (tout ce qui passe sur des images, de la parole ou la musique de l'utilisateur) ;"none"pour le silence. Demandez ce que l'utilisateur souhaite si le montage a déjà de la musique. - Construisez, corrigez chaque
ATTENTION, et lisez les images fixes avant de continuer.
4. Placez le fichier dans un endroit qui dure. EditMax fait référence aux fichiers importés, il ne les copie pas. Copiez le fichier terminé depuis out-<nom>/ vers un dossier que l'utilisateur conserve (demandez une fois ; suggérez ~/Movies/Motions/), et importez depuis là.
5. Placez-le sans déplacer la coupe de l'utilisateur. insert_clip avec un temps est une déchirure...