Vidéo de lancement produit
Produit un film d'environ 40 secondes où les graphiques animés portent le récit et chaque pixel du « produit » est réellement rendu par la vraie application.
Ce skill existe à cause d'un faux choix que les gens ne cessent de faire :
- Un film purement graphique a l'air professionnel mais ne prouve rien — pas de vrai produit.
- Un enregistrement d'écran brut prouve tout mais a l'air amateur — pas de design animé.
La réponse n'est ni l'un ni l'autre : Remotion est la timeline maîtresse, et les prises de vue du produit sont de vraies images capturées en pilotant l'application réelle dans un navigateur headless.
Construisez un petit projet Remotion à côté du produit : toolkit de capture partagé + primitives, un répertoire par film de l'application (marque, prises, script de capture, timeline). Voir references/toolkit.md pour la disposition recommandée.
Non-négociables
Lisez-les avant d'écrire du code. Chacun a coûté du temps de débogage réel, et chacun échoue silencieusement — vous obtenez un résultat plausible mais faux.
Capture
| Piège | Ce qui se passe | Correctif |
|---|---|---|
setViewport({deviceScaleFactor: 2}) | page.screencast() mesure sa chaîne de filtres ffmpeg au DPR système, donc il construit une chaîne 1× et jette le détail Retina. Aucune erreur. | Forcer au niveau du navigateur : --force-device-scale-factor=2 + deviceScaleFactor: 0 dans le viewport (0 = « défaut système »). |
| Le mode Headless ne rend aucun curseur | page.mouse.move() envoie des événements CDP que la page voit, mais rien n'est jamais peint. | Injecter un curseur DOM via evaluateOnNewDocument. Placez-le dans la page, pas composé dans Remotion — alors chaque transformation Remotion (inset de cadre, zoom punch) le redimensionne exactement comme un enregistrement réel. Un sprite nécessite d'inverser chaque transformation, et 6px de dérive donne l'impression d'être cassé. |
| Le mode Headless n'a pas de GPU | Tout contenu WebGL/three.js s'enregistre sous forme de panneau noir ou vide. | --use-gl=angle --use-angle=swiftshader --enable-unsafe-swiftshader. Vérifier avec une lecture de pixel, pas seulement « un contexte existait-il ? ». |
| SwiftShader compose également la page | Un pilote GL logiciel composant 2560x1440 plafonne l'onglet entier à environ 8fps, donc chaque prise animée s'enregistre comme un diaporama. Cela donne l'impression d'un produit lent, pas d'une capture cassée, donc personne ne soupçonne les flags. | Ajouter --disable-gpu-compositing. Le rasteriseur CPU de Skia est beaucoup plus rapide pour le 2D simple, WebGL fonctionne toujours, et les images A/B sont identiques. Mesuré sur la même page : 8,2fps → 60,1fps. |
| Juger la fluidité à l'œil | Les images dupliquées semblent correctes dans un contact sheet et lors d'un défilement image par image. Seule la lecture les révèle, et à ce stade, le rendu est terminé. | Compter les images distinctes : ffmpeg -i take.mp4 -vf mpdecimate,showinfo -f null - 2>&1 | grep -c pts_time. Bien en dessous du nombre d'images du fichier signifie des saccades. |
page.mouse.click(x, y, {clickCount: 2}) | Envoie une pression portant les métadonnées clickCount. Chrome ne synthétise jamais dblclick. Deux clics discrets espacés de 60 ms non plus. | Si l'application a besoin d'un vrai dblclick, cliquez pour le mouvement visible du curseur puis déclenchez l'événement dans le cadre aux propres coordonnées de l'élément. |
page.keyboard.type() après avoir cliqué dans un iframe | Les touches vont à ce que la page parente a ciblé. Si le parent transmet les touches à l'application (navigation par flèches, raccourcis), votre frappe pilote silencieusement l'interface au lieu de saisir du texte. | Tapez dans le cadre : frame.type(selector, text). |
evaluateOnNewDocument effectuant du travail DOM | S'exécute au début du document où document.head et document.documentElement peuvent tous deux être nuls. Un appendChild non protégé lance une erreur et abandonne silencieusement tout ce qui suit dans ce script. | Protéger et différer à DOMContentLoaded. |
| Enregistrement de la navigation | Enterre les secondes utiles profondément dans le fichier et gonfle la mezzanine. | Diviser chaque prise en setup (non enregistré) et action (enregistré). |
| Faire confiance aux horodatages des marqueurs | Le screencast est à fréquence d'images variable ; la timeline encodée comprime par rapport à l'horloge réelle, donc un marqueur à 22s n'est pas à 22s dans le fichier. | Utiliser les marqueurs pour trouver approximativement où regarder, puis choisir startAt en extrayant les images et en regardant. |
| Actifs distants/hotlinkés | Les CDN tiers refusent par intermittence le navigateur de capture ; les grilles d'images peignent des damiers de transparence vides, ce qui donne l'impression d'un produit cassé. | Attendre les images et avertir, puis cadrer la prise pour les éviter. Ne pas expédier une prise avec des images vides. |
window.scrollTo dans une coque d'application | De nombreuses coques défilent un élément intérieur (<main class="overflow-y-auto">), pas la fenêtre. Faire défiler la fenêtre est un no-op silencieux et la prise ne bouge jamais. | Trouver le vrai défileur et définir son scrollTop. |
| Deviner les coordonnées de clic | Les applications n'acceptent souvent les interactions que sur des nœuds spécifiques — par exemple un éditeur en ligne qui n'accepte que les éléments feuilles (childElementCount === 0) tandis que les en-têtes enveloppent leur texte dans des spans. Cliquer sur la cible visuellement évidente ne fait rien. | Lire le code source de l'application pour la règle, puis demander à la page où se trouvent ses cibles valides (frame.evaluate) et mapper les coordonnées du cadre → coordonnées de la page via la boîte mesurée de l'iframe. |
Rendu
| Piège | Ce qui se passe | Correctif |
|---|---|---|
| Sauter le transcodage de la mezzanine | Remotion cherche arbitrairement ; avec un GOP normal, chaque recherche décode à partir de l'image I précédente. Un rendu de 3 minutes devient 40. | Transcoder en tout-intra : -c:v libx264 -crf 14 -g 1 -keyint_min 1 -sc_threshold 0 -pix_fmt yuv420p, plus des tags bt709 explicites pour que Chrome ne modifie pas la couleur de la marque. |
page.screencast({format:'mp4'}) | Émet du VP9 dans un MP4 fragmenté — mal supporté. | format: 'webm', puis transcoder. |
<TransitionSeries> avec une grille de battements | Les transitions se chevauchent, donc la durée totale = somme des séquences − somme des transitions. Votre grille d'images dérive silencieusement. | Pour une coupe image-exacte, utilisez un <Sequence from/durationInFrames> explicite et faites les fondus localement. Affirmez que la chaîne somme à la durée totale. |
Une <Sequence> plus longue que sa source | Se fige sur la dernière image décodée — donne l'impression d'un blocage, pas d'une erreur. | Limiter chaque durée à la longueur réelle de la prise. Enregistrer les durées source dans un commentaire. |
premountFor sur des séquences qui se chevauchent | Une séquence pré-montée rend tôt par-dessus la séquence actuelle — vous obtenez un texte fantôme doublement exposé. | Ne pas pré-montrer les séquences qui partagent de l'espace à l'écran ; pré-montrer uniquement lors des coupes franches. |
| Superpositions positionnées dans l'espace de composition | Si le composant de la séquence recadre et redimensionne la plaque en interne, un masque ou une surbrillance placée dans les coordonnées de la composition ne s'alignera avec rien dans le cadre — les bords coupent à travers le contenu. | Placer les superpositions dans la même transformation que la plaque, ou effectuer le traitement nativement au lieu de sur la séquence. |
Math.random() / Date.now() dans une composition | Se réexécute à chaque image ; le texte scintille. Remotion rend chaque image indépendamment. | Rendre toute aléatoire une fonction pure de l'index (un hachage basé sur sin). |
Animations CSS / transitions / animate-* de Tailwind dans Remotion | Rendu figé ou erroné — Remotion pilote le temps via useCurrentFrame(), pas l'horloge réelle. | Reconstruire le mouvement sur useCurrentFrame(). Cela signifie aussi que vous ne pouvez généralement pas réutiliser les composants animés du site marketing. |
| Construire une boucle en se décalant dans le film | Deux bugs de couture : un flash noir si la plage s'ouvre sur un fondu, et un cut sec là où la boucle se referme. | Donner à la boucle sa propre composition et fondre enchaîné sa queue dans un <Freeze frame={0}> de sa propre tête. Choisir une prise en mouvement continu ; tout ce qui s'attarde est quasi-statique et mauvais en lecture automatique. |
Processus
1. Établir la vérité du produit avant de concevoir le film
L'étape la plus importante. Lisez le code pour chaque capacité que le film a l'intention de revendiquer. Les pages marketing illustrent des aspirations ; ne supposez pas que le produit fait ce que son propre site vitrine suggère.
Cela est déjà arrivé une fois : le clou du spectacle prévu d'un film était tiré d'un composant animé du site marketing montrant un document se transformant à travers quatre rapports d'aspect différents. Le produit codait en dur un seul rapport d'aspect et une étape d'exportation fixe — le composant était une illustration d'une idée, pas une fonctionnalité. Construire la scène clé dessus aurait placé une affirmation non supportée sur la page d'accueil.
Vérifiez, spécifiquement :
- Quel contenu s'affiche réellement ? Les anciens enregistrements peuvent ne pas être rendus par le moteur actuel.
- Quelles routes nécessitent une connexion ? Lesquelles redirigent sans état préalable ?
- Que nécessite réellement chaque fonctionnalité interactive pour s'engager ?
2. Minimiser les risques du pipeline avec le plus petit test possible
Dans l'ordre, avant de construire quoi que ce soit par-dessus :
- Prouver la géométrie de capture (page marquée aux coins → le fichier est-il vraiment 2× ?).
- Prouver l'authentification (le navigateur peut-il atteindre une route connectée ?).
- Prouver WebGL si du 3D est impliqué.
- Prouver une interaction de bout en bout.
3. Repérer, ne pas deviner
Prenez une capture d'écran de chaque surface candidate et montrez à l'utilisateur un contact sheet avant de passer du temps sur des prises vidéo. Le choix du contenu domine la valeur de production : un contenu de démonstration médiocre rend un beau film comme une publicité pour un produit médiocre.
4. Choisir le contenu de démonstration délibérément
Privilégiez la propre marque du produit. Présenter un tiers reconnaissable est plus persuasif mais place la marque de quelqu'un d'autre sur la page d'accueil, et remplacer un tiers par un autre ne change rien. Le contenu autoréférentiel ferme également la boucle : l'artefact vendant l'outil a été fabriqué par l'outil.
Si le contenu n'existe pas encore, le créer est un travail de design, pas de capture — c'est chaque image de la seconde moitié du film. Rédigez-le délibérément, produisez quelques candidats, et laissez l'utilisateur choisir.
5. Couper sur une grille
120 BPM ⇒ 1 mesure = 60 images = 2s à 30fps. Faire atterrir chaque coupe sur un multiple de 30 et chaque limite d'acte sur 60. Ne coûte rien, et les coupes se font gratuitement.
6. Vérifier en regardant
Après chaque rendu, extrayez les images et regardez-les réellement. Construisez un contact sheet étiqueté. Chaque vrai défaut trouvé jusqu'à présent — logos vides, prise montrant la mauvaise page, légende illisible sur un panneau clair, bande sombre d'un recadrage mal mesuré, couture de masque à travers une étiquette — a été trouvé en regardant les images, pas par un test réussi.
Regardez-le muet, car c'est ainsi qu'il sera lu automatiquement.
Règles éditoriales
Voici ce qui distingue un film qui fonctionne d'un film qui est bon.
Chaque affirmation à l'écran doit être soutenue par la séquence à côté. Une coupe avait « Partager un lien » sur une séquence où rien ne montrait le partage. Soit montrez-le, soit énoncez-le clairement sur une carte — n'insinuez pas.
Méfiez-vous des affirmations honnêtes en contexte mais pas à l'échelle vidéo. Un chiffre étiqueté « illustratif » en petits caractères devient une affirmation dure dès qu'il s'agit d'une image clé tenue dans un film marketing. Gardez-les hors des images clés tenues.
Citez le mouvement du produit, ne le réinventez pas. Reprenez la propre courbe d'assouplissement et le vocabulaire d'entrée de l'application. Une courbe pour tout ce qui entre ou atterrit. Rien ne rebondit — un dépassement donne l'impression d'hésitation.
Deux fonds, jamais de gris moyen. Un fond sombre pour les déclarations, un fond clair pour les explications. Choisissez-les parmi les propres tokens du produit.
Un accent, utilisé avec parcimonie — le curseur, le seul mot portant la phrase, un état qui change. Pas de décoration.
Rien dans le cadre qui ne soit pour le public. Adresses e-mail réelles, noms d'organisation, la propre conversation de l'opérateur avec un assistant intégré à l'application, badges de mode développeur. Préférez les propres mécanismes de l'application (cookies pour un panneau latéral réduit ou une bannière ignorée) aux hacks CSS — pas de flash à la caméra.
Une « scène clé » mérite le plus de temps. Choisissez le seul moment montrant quelque chose que les concurrents ne peuvent structurellement pas faire, puis : verrouillez la caméra, pas de coupes, pas de texte dessus, et laissez la musique s'arrêter. L'immobilité dit à l'œil que le sujet bouge, pas la caméra. Mettez la ligne explicative après — le texte avant la réaction dit aux gens quoi penser.
La lisibilité prime sur la couverture. Une prise répartie sur quatre écrans peut rendre aucun d'entre eux lisible. S'attarder sur un seul est généralement mieux.
Construisez le battement plutôt que de le filmer quand l'interface ne peut pas l'exécuter. Un écran statique ne peut pas introduire trois choses une par une. Si le battement a besoin d'un séquençage que le produit ne fait pas, composez-le nativement dans Remotion en utilisant le vrai texte et les tokens du produit — et gardez-le visuellement distinct de la séquence pour qu'il soit lu comme un explicateur, pas une fausse capture d'écran.
S'il n'y a pas de voix off, les cartes portent 100% du sens. Prévoyez du temps de lecture.
Discuter avec l'utilisateur, ne pas décider seul
- Slogans et texte à l'écran. Proposez plusieurs options regroupées par angle, avec une recommandation et le raisonnement. Notez que le film veut un indice de démonstration tandis qu'un titre de site web veut une déclaration de positionnement durable — ils ne doivent pas nécessairement être la même ligne.
- Quel contenu mettre en avant — montrez le contact sheet.
- Musique. Vous ne pouvez pas licencier un morceau. Concevez le film pour fonctionner en silence et fournissez une fiche de repère (BPM, où tombe tout point culminant) pour qu'un morceau fourni puisse être déposé sur la grille plus tard. Quand un arrive, vérifiez sa forme avant de le placer : mesurez le volume dans le temps plutôt que de supposer qu'il a une intro ou un point culminant auquel s'aligner.
- Licence Remotion. Gratuit pour les particuliers, les organisations à but non lucratif et les entreprises de moins de 3 employés ; au-delà, une licence commerciale est requise. Signalez-le avant l'expédition.
Commandes (forme)
La capture ne démarre jamais un serveur et ne devine jamais un port — les URL d'authentification sont généralement figées.
# terminal 1 — le produit sert déjà
pnpm --filter <app> dev # ou équivalent
# terminal 2 — projet film
pnpm install
pnpm check-webgl # si du 3D
pnpm capture:<app> # ou une seule prise : pnpm capture:<app> <prise>
pnpm mezzanine:<app> # obligatoire — voir pièges
pnpm studio
pnpm render:<app> # mp4 / webm / loop / poster selon besoin
Authentification pour les surfaces connectées : préférez emprunter un cookie de session live plutôt que de créer un utilisateur jetable (les comptes vides forcent une génération de contenu lente avant chaque prise).
Expédition sur une page
Deux formes, et c'est le choix de l'utilisateur :
- Boucle courte, cliquer pour le film. Le plus léger — quelques centaines de KB en lecture automatique, le film complet n'est récupéré que si on clique.
- Intégration complète du film. Le fichier complet est téléchargé et lu automatiquement. Plus lourd, mais pas de deuxième chargement.
Pour l'une ou l'autre, le <video> ou le <img> doit être chargé par le navigateur — pas de fondations/firewalls qui interceptent et transforment la requête. Si c'est le cas, le fichier n'arrive jamais. Testez sur le site avant d'expédier.