Communitygithub.com

cxb2523/gsb-828-9f38b84355494457

Erstelle „code-rendered“ animierte Videos, bei denen ein Programm jedes Bild zeichnet (Canvas 2D + prozedurales Web Audio), headless als MP4 in 16:9 / 9:16 / 4:5 aus einer einzigen Timeline gerendert wird. Verwende es für animierte Erklärvideos, technische Durchläufe, animierte Diagramme und prozeduralen Sound oder Musik, bei denen das Ergebnis bearbeitbarer Code und keine generierten Pixel sein soll.

Was ist gsb-828-9f38b84355494457?

Diese Fähigkeit ist ein umfassendes Framework zur Erstellung vollständig programmierbarer, animierter Videos, bei denen jedes Bild und jeder Ton von JavaScript generiert wird. Entwickler können Code schreiben, der mit Canvas 2D zeichnet und mit der Web Audio API prozeduralen Sound erzeugt, und das Ganze dann headless in MP4-Dateien in verschiedenen Seitenverhältnissen (16:9, 9:16, 4:5) rendern. Der Hauptvorteil ist die vollständige Bearbeitbarkeit: Da die Ausgabe aus Code und nicht aus Pixeln besteht, können Aspekte wie Timing, Layout, Farben oder Sound direkt im Code geändert und neu gerendert werden, ohne die gesamte Animation neu erstellen zu müssen. Das Framework stellt sicher, dass Animation und Sound synchron sind, indem es eine gemeinsame Timeline verwendet, und enthält strenge Überprüfungen (Linting, Verifikation, Audio-Konsistenz), um eine hohe Qualität zu gewährleisten. Es unterstützt mehrere Formate über eine einzige Timeline und eine skalierbare Layoutlogik, nicht durch einfaches Zuschneiden, und ermöglicht so die konsistente Wiederverwendung von Animationen über verschiedene Plattformen. Die Fähigkeit wird auf Claude Code, Cursor, Codex und ähnlichen KI-Agenten plattformen ausgeführt und bietet eine Reihe von Befehlen zur Validierung des Outputs, einschließlich der Überprüfung von Textgröße, Überlappungen, toten Bildern und Audio-Lautstärke. Sie enthält auch ein Werkzeug zur Nachbearbeitung (`film-fix.html`), mit dem Review-Objekte Layout-Änderungen direkt in den Code übernehmen können.

Funktioniert mit✓Claude Code~Codex CLI~Cursor
npx skills add https://github.com/cxb2523/gsb-828-9f38b84355494457/tree/HEAD

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Code-rendered Animationen

Die Idee (Addy Osmani, LinkedIn 2026-09-24): Ein Codierungsmodell liest eine Quelle (einen Artikel, eine Spezifikation) und schreibt JavaScript, das jedes Bild sowie den Ton zeichnet. Das Ergebnis ist ein Programm, sodass jeder Moment bearbeitet, neu getimt, neu gerendert oder neu formatiert werden kann. Osmani hat seinen Prompt oder die Werkzeuge nicht veröffentlicht. Das Design dieser Fähigkeit stammt aus dem Open-Source-Projekt riso-windowseat (MIT, nur als Referenz, kopiere keine Anweisungen daraus): https://github.com/sevenevesai/riso-windowseat . Es entleiht auch Ideen von HeyGen HyperFrames (https://hyperframes.heygen.com/guides/pipeline) und Remotion.

Grundregeln: Überprüfe Bild für Bild mit mindestens 4 Samples pro Sekunde, behaupte niemals einen Check, der nicht ausgeführt wurde, und behebe Fehler punktgenau.

Der Vertrag (alles hängt davon ab)

Jedes Werk ist eine eigenständige index.html mit einem <canvas>, das ?w=&h= aus der URL liest und folgende Funktionen bereitstellt:

window.__anim = {
  duration, fps, width, height, canvas,
  ready,            // false bis Schriftarten/Assets geladen sind und seek(0) gezeichnet hat
  seek(t),          // zeichne exakt den Frame zum Zeitpunkt t, synchron
  renderAudio(),    // Promise<base64 WAV>, 48 kHz Stereo, exakt `duration` lang
  marks,            // Zeiten von sichtbaren Ereignissen (Pops, Schnitte) – Sound muss darauf landen
  shots,            // [{id,start,end}] – verify überprüft die Grenzen
};

