Communitygithub.com

HusseinRagab/motion-reels

Créez des Reels 9:16 (1080×1920) de motion design pour une page de décoration intérieure, animés par code – titres, plans qui se dessinent, effets avant/après, zooms photo, échantillons de matériaux – rendus image par image avec Playwright et ffmpeg.

Qu'est-ce que motion-reels ?

motion-reels est un puissant moteur de motion design dont le code est dessiné, spécialisé dans la production de vidéos verticales au format Reel (1080×1920). Il permet de construire des animations complexes – titres, dessins, effets avant/après, zooms photo et échantillons – en définissant chaque bloc comme un fichier JavaScript autonome. Chaque bloc contrôle le moment où chaque élément apparaît, basé sur une timeline de mots provenant soit d'une feuille de beats (sans voix) soit des timings d'un enregistrement vocal. Le moteur rend chaque image dans une page de navigateur fraîche avec Playwright, vérifie automatiquement les superpositions, les débordements de zone de sécurité et les sauts d'image, puis assemble le tout avec la voix et/ou la musique via ffmpeg pour atteindre un volume cible de -14 LUFS. Ce workflow est idéal pour les créateurs de contenu, les designers et les développeurs qui ont besoin d'un contrôle précis sur chaque frame sans recourir aux logiciels d'animation traditionnels. La sortie est propre, calme et premium, parfaite pour les marques et les présentations professionnelles sur les réseaux sociaux.

Compatible avec✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/HusseinRagab/reel-motion-kit/tree/HEAD/motion-reels

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 motion-reels ?

Motion design codé pour les Reels verticaux. Chaque reel est composé d'un ou plusieurs blocs. Un bloc est un petit fichier JavaScript qui place des titres, dessins, photos et échantillons dans un univers et indique quand chacun apparaît. Chaque moment provient d'une timeline de mots : soit une feuille de beats (sans voix), soit le timing des mots d'une prise vocale. Le moteur rend chaque image sur sa propre page de navigateur fraîche, vérifie la mise en page et assemble le résultat avec la voix et/ou la musique.

La personne propriétaire de la page décide du look, des mots et de ce qui est publié. Claude ne publie jamais rien.

