/showcases · Brand
Boucle de mouvement hero : du prompt textuel à l’asset de production de 460 Ko
Comment nous avons généré, encodé et intégré la vidéo hero d’AudioLab.tools : une subtile boucle respirante de 5 secondes avec un ruban cyan. Pipeline de bout en bout incluant la génération avec Higgsfield seedance, l’encodage ffmpeg WebM + H.264, le fallback image et la gestion de prefers-reduced-motion.
Outcome
460 Ko au total livrés (WebM + MP4). 5 secondes de mouvement subtil. Fallback image statique pour les réseaux lents. Entièrement supprimé pour les utilisateurs avec prefers-reduced-motion.
Un hero animé sur un site tech est un pari risqué. Si on le rate, c’est une publicité distrayante en lecture automatique ; si on le réussit, c’est ce qui fait la différence entre « encore un site de développeur » et « marque premium ». Les contraintes que nous nous sommes fixées :
- Subtil. Le mouvement est une respiration, pas une action. Rien ne doit détourner l’attention de la superposition de texte.
- Minuscule. Moins de 500 Ko de transport total, point.
- Fallback-first. La page doit être superbe même sans aucune vidéo.
- Respectueux de reduced-motion. Les utilisateurs qui s’y sont opposés n’obtiennent rien d’animé.
Voici comment nous y sommes parvenus en une vingtaine de minutes de travail réel.
Étape 1 : générer le mouvement (Higgsfield seedance 2.0)
Seedance est le bon modèle pour cela (image-vers-vidéo, préservant l’identité, avec un solide support du mouvement subtil). Nous avons utilisé la même image 21:9 au ruban cyan que celle construite pour le hero statique (voir pipeline d’imagerie de marque) comme frame de référence.
{
model: 'seedance_2_0',
prompt: `The luminous cyan ribbon of light breathes and slowly rotates
in pitch-black space, with subtle volumetric particles drifting
through the frame. Very slow, gentle motion — the ribbon appears
to inhale and exhale, with soft glow pulses. Camera completely
static. The lighting and composition match the reference image
exactly. Premium, calm, atmospheric, ambient. No camera movement,
no cuts, just subtle organic motion of the light ribbon itself.`,
duration: 5,
resolution: '720p',
aspect_ratio: '21:9',
mode: 'std',
medias: [{ value: heroImageUrl, role: 'image' }],
}
Deux contraintes de prompt qui ont rendu la sortie exploitable :
- « Camera completely static. » Sans cela, seedance se rabat sur un lent zoom avant qui ressemble à toutes les autres vidéos tech.
- « No camera movement, no cuts, just subtle organic motion. » Renforce le point 1 et empêche le modèle d’ajouter des coupes stylistiques.
Coût : 22 crédits. Temps de rendu : ~70 secondes.
Étape 2 : retirer l’audio
Seedance produit un MP4 avec audio. Nous n’en avons pas besoin : le hero est silencieux, et tout audio signifierait de toute façon que nous ne pourrions pas faire d’autoplay sur la plupart des navigateurs.
ffmpeg -i hero.mp4 -an -c:v copy hero-noaudio.mp4
Le flag -an retire l’audio ; -c:v copy évite de réencoder la vidéo. Passe rapide, aucune perte de qualité. Nous utilisons cette sortie comme source pour les deux encodages suivants.
Étape 3 : encoder en VP9 WebM (navigateurs modernes)
Le VP9 dans un conteneur WebM compresse mieux que le H.264 et constitue le bon choix pour les navigateurs qui le prennent en charge (~90 % du trafic).
ffmpeg -i hero.mp4 -an \
-c:v libvpx-vp9 \
-b:v 800k -crf 36 \
-row-mt 1 -tile-columns 2 -threads 4 \
-deadline good -cpu-used 2 \
hero.webm
Réglages à signaler :
-crf 36: cible de qualité. 36 est plutôt agressif ; le matériau source (un ruban de lumière abstrait à mouvement lent sur fond noir) se compresse extrêmement bien, donc on peut se le permettre.-b:v 800k: plafond de débit. Empêche le VP9 de sur-allouer sur les rares frames comportant du mouvement.-row-mt 1 -tile-columns 2 -threads 4: encodage multi-thread. Encodage local plus rapide sans affecter la qualité de sortie.-deadline good -cpu-used 2: compromis qualité/vitesse.good/2est le point optimal pour les assets ponctuels.
Sortie : 252 Ko pour 5 secondes en 720p.
Étape 4 : encoder en H.264 MP4 (fallback Safari)
Safari (et certains navigateurs plus anciens) ne prend pas en charge le VP9. Il nous faut un MP4 H.264 en fallback.
ffmpeg -i hero-noaudio.mp4 -an \
-c:v libx264 -crf 28 -preset slow \
-movflags +faststart -pix_fmt yuv420p \
hero.mp4
Réglages :
-crf 28: légèrement plus conservateur que le crf du WebM, car le H.264 ne compresse pas ce matériau aussi efficacement.-preset slow: meilleure compression. Nous encodons une seule fois, pas en temps réel.-movflags +faststart: déplace l’atome de métadonnées du MP4 vers l’avant afin que le navigateur puisse démarrer la lecture avant le téléchargement complet du fichier.-pix_fmt yuv420p: requis pour la compatibilité Safari sur iOS.
Sortie : 209 Ko pour 5 secondes en 720p.
Étape 5 : mettre en place le markup
<section class="relative isolate overflow-hidden">
{/* Static image fallback — renders first, hidden when video plays */}
<Image
src={heroWideImg}
alt=""
widths={[1280, 1920, 2560, 3168]}
sizes="100vw"
loading="eager"
fetchpriority="high"
class="absolute inset-0 -z-10 h-full w-full object-cover"
/>
{/* Motion video — slow ambient loop */}
<video
class="absolute inset-0 -z-10 h-full w-full object-cover opacity-0
transition-opacity duration-700 motion-reduce:hidden"
id="hero-video"
autoplay loop muted playsinline
preload="metadata"
aria-hidden="true"
>
<source src="/media/hero.webm" type="video/webm" />
<source src="/media/hero.mp4" type="video/mp4" />
</video>
{/* Contrast overlays + text layer */}
...
</section>
<script is:inline>
// Fade the video in once it actually starts playing
const v = document.getElementById('hero-video');
v?.addEventListener('playing', () => { v.style.opacity = '1'; });
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) v?.remove();
</script>
Trois patterns à souligner :
- L’image s’affiche en premier, la vidéo apparaît en fondu. L’utilisateur voit immédiatement un superbe hero statique. Ce n’est qu’une fois que la vidéo a décodé une frame et a commencé sa lecture qu’elle apparaît en fondu par-dessus l’image. Aucun flash noir saccadé, aucun temps de chargement perçu.
- Classe Tailwind
motion-reduce:hidden: gestion déclarative de reduced-motion. - Suppression impérative dans le script inline : si l’utilisateur a activé reduced motion, l’élément
<video>est entièrement retiré du DOM. Aucune requête réseau, aucun décodage, aucune consommation d’énergie.
Ce qui a (brièvement) cassé
- Le premier encodage WebM faisait 800 Ko. Trop gros. Passage de
-crfde 30 à 36 et de-cpu-usedde 4 à 2 ; arrivé à 252 Ko. - Problèmes d’autoplay initiaux sur iOS. Oubli de
playsinline. iOS l’exige pour la vidéo inline ; sans lui, la vidéo tente de passer en plein écran à la lecture. - Le MP4 faisait 1,2 Mo avant le réencodage. La sortie de seedance est fidèle mais plus volumineuse que nécessaire pour le web. La passe de réencodage avec
-crf 28l’a ramené à 209 Ko.
Résultat
Transport total : 460 Ko dans le pire des cas (les deux fichiers mis en cache après le premier chargement). Signature visuelle : un ruban cyan lumineux et respirant qui rehausse l’ensemble de la page d’accueil. Délai de l’idée à la production : ~20 minutes.
Le principe général : quand vous livrez du mouvement sur un hero de marque, faites le travail d’encodage. La sortie du modèle est l’asset source, pas l’asset de production.
Liens connexes
More build logs
-
Pipeline d’imagerie de marque : sept images cinématographiques, un seul système de design
7 images de marque cinématographiques générées (hero + 6 spécifiques aux clusters), optimisées de PNG de 5-9 Mo vers des variantes WebP de 50-400 Ko via la pipeline de build d’Astro, puis intégrées aux cartes de cluster, aux sections hero et aux temps forts atmosphériques de la marque.
-
Six démos interactives, une seule plateforme : l’architecture
Six démos live dans le navigateur qui partagent un pipeline d’analyse unifié dans un Web Worker, une synthèse audio au runtime pour les échantillons et un motif d’îlot React par cluster qui garde rapide le shell Astro static-first.