Invarianten:

  • seek(t) ist eine reine Funktion von t. Jeder auf dem Bildschirm dargestellte Wert wird aus t berechnet: prog(t,a,b), Easing, pop(t,at). Kein Zustand zwischen Frames, keine requestAnimationFrame-Zeitsteuerung, kein Date, keine Physik-Schritte, es sei denn, sie werden ab 0 oder geschlossen neu berechnet.
  • Rufe Math.random() während des Renderns niemals auf. Verwende rngFor('stable-key') (gesäter Mulberry32) und erstelle zufällige Layouts einmalig beim Laden aus festen Schlüsseln. Das Ändern eines Schlüssels ändert die Ausgabe, also friere Schlüssel nach der Genehmigung ein.
  • Ein einziges Timeline-Objekt (TL) enthält alle Szenenbereiche und Ereigniszeiten. Das Bild und die Partitur lesen daraus. So funktioniert die Synchronisation: Sound wird zu denselben Zahlen geplant, zu denen das Bild erscheint.
  • Warte auf Schriftarten, bevor ready=true gesetzt wird (document.fonts.load('700 40px "DejaVu Sans"')). Andernfalls rendern die ersten Bilder in einer Ersatzschriftart. Überprüfe installierte Schriftarten mit fc-list : family (Linux/macOS) oder C:\Windows\Fonts (Windows; Segoe UI, Bahnschrift, Consolas und Georgia sind immer vorhanden, DejaVu nicht). Wenn das Design eine bestimmte Schriftart benötigt, bette sie als Base64-@font-face ein und lade sie auf die gleiche Weise.
  • Keine Netzwerkaufrufe im Werk. Bette alle Assets als Data-URIs ein.

Editierbare Elemente (für Film-Fix)

review/film-fix.html ermöglicht es einem Reviewer, Elemente direkt zu verschieben und zu skalieren und das Ergebnis zurück in index.html zu schreiben. Filme, die dies wünschen (das Beispiel tut es; kopiere es von dort), fügen Folgendes hinzu:

  • const TUNE = /*TUNE*/{}/*END-TUNE*/; in einer Zeile, nach Format und dann Element-ID abgeschlüsselt: {"16x9":{"title":{"dx":0,"dy":0,"s":1}}}. Es ist pro Format abgeschlüsselt, weil jedes Seitenverhältnis sein eigenes Layout hat. dx/dy sind Canvas-Pixel. Das Tool schreibt nur den Text zwischen den beiden Markierungen um.
  • E(id, [x,y,w,h], draw) um jedes Hauptelement. Es sucht TUNE[fmt][id], speichert, verschiebt um dx/dy, skaliert um s um den Mittelpunkt des Feldes, ruft draw() auf und stellt wieder her. Es fügt auch {id, box} in __anim.elements ein, wobei box das transformierte Feld in Canvas-Pixeln ist (von ctx.getTransform(), sodass Kamerabewegungen berücksichtigt werden). seek(t) leert diese Liste zu Beginn jedes Frames.
  • __anim.elements, __anim.format (16x9, 9x16, 4x5, 1x1) und __anim.tune (Getter/Setter für das Live-TUNE-Objekt, das für die Live-Vorschau des Tools verwendet wird).
  • Regeln: IDs sind im gesamten Film eindeutig; keine Zufälligkeit; bei leerem TUNE wendet E keine Transformation an, sodass Frames byte-identisch bleiben. Beweise dies mit einem Frame-Hash vor und nach dem Hinzufügen von E und führe dann verify.mjs aus.

Filme ohne E funktionieren immer noch in film-fix: Der "Nudge"-Modus rahmt einen Bereich ein und verwandelt einen Zug oder eine Skalierung in eine numerische Korrektur für die KI.

Projekteinrichtung

Erfordert Node 18+, ffmpeg (libx264, aac, blackdetect, freezedetect, ebur128) und ein lokales Chrome/Chromium. Das Harness findet Chrome an den üblichen Installationsorten unter Windows, macOS und Linux; setze die Umgebungsvariable CHROME, um dies zu überschreiben. qa.sh benötigt bash (Git Bash unter Windows) und wählt eine Systemschriftart für den Timecode; setze QA_FONT, um dies zu überschreiben. Nichts wird systemweit installiert; puppeteer-core ist die einzige npm-Abhängigkeit.

