Que fait gfx-html ?
Les graphiques sont des pages HTML rendues une image à la fois par Chrome headless et encodées en une vidéo avec transparence. La requête complète se trouve dans $ARGUMENTS ; le premier token est généralement le slug du projet. Les noms de verbes suivent le contrat de build ; python engine/ve.py --help l'emporte en cas de conflit.
La règle qui fait fonctionner ce canal
Chaque graphique est une fonction pure de ?t=<secondes>. Pas d'animations CSS, pas de timelines GSAP, pas de requestAnimationFrame, pas de transitions. La page lit t depuis location.search, calcule sa mise en page et rend une seule image statique.
Ce n'est pas une préférence de style. chrome --headless --screenshot se déclenche "quand la page semble chargée" - il n'y a pas d'horloge virtuelle dans ce build, pas de Emulation.setVirtualTimePolicy, pas de beginFrame. Une page animée par CSS échantillonnée de cette façon renvoie un pixel différent à chaque exécution. Cela détruit la mise en cache par contenu (même clé, pixels différents) et produit un saccade visible.
Une fonction pure de t échantillonnée par la même boucle naïve est déterministe et mise en cache possible, et c'est toute la différence.
Le budget, annoncé d'avance
Chrome rend à ~430-470 ms par image en régime permanent, avec un démarrage à froid de 7,9 s. Un graphique de 5 secondes à 30 images/s donne 150 images, soit environ 65 secondes en mono-thread. Le moteur parallélise quatre processus Chrome et met en cache les PNG par image, donc un graphique inchangé se re-rend gratuitement - mais un premier rendu n'est pas instantané.
Informez l'utilisateur du nombre d'images et de l'estimation avant de lancer un long rendu. Privilégiez les graphiques courts : un bandeau bas qui tient 6 secondes n'a pas besoin de 6 secondes d'animation, il lui faut 0,4 s d'entrée, un maintien, et 0,4 s de sortie.
Étape 1 - choisir ou écrire un template
Les templates se trouvent dans presets/graphics/*.html. Réutilisez avant d'écrire. Un template est un seul fichier HTML autonome qui :
- Lit
t(secondes) et ses propriétés depuislocation.search. - Calcule toutes les positions/opacités à partir de
tavec de l'arithmétique simple. Les fonctions d'easing sont acceptables - elles sont pures. - Rendu sur un corps transparent. Ne jamais peindre une couleur de fond.
- Utilise les polices de
assets/fonts/via@font-faceavec une URLfile:///. Ne pas compter sur les polices système - ce que Chrome résout sur cette machine n'est pas ce que le contrôle de rendu QC suppose. - Est lisible en 1920x1080 et à l'intérieur de la zone de sécurité des sous-titres de la plateforme. Vérifiez
output.platform: un bandeau bas qui se trouve dans le tiers inférieur est sous l'UI TikTok.
Les détails de mise en page, les conventions d'easing et le contrat de propriétés sont dans docs/graphics.md. Ne les répétez pas ici.
Étape 2 - rendre les images
python engine/ve.py gfx <slug>
Pour chaque image, le moteur invoque Chrome comme une liste d'arguments, approximativement :
chrome.exe --headless=new --disable-gpu --no-sandbox --hide-scrollbars
--user-data-dir=<win-path>\prof --default-background-color=00000000
--window-size=1920,1080 --screenshot=<win-path>\frame_0007.png
"file:///C:/.../template.html?t=0.233&title=..."
Deux flags portent tout le canal :
--default-background-color=00000000est ce qui produit un véritable alpha. Vérifié : le pixel du coin lit0 0 0 0. Sans cela, vous obtenez un noir opaque et la superposition couvre la vidéo.- L'URL
file:///doit être un vrai chemin Windows. Un chemin Git Bash$PWDéchoue silencieusement - trois fichiers HTML différents ont produit le même md5 d'une page d'erreur vierge en mode sombre, exit 0, taille de fichier plausible. C'est pourquoi le contrôle QC (g) compare avec le hash de page vierge connu.
Les images sont mises en cache sous .cache/html/ indexées par sha256(html_content + t + params + renderer + renderer_version). Modifier le template invalide chaque image, ce qui est correct.
Étape 3 - encoder avec alpha intact
Même commande. ve gfx rend les images et les encode en un seul passage - il n'y a pas de verbe d'encodage séparé, car une séquence PNG sans film à côté est un artefact à moitié fini que rien en aval ne peut utiliser.
Séquence PNG vers QuickTime RLE (qtrle, argb). ProRes 4444 et une séquence PNG brute préservent également l'alpha correctement. VP9 dans WebM ignore silencieusement l'alpha dans ce build, même avec -metadata alpha_mode=1 - ne l'utilisez pas, et ne l'acceptez pas non plus d'un fichier utilisateur sans sondage.
La sortie va dans .cache/ovl/<xx>/<key>.mov avec un fichier annexe <key>.json.
Étape 4 - ancrer la superposition
Écrivez l'entrée overlays[] : id, kind: "html", template, props, anchor, duration_frames, layer, render.
anchor est toujours {segment_id, offset_frames} - jamais une position absolue dans la timeline. Les positions dans la timeline sont dérivées d'une somme cumulée sur les segments activés. Dès que quelqu'un désactive un segment en amont, chaque position absolue est erronée et chaque graphique dérive. Une superposition ancrée se déplace automatiquement avec son segment, ce qui est le comportement attendu par un monteur.
layer ordonne la composition ; plus haut est au-dessus. Les sous-titres sont rendus en dernier et se placent au-dessus des graphiques.
Étape 5 - vérifier avant de déclarer terminé
Trois vérifications, toutes peu coûteuses :
- Sonde alpha. Lisez un pixel rgba brut depuis une image de coin et vérifiez que
a = 0. QC (d). - Vérification de page blanche. Vérifiez que les images ne sont pas chacune une couleur unie. QC (g). Cela détecte une mauvaise URL
file:///, une police qui n'a pas pu se charger et une erreur JS, situations qui sinon sortent avec un exit 0 et un fichier plausible. Elle échantillonne la première, la troisième et la troisième-quart images et échoue seulement si toutes sont uniformes - chaque template ici s'ouvre à opacité 0, donc l'image 0 d'un graphique fonctionnel est légitimement vierge et juger uniquement sur celle-ci ferait échouer un rendu correct.
ve gfx exécute les deux automatiquement sur ce qu'il vient de produire et sort avec exit 2 si l'une ou l'autre échoue.
3. Déterminisme. Rendez une image deux fois et comparez les hashs. S'ils diffèrent, quelque chose dans le template n'est pas une fonction pure de t - trouvez-le. Cette vérification existe car l'échec qu'elle détecte est invisible jusqu'à ce qu'un rendu mis en cache soit en désaccord avec un rendu frais.
Ensuite, montrez à l'utilisateur une image fixe au point médian. Ne décrivez pas un graphique en prose quand une capture d'écran est à une commande.
Modes d'échec
| Symptôme | Cause | Correction |
|---|---|---|
| La superposition couvre la vidéo en noir | --default-background-color manquant, ou le template peint un fond | Ajoutez le flag ; rendez le corps transparent |
| Chaque image identique, taille de fichier plausible | Page d'erreur vierge due à une mauvaise URL file:/// | Utilisez un vrai chemin Windows ; testez par rapport au hash de page vierge |
| Même clé, pixels différents entre exécutions | Le template n'est pas une fonction pure de t | Supprimez toute animation CSS et rAF |
| Le texte s'affiche avec une police incorrecte | Police système de secours | @font-face depuis assets/fonts/ avec une URL file:/// |
| Alpha perdu après encodage | VP9/WebM | Utilisez qtrle argb ou ProRes 4444 |
| Le graphique dérive après une modification en amont | Ancrage absolu dans la timeline | Ancrez à {segment_id, offset_frames} |
| Le rendu prend des minutes | 430 ms/image est le minimum | Raccourcissez le graphique ou acceptez-le ; le cache rend la deuxième exécution gratuite |