gfx-html
Grafiken sind HTML-Seiten, die von Headless Chrome Einzelbild für Einzelbild gerendert und zu einem Alphafilm codiert werden. Der vollständige Anfrage-Text steht in $ARGUMENTS; das erste Token ist normalerweise der Projekt-Slug. Verben folgen dem Build-Contract; python engine/ve.py --help gewinnt bei Konflikten.
Die Regel, die diesen Lane zusammenhält
Jede Grafik ist eine reine Funktion von ?t=<Sekunden>. Keine CSS-Animationen, keine GSAP-Timelines, keine requestAnimationFrame, keine Übergänge. Die Seite liest t aus location.search, berechnet ihr Layout und rendert einen statischen Frame.
Dies ist kein Stil-Statement. chrome --headless --screenshot feuert "wenn die Seite geladen aussieht" – es gibt in diesem Build keine virtuelle Uhr, kein Emulation.setVirtualTimePolicy, kein beginFrame. Eine CSS-animierte Seite, die so abgetastet wird, gibt bei jedem Durchlauf andere Pixel zurück. Das zerstört das inhaltsadressierte Caching (gleicher Schlüssel, andere Pixel) und führt zu sichtbarem Ruckeln.
Eine reine Funktion von t, die von derselben naiven Schleife abgetastet wird, ist deterministisch und cachebar – das ist der ganze Unterschied.
Das Budget, gleich vorweg
Chrome rendert im eingeschwungenen Zustand mit ~430–470 ms pro Frame, plus 7,9 s Kaltstart. Eine 5-Sekunden-Grafik bei 30 fps hat 150 Frames – etwa 65 Sekunden single-threaded. Die Engine parallelisiert vier Chrome-Prozesse und cached die PNGs pro Frame, sodass eine unveränderte Grafik kostenlos neu gerendert wird – aber ein Erst-Render ist nicht sofort da.
Teile dem Benutzer die Frame-Anzahl und die Schätzung mit, bevor du eine lange Grafik startest. Bevorzuge kurze Grafiken: Ein Lower Third, der 6 Sekunden hält, braucht nicht 6 Sekunden Bewegung – er braucht 0,4 s rein, eine Haltephase und 0,4 s raus.
Schritt 1 – Vorlage auswählen oder schreiben
Vorlagen liegen in presets/graphics/*.html. Wiederverwenden vor Schreiben. Eine Vorlage ist eine einzelne, eigenständige HTML-Datei, die:
t(Sekunden) und ihre Props auslocation.searchliest.- Alle Positionen/Deckkräfte aus
tmit einfacher Arithmetik berechnet. Ease-Funktionen sind in Ordnung – sie sind rein. - Auf einem transparenten Body rendert. Niemals eine Hintergrundfarbe malen.
- Schriftarten aus
assets/fonts/über@font-facemit einerfile:///-URL verwendet. Verlasse dich nicht auf System-Schriften – was Chrome auf dieser Box auflöst, ist nicht das, was der QC-Kontroll-Render annimmt. - Bei 1920x1080 und innerhalb des Caption-Safe-Bereichs der Plattform lesbar ist. Prüfe
output.platform: Ein Lower Third, der im unteren Drittel sitzt, liegt unter der TikTok-Oberfläche.
Layout-Details, Easing-Konventionen und der Props-Contract stehen in docs/graphics.md. Wiederhole sie hier nicht.
Schritt 2 – Einzelbilder rendern
python engine/ve.py gfx <slug>
Pro Frame ruft die Engine Chrome als argv-Liste auf, etwa:
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=..."
Zwei Flags tragen den gesamten Lane:
--default-background-color=00000000bewirkt echten Alpha. Verifiziert: Der Eckpixel liest0 0 0 0. Ohne das Flag erhält man undurchsichtiges Schwarz und das Overlay deckt das Video ab.- Die
file:///-URL muss ein echter Windows-Pfad sein. Ein Git-Bash-$PWD-Pfad schlägt still fehl – drei verschiedene HTML-Dateien lieferten den identischen MD5 einer leeren Error-Seite im Dark Mode, Exit 0, plausible Dateigröße. Deshalb prüft QC (g) gegen den bekannten Leer-Hash.
Frames werden unter .cache/html/ gecached, Schlüssel ist sha256(html_content + t + params + renderer + renderer_version). Eine Änderung der Vorlage macht jeden Frame ungültig – das ist korrekt.
Schritt 3 – Mit intaktem Alpha codieren
Gleicher Befehl. ve gfx rendert die Frames und codiert sie in einem Durchgang – es gibt kein separates Codieren, denn eine PNG-Sequenz ohne dazugehöriges Video ist ein halbfertiges Artefakt, das niemand downstream verwenden kann.
PNG-Sequenz zu QuickTime RLE (qtrle, argb). ProRes 4444 und eine rohe PNG-Sequenz runden Alpha ebenfalls korrekt. VP9-in-WebM verwirft Alpha in diesem Build stillschweigend, selbst mit -metadata alpha_mode=1 – verwende es nicht und akzeptiere es nicht von einem Benutzer-Asset, ohne es vorher zu prüfen.
Die Ausgabe geht nach .cache/ovl/<xx>/<key>.mov mit einer <key>.json-Sidecar-Datei.
Schritt 4 – Das Overlay verankern
Schreibe den overlays[]-Eintrag: id, kind: "html", template, props, anchor, duration_frames, layer, render.
anchor ist immer {segment_id, offset_frames} – niemals eine absolute Timeline-Position. Timeline-Positionen werden aus einer laufenden Summe über aktivierte Segmente abgeleitet. Sobald jemand upstream ein Segment deaktiviert, ist jede absolute Position falsch und jede Grafik driftet. Ein verankertes Overlay bewegt sich automatisch mit seinem Segment – das ist das Verhalten, das ein Editor erwartet.
layer steuert die Compositing-Reihenfolge; je höher, desto weiter oben. Untertitel werden zuletzt eingebrannt und liegen über den Grafiken.
Schritt 5 – Prüfen, bevor es fertig gemeldet wird
Drei Prüfungen, alle günstig:
- Alpha-Test. Lies einen rohen RGBA-Pixel aus einem Eckframe und stelle sicher, dass
a = 0ist. QC (d). - Leerseiten-Prüfung. Stelle sicher, dass die Frames nicht alle eine einzige Farbe sind. QC (g). Dies fängt eine fehlerhafte
file:///-URL, eine Schriftart, die nicht geladen wurde, und einen JS-Fehler – alles Fälle, die sonst Exit 0 mit einer plausiblen Datei liefern. Es prüft den ersten, den mittleren und den Frame bei 3/4 und schlägt nur dann fehl, wenn alle gleichförmig sind – jede Vorlage hier startet mit Deckkraft 0, daher ist Frame 0 einer funktionierenden Grafik berechtigt leer, und ein Urteil allein aufgrund dieses Frames würde ein korrektes Rendering fälschlich ablehnen.
ve gfx führt beide Prüfungen automatisch direkt nach der Erstellung durch und beendet sich mit Exit 2, falls eine fehlschlägt.
3. Determinismus. Rendere einen Frame zweimal und vergleiche die Hashwerte. Unterscheiden sie sich, ist etwas in der Vorlage keine reine Funktion von t – finde es. Diese Prüfung existiert, weil der Fehler, den sie aufdeckt, unsichtbar bleibt, bis ein gecachtes Rendering von einem frischen abweicht.
Zeige dem Benutzer dann ein Standbild in der Mitte des Frames. Beschreibe eine Grafik nicht in Prosa, wenn ein Screenshot nur einen Befehl entfernt ist.
Fehlermodi
| Symptom | Ursache | Lösung |
|---|---|---|
| Overlay deckt Video mit Schwarz ab | --default-background-color fehlt oder die Vorlage malt einen Hintergrund | Flag hinzufügen; Body transparent machen |
| Alle Frames identisch, plausible Dateigröße | Leere Error-Seite aufgrund einer fehlerhaften file:///-URL | Echten Windows-Pfad verwenden; gegen den Leer-Hash prüfen |
| Gleicher Schlüssel, unterschiedliche Pixel zwischen Durchläufen | Vorlage ist keine reine Funktion von t | Alle CSS-Animationen und rAF entfernen |
| Text in falscher Schriftart | System-Schrift-Fallback | @font-face aus assets/fonts/ mit file:///-URL |
| Alpha nach Codierung verloren | VP9/WebM | qtrle argb oder ProRes 4444 verwenden |
| Grafik driftet nach einem Schnitt upstream | Absoluter Timeline-Anker | Auf {segment_id, offset_frames} verankern |
| Rendern dauert Minuten | 430 ms/Frame ist die Untergrenze | Grafik verkürzen oder akzeptieren; der Cache macht den zweiten Durchlauf kostenlos |