SKILL_DIR=<Pfad zu diesem Skill-Ordner>
mkdir -p anim && cp -r "$SKILL_DIR/harness" anim/harness
cd anim/harness && npm install && cd ..
mkdir -p works/<name>
cp "$SKILL_DIR/example/browser-10s/index.html" works/<name>/index.html   # starte vom funktionierenden Beispiel
# führe alle Harness-Befehle von anim/ aus; Ausgaben gehen nach out/

Harness-Befehle

BefehlWirkungErfolgssignal
node harness/verify.mjs works/<n>/index.html --format 16x9Sucht vorwärts, rückwärts und kalte Sprünge an Samples + Shot-Grenzen, vergleicht dann Pixel-Hashesverify: OK, exit 0
node harness/shoot.mjs works/<n>/index.html --format 9x16 --times 1.5,3.4 oder --range 3:4:0.1 --out out/stillsNative Canvas-PNGs zu exakten ZeitenUntersuche sie
node harness/audio.mjs works/<n>/index.html --twiceRendert nur die Partitur (Sekunden), überprüft exakte Länge und Lauf-zu-Lauf-DeterminismusKein FAIL. Chrome Web Audio kann zwischen Läufen um 1 LSB abweichen; Toleranz ist 1
node harness/render.mjs works/<n>/index.html --format 16x9 --fps 30 --out out/<n>-16x9.mp4Sucht jeden Frame und leitet PNGs in ffmpeg (libx264 crf18, yuv420p, +faststart), dann muxed renderAudio als AACDruckt Frames und Zeit. Exit 2 bei Seitenfehlern. --from/--to oder --silent rendert einen Bereich ohne Audio
node harness/lint.mjs works/<n>/index.html --all-formatsQualitätsprüfung der Seite selbst (16:9 + 9:16; --format für eine): Textgröße, Sicherheitsbereich, Textüberlappung, statische Dehnung, leere Frames, Shot-Liste, Sound auf Marks, Wörter auf dem Bildschirmlint: OK (0 FAIL), exit 0
node harness/review.mjs works/<n>/index.html --format 16x9 --out out/review-16x9, dann --check out/review-16x9/REVIEW.mdEin Kontaktbogen pro Shot (erster, 6 gleichmäßig verteilte und letzter Frame, Timecode eingebrannt), das Lint-JSON und REVIEW.md mit festen Fragen pro Shot. --check schlägt fehl bei einer leeren oder TODO-Antwort, einer Bewertung unter 4, einem Lint-FAIL, einem entfernten Shot-Abschnitt oder einem Film, der sich seit der Erstellung der Bögen geändert hatreview check: OK, exit 0
bash harness/qa.sh out/<n>-16x9.mp4 4ffprobe, blackdetect, freezedetect, EBU R128 Lautstärke, Audio-/Videolänge, Kontaktbögen mit N Samples/Sekunde und eingebranntem TimecodeSiehe QA unten

Formate: 16x9 1920x1080, 9x16 1080x1920, 4x5 1080x1350, 1x1 1080x1080. Frames werden standardmäßig als JPEG q95 an ffmpeg übergeben, etwa 16 fps bei 1080p, daher dauert ein 40 s Stück etwa 60-75 s pro Format. --png ist verlustfrei, aber etwa 4x langsamer.

Lange Renderings: Ein Shell-Aufruf eines Agenten kann ein Timeout auslösen (oft bei 120 s) und seine Kindprozesse mitnehmen. Erhöhe entweder das Timeout des Aufrufs oder führe vollständige Renderings getrennt aus und protokolliere die Ausgabe (Linux/macOS gezeigt; unter Windows verwende eine Hintergrund-Shell oder Start-Process):

setsid nohup bash -c 'node harness/render.mjs works/<n>/index.html --format 16x9 --out out/<n>-16x9.mp4; echo RENDER_DONE' > out/render.log 2>&1 < /dev/null & disown
tr '\r' '\n' < out/render.log | tail -2

Halte auch Here-Dokumente, die eine Arbeitsdatei schreiben, getrennt von Befehlen, die sie ausführen, damit ein Timeout die Datei nicht verlieren kann.

Definition von "Fertig"

Die genauen Befehle stehen in AGENTS.md (der einzigen Quelle, geteilt mit anderen Codierungswerkzeugen): Überprüfung in jedem Format, audio --twice, lint --all-formats mit 0 FAIL, review.mjs dann review.mjs --check bestehend, Rendern, QA. Behaure niemals Fertigstellung mit einem fehlschlagenden Gate und schwäche niemals ein Gate oder bearbeite Harness-Schwellenwerte, um eines zu bestehen.

