Vidéo promo motion (HTML → MP4)
Livraison : MP4 prêt (H.264, yuv420p, AAC) envoyé avec SendUserFile. Répondez en français, court.
1. Briefing minimal (ne posez pas trop de questions)
Définissez par défaut et ne demandez que ce qui change le résultat :
- Format : 9 :16 (1080×1920) par défaut ; 16 :9 (1920×1080) si demandé. Durée : annonce Insta 20–35 s ; pacing LENT (chaque étape ~1,5–4 s) — l’utilisateur s’est déjà plaint « on dirait du ×2 ».
- Public : jeune/SaaS → SFX brillants (cloches, pops, clics). Public plus âgé/premium (montres, bijoux) → sobre : pads, impacts doux, whooshes, shimmers ; RIEN de pop enfantin.
- Marque : palette, police, logo, CTA (WhatsApp/URL), captures/prints, photos. Musique : si l’utilisateur cite un morceau (Spotify, etc.), vous N’ACCÉDEZ PAS à l’audio — prévenez, utilisez une piste originale et proposez de synchroniser s’il envoie le mp3 (et rappelez la licence pour les annonces).
2. Préparer les assets
- Photos de produit : supprimer le fond avec
rembg(modèle isnet-general-use) → PNG avec alpha pour effet « 3D » (flotter, rotateY/rotateX avec perspective, brillancesheenmasquée par l’alpha du PNG via-webkit-mask-image). - Captures d’écran basse résolution : upscale Lanczos 3× + UnsharpMask. Supprimez les textes publicitaires de la photo originale quand l’utilisateur demande « sans pub ».
- Polices : Inter / Inter Display ExtraBold (SaaS, style flat), Lora serif + italique doré (premium), Poppins.
3. Style approuvé par l’utilisateur
- Blocs de couleur unis, grandes cartes, typographie géante avec dévoilement par mot masqué, beaucoup de mouvement continu (parallaxe lente, zoom lent 1→1,15, étoiles/particules), coupes par wipes de 3 panneaux colorés synchronisés sur le beat.
- PAS de tremblement de caméra (l’utilisateur a rejeté shake) : shake ×0,12 maximum, pas de rotation de caméra, drift ≤3 px.
- Structure narrative : accroche/problème → solution → 2–3 fonctionnalités/intégrations → preuve/bénéfice → CTA. Chaque scène 3–5 s, wipe entre elles. Pour SaaS, montrer le PROBLÈME de manière concrète (message sans réponse, vente perdue −R$) avant la solution.
- Textes courts, 2–4 mots par ligne, en français, avec un mot mis en valeur (couleur/italique). Respecter la zone de sécurité Instagram (éviter ~250 px en haut et ~400 px en bas en 9 :16 pour CTA/légende).
4. Moteur (HTML déterministe)
Un fichier video.html avec window.seek(T) qui positionne TOUT en fonction du temps T (sans requestAnimationFrame/animations CSS). Noyau :
const cl=(x,a=0,b=1)=>Math.min(b,Math.max(a,x)),lerp=(a,b,p)=>a+(b-a)*p;
const E={outBack:(p,c=1.8)=>1+(c+1)*Math.pow(p-1,3)+c*Math.pow(p-1,2),outExpo:p=>p>=1?1:1-Math.pow(2,-10*p),outQuart:p=>1-Math.pow(1-p,4),inOutQuart:p=>p<.5?8*p**4:1-Math.pow(-2*p+2,4)/2,inOutCubic:p=>p<.5?4*p**3:1-Math.pow(-2*p+2,3)/2,lin:p=>p,inCubic:p=>p**3,inOutSine:p=>-(Math.cos(Math.PI*p)-1)/2};
const ez=(T,a,d,f=E.outExpo)=>f(cl((T-a)/d));
function mk(parent,html,cls='',style=''){const d=document.createElement('div');d.className='abs '+cls;d.style.cssText=style;d.innerHTML=html;parent.appendChild(d);return d} // .abs{position:absolute;left:50%;top:50%}
function P(el,o){const x=o.x||0,y=o.y||0,s=o.s==null?1:o.s,op=o.o==null?1:o.o,r=o.r||0,rx=o.rx||0,ry=o.ry||0;el.style.transform=`perspective(2400px) translate(-50%,-50%) translate(${x}px,${y}px) rotateX(${rx}deg) rotateY(${ry}deg) rotate(${r}deg) scale(${s})`;el.style.opacity=op;el.style.visibility=op<=.003?'hidden':'visible'}
function words(el,arr){el.innerHTML=arr.map(w=>`<span class="mk"><span class="wd ${w[1]||''}">${w[0]}</span></span>`).join(' ')} // .mk{display:inline-block;overflow:hidden;padding:.14em .05em .24em;margin:-.14em -.05em -.24em}
function lineIO(el,T,tin,tout,step=.1,d=1.0){[...el.querySelectorAll('.wd')].forEach((w,i)=>{const a=ez(T,tin+i*step,d,E.outQuart),b=tout?ez(T,tout+i*step*.5,.5,E.inCubic):0;w.style.transform=`translateY(${(1-a)*118-b*118}%)`;w.style.opacity=cl(a*2.2,0,1)*(1-b)})}
function addScene(a,b,bg,build,upd,cam){/* crée div.sc avec bg, inner; scenes.push({a,b,root,inner,obj:build(inner),upd,cam}) */}
function runScenes(T){/* affiche uniquement si T dans [a-.02,b+.02]; inner.style.transform=translate/scale de la cam(t,T); s.upd(t,obj,T) */}
// wipes : 3 panneaux [couleur sombre, doré/accent, couleur de fond de la scène suivante], chacun ez(T,t0+i*.09,.38,E.inOutQuart) entrant et sortant (t0=wipe-.55); alterner direction x / xr
function seek(T){runScenes(T);drawWipes(T);drawFX(T);drawGrain(T);/* fondu noir final */}
Couches fixes au-dessus du monde : canvas de poussière/particules, wipes, grain de film (alpha .07), vignette, voile noir pour fondu entrée/sortie.
Effet 3D du produit : P(hero,{ry:Math.sin(T*.7)*8, rx:Math.sin(T*.5)*3, y:Math.sin(T*1.1)*14, s}) + sweep() (bande de brillance mix-blend-mode:soft-light masquée par le PNG) + glow radial doré derrière + ombre drop-shadow.
Ouverture type montre (approuvée par l’utilisateur dans la v1 des montres) : SVG 760 px avec 60 ticks (tous les 5 plus épais/dorés), arc doré qui se dessine (stroke-dashoffset), aiguille qui avance de 6° toutes les 0,5 s avec E.outBack, ticks déjà parcourus s’allument ; texte « Le temps passe pour tout le monde. / Peu laissent une marque. » ; petites étoiles ✦ tournantes. Réutiliser ce type d’ouverture thématique quand le produit le permet.
Timeline à vitesse variable : SEG=[[début,fin,facteur],...] appliqué dans seek ET dans la fonction M(t) de l’audio pour que les SFX suivent les coupes.
5. Rendu (déterministe, reprenable)
- Playwright Chromium viewport 1080×1920 (ou 1920×1080),
page.evaluate('seek(t)')+screenshot(type='jpeg',quality=92). - 60 ips avec motion blur : 2 sous-images par image (t=(f+(s+.5)/2-.5)/60), moyenne en numpy float32 → JPEG q95 →
ffmpeg -f image2pipe -c:v mjpeg -i - -c:v libx264 -preset fast -crf 18 -pix_fmt yuv420p seg/NNNNN.mp4. - Chunks de 120 images dans
seg/, 2 processus parallèles (chunks pairs/impairs), saute le chunk existant (le conteneur peut redémarrer). Lancez avecsetsid nohup ./go.sh &et utilisez un Moniteur avec boucle until dans un fichier done (sleep en foreground est bloqué ; réactivez le Moniteur s’il expire dans 30 min). ~0,38 s par screenshot, 2 CPU : 24 s ≈ 1440 images ≈ 25–30 min. - Vous n’avez modifié que le début ? Supprimez uniquement les chunks concernés et re-rendez ; si seul l’audio a changé, remuxez avec
-c:v copy. - Concat :
ffmpeg -f concat -safe 0 -i list.txt -i audio.wav -c:v libx264 -crf 21 -pix_fmt yuv420p -c:a aac -b:a 192k -shortest -movflags +faststart out.mp4. - Avant de tout rendre, prenez 2–4 screenshots de
seek()à des temps clés et VÉRIFIEZ les textes coupés/débordements dans les deux formats (c’est déjà arrivé avec une police trop grande), et les erreurs JS (page.on('pageerror')).
6. Audio (numpy/scipy, synthétisé)
Aides : lp/hp/bp (butter), add(buf,t,x,g), tt(d) ; sons : thump (sinus 46–96 Hz en décroissance), impact (thump+bruit lp 700), riser (bruit avec bande montante), swish (bruit bp 1,8–5,5 kHz, fenêtre sin²), shimmer (partiels inharmoniques avec décroissance), tick (sinus ~900 Hz, decay 90), pad (3 sinus désaccordés lp 1,4 kHz avec fade), pluck ; réverb par convolution avec bruit décroissant.
- SaaS/jeune : cloches brillantes, pops, clics, whooshes — NE PAS utiliser de basses lourdes (l’utilisateur a détesté « tout grave »).
- Premium/âgé : riser+impact sur les wipes, swish sur les dévoilements de texte, shimmer à l’entrée du produit, ticks dans les listes, tick d’horloge toutes les 0,5 s dans l’ouverture de montre, pads en accords lents (Dm–Bb–F–C), battement doux ~90 bpm. Beat 80–90 bpm correspond à des coupes toutes les 4 s.
- MASTERISATION (évite « saturation ») : JAMAIS de
tanhsur un mix chaud. Faitesx=x/np.abs(x).max()*.85, sauvegardez en wav et exécutezffmpeg -af loudnorm=I=-16:TP=-2:LRA=11 -ar 44100. Vérifiez pic < −1 dBFS et crête > 10 dB.
7. Livraison et itération
Envoyez le MP4 avec une légende d’1 ligne ; mentionnez ce qui a changé et ce qui N’A PAS été vérifié (ex. : « j’ai vérifié les images et les métriques, pas écouté »). Retours typiques et réponse : « trop rapide » → scalez la timeline ×1,5 ; « tremblant » → réduisez le shake ; « mauvais son » → changez le profil SFX (brillant vs sobre) ; « sature » → remasterisez uniquement l’audio et remuxez ; « j’ai aimé l’animation X de la v1 » → ramenez la scène X, re-rendez uniquement les chunks concernés. Si l’utilisateur demande un prompt/documentation réutilisable, générez un doc avec ce flux.