Configuration (une fois)

  • Python 3.10+ et ffmpeg/ffprobe dans le PATH. Ensuite, setup.ps1 (Windows) ou setup.sh (macOS/Linux) du dépôt, ou pip install -r requirements.txt + python -m playwright install chromium (ou la variable d'environnement MG_CHROME = un Chrome existant).
  • Voix uniquement : pip install -r requirements-voice.txt (faster-whisper ; le premier téléchargement récupère le modèle de reconnaissance vocale).
  • Test : python engine/selftest.py doit afficher SELFTEST OK (vérifie que les exemples passent, que les tests négatifs échouent comme prévu, que les sous-titres arabes se coupent correctement, et qu'un court rendu + analyse des défauts + assemblage atteint -14 LUFS). --quick saute le rendu.

Fichiers du moteur (tous dans engine/) : render.py (vérification / storyboard / images fixes / vidéo), beats.py, words.py, frames.py, faultscan.py, assemble.py, storyboard_sheet.py, make_grain.py, templates/ (shell, reel.js, reel.css, polices).

Un dossier par reel

<nom du reel>/
  brief.md          à quoi sert le reel, l'idée unique, l'appel à l'action, photos disponibles
  beats.txt         les mots à l'écran, un beat par ligne (ou script.txt + take.wav quand il y a une voix)
  words.json        timeline (depuis beats.py ou words.py)
  photos/           les photos du propriétaire (uniquement celles dont il a les droits)
  A.js, B.js ...    blocs
  build/            json de vérification, images du storyboard, vidéos des blocs, fichiers render.json, planches contact
  <Reel>_REV01_<date>.mp4   le livrable ; une correction ultérieure est livrée en _REV02, jamais écrasé

Portes (ne jamais sauter ni réordonner ; s'arrêter à chaque ⛔ pour l'accord du propriétaire)

  1. Brief. Une idée par reel (révélation d'une pièce, histoire de matériau, modification de plan, astuce). Demander les photos et le pseudo. Lister ce que chaque photo montre. Accrocher dans les 2 premières secondes.
  2. Feuille de beats (beats.txt) : les mots exacts à l'écran, dans l'ordre, un beat par ligne. Garder un beat court (2–7 mots). Mettre chaque élément d'une liste sur sa propre ligne, pour que chacun ait le temps d'être vu. ⛔ Le propriétaire signe les mots. Après validation, un changement de mot signifie un nouveau timing du bloc (et une nouvelle voix, si voix enregistrée) : le dire avant de modifier quoi que ce soit.
  3. Timeline.
    • Pas de voix : python engine/beats.py beats.txt words.json (rythme par défaut 2,6 mots/s + 0,7 s après chaque beat ; [+1.5] en fin de ligne maintient plus longtemps ; [pause 2] maintient sans rien de nouveau).
    • Voix (n'importe quel TTS ou un enregistrement, une prise par bloc, jamais éditée) : python engine/words.py take.wav words.json, puis comparer la transcription avec le script mot pour mot. L'image s'ajuste à la prise, jamais l'inverse.
  4. ⛔ Images du storyboard. Construire les mises en page des blocs (les positions seules suffisent), exécuter check, puis render.py A.js words.json storyboard build/sb et storyboard_sheet.py build/storyboard.png build/sb. Faire l'auto-vérification de la planche (liste ci-dessous), puis l'envoyer. Rien n'est animé avant que le propriétaire approuve la mise en page.
  5. Construire le mouvement. Déclencher chaque élément sur le mot qui le nomme (règles ci-dessous). Exécuter check après chaque modification.
  6. Rendu. render.py A.js words.json video build/A.mp4 --workers 4 par bloc (jamais --draft pour quoi que ce soit d'envoyé). Le fichier build/A.mp4.render.json de chaque bloc doit indiquer "capture": "fresh" et le même navigateur que les autres blocs ; si le navigateur a changé, refaire le rendu de tous les blocs.
  7. Analyse des défauts. faultscan.py build/A.mp4 --sheet build/A_contact.png sur chaque bloc, puis sur le reel final. Regarder chaque moment signalé. Une bande noire ou un flash d'une seule image est un défaut : arrêter et corriger. Un contenu de photo sombre est acceptable.
  8. Assemblage. assemble.py blocks.json <Reel>_REV01_<date>.mp4 [--music bed.mp3]. Le script affiche le volume mesuré ; il doit être à la cible (-14 LUFS par défaut) et le pic réel à -1,5 dBTP ou moins.
  9. ⛔ Révision. Envoyer le reel et sa planche contact. Les corrections purement visuelles refont le rendu uniquement des blocs modifiés et sont livrées en tant que _REV suivante. Le propriétaire publie le reel.

Règles de mouvement (pourquoi : un rendu calme et premium ; rien ne tressaute, rien n'est difficile à lire)

  • Les mots ne bougent jamais. Un mot apparaît en fondu (0,28 s) puis reste fixe. Pas de glissement, d'échelle, de rebond ou de dérive sur le texte.
  • Titres : hl(el, text, cue(...)) fait apparaître chaque mot en fondu sur son propre mot dans la timeline. Quand un titre paraphrase une voix, utiliser {stream:true} : les mots apparaissent de gauche à droite, espacés de 0,1 s, à partir du signal.
  • Caméra : parfaitement fixe pendant qu'un plan tient ; elle ne glisse que (0,8 s, ease-in-out) entre les plans. En 9:16, empiler les plans verticalement dans l'univers (plan 2 à y 1920, plan 3 à y 3840) et glisser vers le bas : SHOTS=[[t,540,960,1],[t2,540,2880,1],...].
  • Les photos peuvent avancer lentement : push:[1,1.04] = vitesse constante, jamais plus de +6 % (MOTION.PUSH_MAX ; le moteur refuse plus). Pas de push sur une photo qui porte du texte.
  • Les dessins se tracent au fur et à mesure qu'ils sont nommés, d'abord les références : les murs avant la fenêtre et la porte qu'ils contiennent, la ligne de sol avant les hauteurs mesurées à partir d'elle. at('#walls', cue('Walls'), null, 'draw', 0.9) ; 0,6–1,0 s par élément.
  • Dimensions à une seule échelle. Si un plan montre deux dimensions, les deux utilisent la même échelle.
  • Étiqueter ce que les mots nomment ; tout nombre nomme son unité ("3,2 m", jamais un simple "3,2").
  • Pas de rebond, dépassement, élastique ou ressort nulle part. Chaque timing provient de MOTION dans reel.js ; le modifier uniquement là, jamais en dur dans un bloc.
  • Un élément SVG en pointillé donné à at() s'affiche avant son signal : l'envelopper dans un <g>.

Jetons de timing (MOTION dans reel.js)

JetonValeurUtilisation
WORD_FADE / WORD_LEAD0,28 s / 0,08 sfondu du mot ; commence juste avant le mot
STREAM0,10 sespacement des mots en mode flux pour les titres
REVEAL / EXIT0,5 s / 0,4 sblocs, chips, échantillons, photos entrée / sortie
draw (at(...,'draw',dur))0,6–1,0 spar élément dessiné
CAM_SLIDE / CAM_LEAD0,8 s / 0,35 sglissement de caméra ; commence avant le signal du plan
PUSH_MAX0,06limite de push photo
WIPE1,2 seffet avant/après
Easing : ease-out cubique pour les révélations, ease-in-out cubique pour les glissements, dessins et wipes.

Mise en page et vérification

  • Zone de sécurité (SAFE dans reel.js) : le texte, les étiquettes et les sous-titres restent à l'intérieur de x 65–1015, y 269–1248 du cadre 1080×1920 (14 % en haut, 35 % en bas, 6 % sur les côtés). Ces chiffres sont largement utilisés par des tiers pour les Reels, pas une règle publiée par Meta pour les publications organiques : mesurer à partir d'une capture d'écran téléphonique d'un reel publié et définir safeZone({...}) si l'application change. Les photos et dessins peuvent les dépasser ; le texte ne le peut pas.
  • render.py <block> words.json check build/<block>.check.json est obligatoire avant chaque rendu. Il échantillonne toutes les 0,25 s et échoue en cas de chevauchement maintenu 0,5 s ou plus entre des éléments séparés (texte, dessins, chips, échantillons, photos, sous-titres) et en cas de texte en dehors de la zone de sécurité pendant que la caméra tient. Un échec signifie corriger la mise en page, jamais faire le rendu. Prouver une nouvelle règle avec un test négatif (réintroduire un défaut connu ; la vérification doit échouer).
  • Le texte qui appartient à une photo va à l'intérieur de l'élément photo (parent: photoDiv) avec un scrim() derrière, ou la photo est un fond (bg:true). Sinon, la vérification signale texte sur photo, ce qui est le comportement souhaité.
  • La vérification ne détecte pas les éléments coupés au bord du cadre : l'auto-vérification s'en charge.

Construction des blocs (reel.js)

boot(()=>{                                   // chaque bloc commence par boot(...)
  handle('@yourstudio');                     // petit pseudo en haut de la zone de sécurité
  $('world').insertAdjacentHTML('beforeend', `<div id="h1" class="abs serif" style="left:90px;top:360px;width:900px;font-size:104px"></div>`);
  hl('h1', 'A small room, *re-planned.*', cue('A small'));          // *...* = accent italique ; ~mot = pas dans la timeline
  at('#walls', cue('Walls'), null, 'draw', 0.9);                   // fx : 'fade' | 'pop' | 'wipe' | 'draw'
  photo({src:rel('photos/living.jpg'), x:0, y:1920, w:1080, h:1350, a:cue('Light'), push:[1,1.04]});
  beforeAfter({before:rel('photos/before.jpg'), after:rel('photos/after.jpg'), x:90, y:2440, w:900, h:900, a:cue('Before'), t:cue('after')});
  swatch({name:'Oak', note:'floor', color:'#B08A62', x:90, y:4480, size:270, a:cue('Oak')});   // ou src: rel('photos/oak.jpg')
  clip({dir:rel('clips/walk'), n:240, x:0, y:5760, w:1080, h:1920, a:cue('Walk'), t0:cue('Walk')}); // images depuis frames.py
  captions(1120, 3);                         // sous-titres incrustés depuis la timeline (reels avec voix)
  SHOTS=[[0,540,960,1],[cue('Light'),540,2880,1]];
});

rel(path) résout un fichier à côté du bloc. cue('words', after) échoue si les mots ne sont pas dans la timeline, donc une faute de frappe échoue bruyamment. Exemple complet : examples/demo/demo.js.

Reels en arabe et autres langues de droite à gauche

  • Appeler rtl() en premier dans boot(). Les titres utilisent Noto Naskh Arabic, le texte IBM Plex Sans Arabic (OFL) ; le latin utilise les polices par défaut. Pas d'italique, d'espacement des lettres ni de majuscules en arabe (les lettres doivent se lier) ; les mots d'accent changent uniquement de couleur.
  • Les zones de texte conservent left + width ; le texte s'aligne à droite. Disposer les listes de droite à gauche (le premier nommé est le plus à droite).
  • Les signaux ignorent les diacritiques, tatweel, variantes alef/yaa/taa-marbuta et convertissent les chiffres arabo-indiens ; un signal sans lettres échoue. ة et ه comptent comme une seule lettre, donc signaler une phrase plus longue où deux mots ne diffèrent qu'à cet endroit. Les mots numériques correspondent uniquement en anglais : en arabe, signaler les chiffres ou le mot tel que transcrit (words.py --lang ar choisit le modèle multilingue).
  • Les sous-titres se coupent sur ، ؛ ؟ et la ponctuation latine, et ne l'affichent pas.
  • Exemple : examples/arabic/.

Look

Le thème réside uniquement dans engine/templates/reel.css (couleurs en tant que variables CSS, polices) et les fichiers de police. Par défaut "minimal chaleureux" : papier #F4EFE8, encre #2B2622, accent #9C6B4E, titres Cormorant Garamond + texte Jost (les deux SIL OFL 1.1). Pour changer le look : modifier les variables, ou ajouter des polices OFL dans engine/fonts/ avec leur fichier de licence et les lister dans fonts.css et window.FONTS (reel.js). Demander au propriétaire 3 à 5 captures d'écran de sa grille d'abord et s'y conformer.

Auto-vérification (Claude, avant d'envoyer toute planche ou rendu)

  1. Chaque élément commence sur son mot ; les lignes de référence se dessinent en premier.
  2. Les mots restent immobiles une fois posés ; la caméra reste fixe en maintenant ; pas de saut à la fin d'un glissement.
  3. Rien n'est coupé au bord du cadre ; tout le texte est dans la zone de sécurité ; rien d'important sous les 35 % du bas.
  4. Dimensions à une seule échelle ; chaque nombre a son unité ; l'orthographe correspond à la feuille de beats signée.
  5. Vérification PASS sur chaque bloc ; render.json indique capture: fresh, même navigateur pour tous les blocs.
  6. Analyse des défauts propre, ou chaque instant signalé inspecté et expliqué.
  7. Volume et pic réel affichés par assemble.py à la cible.

Droits et confidentialité

  • Uniquement les photos du propriétaire, ou celles dont il a les droits. Foyers clients : pas de visages, adresses ou documents sans consentement.
  • Musique : uniquement les pistes pour lesquelles le propriétaire a une licence, ou la bibliothèque de la plateforme ajoutée dans l'application lors de la publication.
  • Pas de revendications que le propriétaire n'a pas formulées (prix, délais, récompenses).

Dépannage

  • cue not found: "..." : les mots ne sont pas dans words.json dans cet ordre ; corriger la feuille de beats ou le signal.
  • ERR reqfail:file:///...jpg : le chemin d'une photo ou d'un clip est erroné ; utiliser rel() et vérifier le nom du fichier.
  • Copies fantômes de dessins antérieurs ou bande noire : vous avez effectué le rendu avec --draft. Refaire le rendu sans.
  • Ralentissements : --workers 4 (jusqu'à 8) sur une machine disposant d'autant de cœurs ; la vérification et le storyboard sont rapides, la vidéo ne l'est pas.

Skills associés