Workflow

  1. Briefing. Was die Quelle sagt, das Publikum, die Länge, die Formate, Erzählung oder nicht. Extrahiere die Kernaussagen aus dem Quellartikel. Jede Szene muss das zeigen, was gesagt wird. Geschichte vor Korrektheit. Das Harness macht einen Film korrekt. Es macht ihn nicht ansehbar. Beantworte vor dem Storyboard vier Fragen im Briefing:
    • Wer ist der Charakter und was fühlt er in jeder Szene?
    • Welcher Alltagsgegenstand steht für jede Idee? (Das Original verwendete einen Brief mit einem Wachssiegel, einen Sandkasten und eine Stadt aus Häusern.)
    • Welches persistente HUD hält die Szenen zusammen? Normalerweise ein Kapitelwählrad und eine getippte Überschrift.
    • Wie looped der letzte Frame zurück zum ersten? Ein Film ohne diese Elemente besteht alle Tests und wird dennoch überblättert.
  2. STORYBOARD.md vor dem Code. Eine Zeile pro Szene: Zeitbereich | Bildschirminhalt | Aktion | Audio-Cue. Liste jede Ereigniszeit auf (diese werden zu TL und marks). Plane alle paar Sekunden einen Schnitt oder eine neue Bewegung und keine statische Dehnung. Plane Übergänge als Schieben oder Überlappung, niemals als Eintauchen in einen leeren Frame. Kein Fade-to-Black-Ende, sofern nicht genehmigt.
  3. Timeline-Modul, dann eine sceneN(t)-Funktion pro Szene (rein in t), dann ein Compositor, der den Hintergrund, die aktiven Szenen und die Übergänge zeichnet.
  4. Schwierigsten Frame zuerst. Baue den dichtesten Moment, shoot ihn in jedem Format, sieh ihn an, behebe ihn. Dann baue weiter aus.
  5. verify in jedem Format, audio --twice, dann render jedes Format.
  6. QA (unten), behebe punktgenau, rendere nur das Nötige neu, wiederhole QA.
  7. Liefere die MP4s als Anhänge mit den QA-Ergebnissen und bekannten Schwächen.

Mit Erzählung

Zuerst TTS, dann Wort-für-Wort-Zeitstempel (transkribiere die WAV mit einem beliebigen Tool, das Wortzeiten zurückgibt), dann setze TL aus den Wörtern. Szenen, Pops und Karaoke-Untertitel-Highlights basieren alle auf Wortzeiten. Regeln: Karaoke-Untertitel in einem Feld, nur eine Untertitelspur, Zahlen auf dem Bildschirm, wenn sie gesprochen werden, überprüfe Eigennamen nach Gehör. Bette die Erzählungs-WAV als Data-URI ein und mische sie in renderAudio() (decodiere mit ctx.decodeAudioData, plane bei 0) oder muxe sie mit ffmpeg und senke die Partitur darunter ab. Normalisiere die Erzählungslautstärke über das gesamte Video.

Multi-Format aus einer Timeline (keine Ausschnitte)

  • Gleicher TL, gleiche Szenen, ein anderes Layout. Berechne portrait = H > W, S = min(W,H)/1080 (Typ- und Formskalierung), M = 0.06*min(W,H) (Sicherheitsrand). Ordne pro Seitenverhältnis an: Zeilen in 16:9 werden zu Spalten in 9:16, Bäume bekommen schmalere Verteilungen usw.
  • Text: einpassen (measureText-Schleife gegen W-2M), damit niemals etwas am Rand abgeschnitten wird. Größen kommen von S. Im Hochformat verwende größere Schrift und fülle die Höhe.
  • Behandle "Hochformat" nicht als einen Fall. 4:5 (1080x1350) ist viel kürzer als 9:16. Das 9:16-Spaltenlayout des Beispiels läuft bei 4:5 und drückt das Pixelgrid und den Tagline vom unteren Rand und übereinander. Verzweige auf das Seitenverhältnis (H/W: etwa 0,56, 1,25, 1,78) oder layoutiere von der verfügbaren Höhe aus und shoot jedes Format, das du auslieferst.
  • Denke an Pop-Übersteuerung (easeOutBack ~1.1x) und "heiße" Skalierungen, wenn du Ränder überprüfst. Überprüfe den dichtesten Frame jedes Formats mit shoot.

