Communitygithub.com

motionkit

Créez des films de motion graphics en code avec le moteur canvas motionkit – copiez le mouvement (timing, disposition, déplacement) d'une vidéo de référence avec vos propres mots et illustrations, ou construisez un nouveau film calé sur un rythme à partir de 44 mouvements prêts à l'emploi avec une bande-son synthétisée.

Qu'est-ce que motionkit ?

**motionkit** est un outil permettant de créer des films de motion graphics entièrement en code, à l'aide d'un moteur canvas. Il permet de reproduire le mouvement (timing, disposition, animation) d'une vidéo de référence en remplaçant le texte et les visuels, ou de construire un film original calé sur un rythme (BPM) avec 44 mouvements prêts à l'emploi et une bande-son synthétisée. Il peut également générer des titres et incrustations animés avec fond transparent, et les placer sur une timeline EditMax via les outils MCP d'EditMax. L'outil s'exécute en local via un shell et nécessite un navigateur Chromium, ffmpeg, bun et Python. Il a été testé principalement sur macOS. Il est idéal pour les commandes comme « clone cette vidéo », « copie ce mouvement », « crée un reel comme ça », « ajoute un titre animé / intro / outro à mon montage », « ajoute ce mouvement », « corrige le son », ou pour tout nouveau film en motion.

Compatible avec✓Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/SkidGod4444/motionkit/tree/HEAD/skills/motionkit

Demander à votre IA préférée

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

Documentation

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 $E et utilisez son chemin complet. Lisez-le ; n'y mettez jamais de films (l'installation ajoute son propre node_modules, ce qui est normal).
  • Dossier de travail : le dossier dans lequel l'utilisateur travaille. Les films (<nom>.html, <nom>.js), assets/, ref/ et out-<nom>/ y vont tous, et chaque commande est exécutée depuis ce dossier. Une page trouve lib.js, moves.js et fonts/ dans le moteur par elle-même. Si le dossier de travail est un dépôt git, ajoutez out-*/ et ref/ à son .gitignore.

D'abord : configuration (une fois par machine)

  1. Exécutez sh $E/setup.sh. Il vérifie seulement : bun, ffmpeg avec ffprobe, python3, uv, un navigateur Chromium (Chrome, Chromium ou Edge ; CHROME_PATH permet de le surcharger) et le seul paquet du moteur.
  2. S'il affiche Prêt, continuez. Si seul le paquet du moteur manque (normal sur une nouvelle installation), exécutez sh $E/setup.sh --install sans demander : il ne récupère qu'un seul paquet dans le dossier de la compétence. Si un outil ou le navigateur est MANQUANT, 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.
  3. 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 indiquant exécutez ceci vous-même nécessitent le mot de passe de l'utilisateur (sudo sur 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.
  4. Prouvez que cela fonctionne : depuis le dossier de travail, exécutez bun $E/build.ts hello et confirmez que out-hello/hello.mp4 existe.

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) : start et length sont en images ; length est 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 avec if (K.x.on(f)), où on est vrai de start jusqu'à until (indéfiniment sans until). Pour afficher un mouvement de texte terminé à l'intérieur d'un from d'une transition, dessinez-le à t = 1 : M.words(ctx, 1, K.line.o). Les effets ponctuels (burst, glitchCut) nécessitent également K.x.t(f) < 1.
  • Formes d'appel à l'intérieur d'un clip : les transitions prennent from et to dans les options ; les wrappers (punch, shake, sweep, smear) prennent draw. Tout le reste (pathDraw, burst, bars, chart, callout, logoDraw, bounce, ticker) ne prend que des options. mesh et parallax sont des arrière-plans : appelez-les directement, M.mesh(ctx, f / 240, { colors }), où t est juste un nombre qui croît lentement.
  • Options de texte : x est le centre de la ligne (align: "center" par défaut), y la ligne de base. stretch: B.wide donne la coupe large, stretch: null la 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)]. renderFrame ré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.levels n'est nécessaire que pour M.eq et est sans danger avant que le fichier n'existe.
  • Timeline : sections sont [battement, nom] ; un film court peut utiliser juste [[0, "intro"], [8, "drop"], [16, "outro"]], et chaque début de drop reçoit le demi-battement silencieux (gardez ou mute: les clips là). sync liste 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 affiche PAS DE CHANGEMENT : ajoutez un court flash ou démarrez le mouvement suivant sur cette image. cuts correspond à 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.setBrand modifie M.brand sur place ; appelez-le avant toute déclaration de clip et définissez aussi accentOnDark (l'accent utilisé sur les fonds sombres ; il ne suit pas accent). Pour une police sans coupe large définissez wide: 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 outro pour que la coupe tombe dans sa queue.
  • Résultats de construction : build.ts quitte avec 0 même en cas d'échec d'une vérification ; les échecs s'affichent sous forme de lignes ATTENTION : avec le détail au-dessus. Traitez chaque avertissement comme une erreur : lisez la sortie (filtrez les lignes de progression image n/total), corrigez, reconstruisez jusqu'à ce qu'il n'y en ait plus. Les images fixes sont écrites sous forme out-<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.py a besoin de numpy et est toujours exécuté via uv 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 :

  1. 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.
  2. 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.
  3. 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ècedescription
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

  1. 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.
  2. 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, puis uv run $E/beats.py <fichier> --from <secondes> --len <secondes> --out beats.json écrit beats.json et beats.js.
  3. Chargez beats.js dans 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.
  4. Dans la timeline, définissez music: { file, from }, sound: "fx" (les sons des mouvements restent, discrètement, sous la musique) ou "none", un bpm, et sync: [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 dissoutadd_text / add_lower_third + set_text_animation
une transition de fondu, poussée, glissade, volet ou iris entre deux clipsset_transition
un clip qui bouge, se redimensionne ou s'estompeset_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èteeffectuer 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 a alpha: true et sound: "fx" ; la construction écrit out-<nom>/<nom>.mov (ProRes 4444 avec fond transparent). Règles : effacez le cadre (X.clearRect), ne le remplissez jamais ; pas de grain, vignette, mesh, parallax ni 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 écrit out-<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...

Skills associés