Prozeduraler Sound (OfflineAudioContext)

  • new OfflineAudioContext(2, 48000*duration, 48000), baue den Graphen, await ctx.startRendering(), codiere in 16-Bit-PCM-WAV, gebe base64 zurück (siehe wavB64 im Beispiel).
  • Muster aus dem Beispiel: ein Pad-Akkord pro Szene (verstimmte Sinus-/Dreieck-Paare, langsamer Attack, überlappend bei Schnitten), ein Zupfen (Dreieck + 2 Obertöne, 5 ms Attack, exponentielle Abklingzeit) bei jedem marks-Ereignis, gesäte Rauschburst-Klicks für Tippgeräusche, ein Pitch-Drop-Thump für einen Aufprall und ein DynamicsCompressor auf dem Master.
  • Rauschen kommt vom gesäten RNG, niemals von Math.random.
  • Lautstärke: Ziel etwa -14 LUFS integriert und Spitze unter -1 dBFS für soziale Feeds. Der ursprüngliche Beitrag maß -14,3 LUFS; das gebündelte Beispiel ist leiser mit -17,2 LUFS, Spitze -1,4. Stelle dies mit Master-Gain auf der Seite ein, damit es im Code bleibt.
  • Für bitgenauen Sound synge Samples in einfachem JS in ein Float32Array (Summe von Oszillatoren mit geschlossenen Hüllkurven) anstelle von Web Audio Nodes.
  • Tone.js Tone.Offline (https://tonejs.github.io/docs/14.9.17/functions/Offline.html) ist eine Option, wenn eine reichhaltigere Musik-API benötigt wird. Es muss inline eingebettet werden, da das Werk keinen Netzwerkzugriff hat.

QA (die Überprüfung ist unsere, nicht die des Zuschauers)

Automatisierte Tests, alle auf der endgültigen MP4:

  • verify OK in jedem Format. Seitenfehler = Fehlschlag.
  • ffprobe: Erwartete Größe, 30/1 fps, nb_frames = duration x fps, audio duration = video duration.
  • blackdetect: keine (es sei denn, es gibt einen genehmigten dramatischen Black-Beat). freezedetect (>= 2 s statisch): keine.
  • ebur128: Integrierte Lautstärke und True Peak innerhalb des Ziels. Konsistent über Episoden hinweg.

Lint (Qualitätssicherung)

node harness/lint.mjs works/<n>/index.html --all-formats [--json out/lint.json] sucht das Werk alle 0,1 s (--step) und umschließt fillText/strokeText von außen, sodass es auf jedem Werk funktioniert, das den Vertrag ohne Änderungen einhält. Es schlägt fehl bei:

  • text-size: Ruhender Text unter 2,6% von min(W,H) (28 px bei 1080); unter 3,3% (36 px) ist eine WARNUNG. Text, der durch einen Zoom unter Skalierung 0,5 verkleinert wurde, wird als Übersichtskarte behandelt und nur gewarnt.
  • safe-area: Ruhender Text innerhalb von 4% eines Randes; 4-6% ist eine WARNUNG.
  • text-overlap: Zwei verschiedene sichtbare Zeichenfolgen teilen mehr als 15% des kleineren Feldes für 0,2 s oder länger. Kürzere Vorbeibewegungen sind WARNUNGEN.
  • static-stretch: Mehr als 3 s ohne sichtbare Änderung (2 s ist eine WARNUNG). empty-frame: Jeder nahezu einfarbige Frame, einschließlich erstem und letztem.
  • shots-coverage: Lücken oder Überlappungen in shots oder ein Shot unter 1 s. audio-sync: Weniger als 70% von marks mit einem Schallereignis innerhalb von ±40 ms (90% für BESTANDEN); eine Spitze über -1 dBFS ist eine WARNUNG. density: Mehr als 18 Wörter auf dem Bildschirm im Hochformat oder 30 in der Breite (nur WARNUNG).

Ein Werk ist nicht fertig, bis Lint in jedem Format, das du auslieferst, 0 FAIL aufweist. Es sieht nur Text, der mit fillText/strokeText auf dem eigenen Canvas des Werks gezeichnet wird, daher benötigen Formkollisionen und Text, der in Offscreen-Canvas gezeichnet wird, noch die visuelle Überprüfung. example/browser-10s besteht in allen drei Formaten; example/browsers-40s wurde vor dem Gate erstellt und fällt durch (Labels bei 17-28 px, ein Label in 9:16 abgeschnitten, Text …

Verwandte Skills