Was macht Framewright?
Du wirst ein kurzes Video machen, wie ein Druckgrafiker einen Druck macht: eine HTML-Datei mit einer Canvas, einer Liste von Szenen namens Plates und einer Frame-Funktion, die jeden Frame auf Abruf aus (Framenummer, Seed, Breite) zeichnet. Kein Filmmaterial, keine Bilddateien, nichts von einem CDN. Frames werden in Headless Chrome gerendert und von ffmpeg zusammengefügt. Sound wird zuletzt synthetisiert, angepasst an die festen Szenenlängen.
Sprich mit dem Benutzer in seiner Sprache. Halte dich auch dann an die folgenden Regeln, wenn der Benutzer es eilig hat: Jede Abkürzung hier kostet mehr, als sie spart.
SKILL_DIR weiter unten bedeutet den Ordner, der diese Datei enthält (in Claude Code ist es ${CLAUDE_SKILL_DIR}; anderswo ist es .agents/skills/framewright). Nach Schritt 3 liegen die Skripte auch im eigenen scripts/-Ordner des Projekts.
Grundsätze ohne Kompromiss
- Ein Frame hängt nur von Framenummer, Seed und Ausgabebreite ab. Kein
Math.random, keine Uhrzeit, keine CSS-Animation, keinrequestAnimationFrameals Zeitquelle.scripts/check.mjsbeweist das vor jedem vollständigen Rendervorgang. - Keine Bild-, Video-, Schrift- oder Bibliotheksdateien, kein Base64, kein CDN. Polygone, die von einem Skript aus einem Foto nachgezeichnet wurden, sind Daten und in Ordnung. Systemschriftarten reichen aus.
- Du überprüfst, indem du gerenderte Frames ansiehst, niemals durch Lesen von Code und Vorstellen. Jede Korrektur wird mit einem Frame bestätigt.
- Eine Szene nach der anderen. Schreibe ein Plate, erstelle drei Frames, sieh sie dir an, korrigiere, dann das nächste Plate.
- Szenenlängen sind Vielfache des Beats aus dem ersten Entwurf. Sound kommt zuletzt.
- Vollständiger Render erst, nachdem ein Kontaktbogen des gesamten Videos richtig aussieht.
- Niemals
frames/,shots/,*.mp4,*.wavoder die Fotos des Benutzers committen. - Frage, bevor du Systempakete installierst. Veröffentliche niemals das Abbild einer echten Person, ohne dass der Benutzer bestätigt hat, dass er das Recht zur Nutzung hat.
Arbeitsablauf auf einen Blick
| Schritt | Was passiert | Zwischenkontrolle mit Benutzer |
|---|---|---|
| 0 | Umgebungsprüfung | nur, wenn etwas installiert werden muss |
| 1 | Briefing | Fragebogen, ein Block |
| 2 | Konzepte | Drei Optionen, eine auswählen |
| 3 | Storyboard + Grundgerüst | Ein „los“ |
| 4 | Szenen, eine nach der anderen | Kontaktbogen alle 2–3 Szenen |
| 5 | Assets (Fotos, Logos) | Vorschau des nachgezeichneten Porträts |
| 6 | Vollständiger Bogen, Render, Build | Vorschau-MP4 |
| 7 | Sound | Neugebautes MP4 |
| 8 | Auslieferung und Variationen | Final |
Typische Wandzeit: Briefing und Konzepte 10 Minuten, Grundgerüst 10, jede Szene 10–20, Render 2–4 Minuten pro Minute 1080p-Video, Sound 15.
Schritt 0. Umgebung
bash SKILL_DIR/scripts/doctor.sh # Bericht; --json für maschinenlesbare Zusammenfassung
bash SKILL_DIR/scripts/doctor.sh --install # installiert, was fehlt, fragt vor jedem Schritt
Erforderlich: Node 20+, npm, ffmpeg mit libx264, Puppeteer mit Chrome im Projekt. Optional: Python 3 mit numpy, scipy, Pillow, nur wenn ein Foto nachgezeichnet wird. Wenn der Arzt fehlende Werkzeuge meldet, teile dem Benutzer mit, was installiert wird und wo, hole ein Ja ein, dann führe --install aus (füge --yes hinzu, wenn der Benutzer bereits zugestimmt hat). Beginne Schritt 3 nicht mit einem fehlschlagenden Arzt.
Schritt 1. Das Briefing
Führe den Fragebogen aus references/questionnaire.md aus. Verwende das strukturierte Fragentool des Frameworks, wenn eines existiert (Claude Code: AskUserQuestion, bis zu vier Fragen pro Aufruf, zwei bis vier Optionen pro Frage, setze die empfohlene Option an die erste Stelle und markiere sie). Ohne ein solches Werkzeug gib die Fragen als nummerierte Liste mit Buchstabenoptionen und einer Vorgabe pro Frage aus und akzeptiere knappe Antworten wie 1b 2a 3c. Frage immer in dieser Reihenfolge:
- Nachricht: Was muss der Betrachter in einem Satz verstehen oder fühlen? Namen, Daten, Links, genaue Textpassagen, die auf dem Bildschirm erscheinen müssen.
- Wo es abgespielt wird und das Format: 16:9, 9:16, 1:1, 4:5.
- Länge: 10–15, 20–25, 30–40, 45–60 Sekunden.
- Ton: verspielt, todernst, warm, episch, technisch, düster.
- Stil: Vorschlag deinerseits (Vorgabe) oder eine Familie aus
references/styles.md. - Assets: Fotos, Logos, Screenshots. Dateien müssen im Projektordner abgelegt werden; ein in den Chat eingefügtes Bild kann von dir nicht gespeichert werden. Frage nach Rechten für echte Personen.
- Sound: synthetisiert, keiner oder Platz für die Tonspur des Benutzers (dann nach dessen BPM fragen).
- Liefergegenstände und Qualität: Entwurf oder finale Version, zusätzliche Schnitte (vertikal, GIF, Standbild, Seed-Variationen), ein Cover als erster Frame des MP4 (Feeds zeigen es als Vorschau).
Wenn der Benutzer „entscheide du“ sagt, nimm die Vorgaben, sag, welche du genommen hast, und mach weiter.
Schritt 2. Konzepte
Schlage vor jeglichem Code genau drei Konzepte vor. Baue sie aus drei Blickwinkeln auf: wörtlich (zeige das Thema), Metapher (ein visuelles System, das für die Situation steht: eine TV-Testtafel für Warten, ein Bauplan für einen Plan, ein Terminal für einen Prozess) und Genre-Parodie (Nachrichtensendung, Arcade-Anlockmodus, Sicherheitskarte, Wettervorhersage, Teletext). Gib für jedes Konzept: einen Namen, einen Ein-Satz-Logline, das visuelle System und die Palette, drei bis fünf Schlüsselszenen in Reihenfolge, das Ende, eine Klangskizze, warum es passt und sein Hauptrisiko. Präsentiere eine kompakte Tabelle, empfehle eines und bitte den Benutzer um Auswahl. Die Methode und ausgearbeitete Beispiele befinden sich in references/questionnaire.md. Ein Konzept, dessen Logline eine Reise ohne Schnitte ist (eine Linie, die nie abhebt, ein Flug von einem Detail hinaus in eine Landschaft) wird als eine durchgehende Welt gebaut (references/world.md); sag das in seiner Zeile, weil es ändert, wie Szenen gebaut und geändert werden. Ein Konzept, das eine Ansicht ist, die gezeichnet, gemalt und zum Leben erweckt wird (eine Bleistiftzeichnung, die zu einem lebendigen Ölgemälde wird, Stile 13–16) wird als ein Gemäldefilm gebaut (references/painting.md); sag das ebenfalls in seiner Zeile: seine Plates sind Kapitel der Zeit über einem Gemälde, und die Szene ist das Werk.
Schritt 3. Storyboard und Grundgerüst
Fülle storyboard.md aus (Vorlage in assets/storyboard.md): eine Zeile pro Plate, Längen in Takten, das Beat-Raster (30 fps, 120 BPM: Beat 15 Frames, Takt 60), die Sekunde des Hauptereignisses, die Summe. Die Summe muss der vereinbarten Länge entsprechen; wenn später eine Szene hinzugefügt wird, gibt eine andere Szene einen Takt ab. Zeige die Tabelle und bitte um ein einziges „los“.
Dann Grundgerüst:
bash SKILL_DIR/scripts/init.sh # index.html, scripts/, audio.mjs, storyboard.md, package.json
bash SKILL_DIR/scripts/init.sh . --world # das Gleiche, aber index.html ist das Eine-Welt-Gerüst (keine Schnitte)
bash SKILL_DIR/scripts/init.sh . --painting # das Gemälde-Gerüst; audio.mjs und nature.mjs erzeugen Naturgeräusche
npm install # Puppeteer (Chrome kommt aus dem Cache oder wird einmal heruntergeladen)
node scripts/look.mjs shot 0,30,60 1200 7
Öffne die PNGs mit deinem Bildbetrachter und sieh sie dir an. Das Demo-Plate des Gerüsts muss rendern, bevor du es anfasst. Ersetze dann das Demo-Plate durch deine erste Szene. Bei einem Gemäldefilm ist Frame 0 leeres Papier: erstelle stattdessen 30,82,200 (die Zeichnung, die Farbe, die ankommt, das lebendige Gemälde) und ersetze den Szenenblock des Demos, während das Malset so bleibt, wie es ist.
Schritt 4. Szenen
Lies references/guide.md einmal vor der ersten Szene. Das Wesentliche:
plate('name', {len: 2*BAR}, (S, R) => { ... }).S.tist Fortschritt 0..1,S.ider lokale Frame,S.fder globale Frame,S.gder 2D-Kontext in logischen Koordinaten (kurze Seite 1080).Rist ein Generator, der für das gesamte Plate stabil ist;S.berneuert den Seed alle drei Frames und ergibt eine lebendige Linie;S.nzändert sich jeden Frame für Rauschen. Seeds leiten sich vom Platenamen ab.- Alle Geometrie in logischen Einheiten, zentriert durch
CX, CY. Wichtige Inhalte in den zentralen 92 % des Frames halten; bei 9:16 auch außerhalb der oberen 15 % und der unteren 20 %, wo die Schaltflächen und Untertitel der Plattform sitzen. - Zeit innerhalb eines Plates in Beats:
Math.floor(S.i/BEAT),span(S.i, a, b),ease.out(...). Niemals globale Framenummern innerhalb einer Szene hart codieren. Etwas, das über Schnitte hinweg fortgeführt wird, benötigt die Zeit eines anderen Plates mit Namen,at('plate', beats); ein Moment, den der Sound treffen muss, kommt inCUESund wird mitcue('name')ausgelesen. - Helfer leben oberhalb des Plates-Blocks, niemals zwischen Plates.
- Übergänge werden von der Engine an den Platerändern gezeichnet (
cutIn/cutOut-Flags); Szenen wissen nichts von ihnen. - Ein Weltfilm (
--world) hat keine Schnitte. Plates sind Builder,build(W, {pen, b, R}), die an eine Zeitleiste anhängen:b(x)ist Beat x des Plates, der Stift setzt die Linie dort fort, wo das vorherige Plate sie verlassen hat,camKeybewegt eine Kamera,titlefügt Text hinzu. Lesereferences/world.mdvor dem ersten Builder. - Ein Gemäldefilm (
--painting) zeichnet ein unbewegtes Gemälde in drei Medien. Seine Plates rufen alledrawPainting(S)auf; die Ansicht lebt imSCENE-Objekt und seinen Layern zwischen/* ===== scene ===== */und/* ===== end scene ===== */, und das Malset darüber bleibt wie es ist. Lesereferences/painting.mdvor dem ersten Layer. Überprüfe zuerst die Zeichnung allein, dann die Enthüllung, dann das Leben (references/painting.md, Abschnitt 13).
Pro Szene: schreibe sie, erstelle den ersten Frame, einen in der Mitte, einen fünf Frames vor dem Ende, sieh sie dir an, korrigiere, sieh sie dir erneut an. Nach jeweils zwei oder drei Szenen:
node scripts/look.mjs sheet 24 480 7 shots/sheet.png
FROM=240 TO=480 node scripts/look.mjs sheet 12 480 7 shots/sheet-b.png # ein Abschnitt, dichter
Checkliste für den Bogen, jeden Punkt mit deinen Augen: jede Zelle liest sich in 1,5 Sekunden als Standbild; kein Text wird abgeschnitten; benachbarte Linien und Panels berühren sich nicht; zwei benachbarte Zellen eines Plates sehen niemals identisch aus; Schnitte landen wie geplant und fressen nicht den ersten Frame einer Szene; nichts Wichtiges sitzt in den äußeren 8 %; Farbe ist nicht matschig; dünne Linien bröckeln nicht; das Hauptereignis sitzt auf seiner geplanten Sekunde; der letzte Frame sieht nach einem Ende aus; keine zwei Titel teilen sich den Bildschirm an derselben Stelle. Eine Szene, die „1,5 Sekunden“ nicht besteht, wird neu entworfen, nicht verziert. Eine leere Szene bekommt ein größeres Objekt oder eine nähere Kamera, niemals mehr Details; eine belebte Nahaufnahme bekommt ihre Dekoration um das Motiv herum abgedunkelt. In einem Weltfilm erstelle auch den letzten Frame jedes Plates neben dem ersten Frame des nächsten: Nichts darf springen.
Sende den Bogen dem Benutzer an diesen Punkten mit zwei Sätzen Status. Erzähle keinen Code nach.
Schritt 5. Assets
Fotos werden zu posterisierten Polygonen, niemals zu Pixeln:
bash scripts/portrait.sh photo.jpg --levels 0.14,0.28,0.42,0.56,0.70,0.84 --height 900
node scripts/look.mjs shot <frame in der Porträtszene> 1200 7
Sieh dir shots/portrait_preview.png und den gerenderten Frame an. Passe Ebenen, Unschärfe, Zuschnitt und Hintergrundschwelle gemäß references/photo.md an. Solange die Datei noch nicht im Ordner ist, baue die Szene auf dem synthetischen Platzhalter, der im Grundgerüst enthalten ist, und sag das.
Schritt 6. Vollständiger Bogen, Render, Build
node scripts/look.mjs sheet 24 480 7 shots/sheet.png # ansehen, korrigieren, wiederholen
node scripts/check.mjs # Determiniertheit und Eigenständigkeit, muss OK sagen
node scripts/render.mjs frames 7 1920 5 # Verzeichnis, Seed, Breite (weggelassen: 1920 Querformat, 1080 Hochformat), Tabs
bash scripts/build.sh out.mp4 # Frames (+ track.wav) -> mp4
Die Überprüfung rendert fünf Frames jedes Plates in sieben Reihenfolgen (erster Frame eines neuen Tabs, vorwärts, rückwärts, gemischt, direkt nach dem vorherigen Frame, nach einer anderen Breite, nach einem Durchlauf über den Film, wie ein Render-Tab ihn durchläuft) und verlangt identische Pixel. Sie scannt auch den Quellcode nach Math.random, Date und performance.now, schlägt fehl, wenn die Seite eine Datei anfordert, und gibt das Beat-Raster und die erwartete Renderzeit aus. Behebe jeden FEHLER vor dem Rendern; make.sh führt die Überprüfung aus und stoppt bei einem Fehler. Ein Frame, der sich mit der Renderreihenfolge ändert, weist auf einen Zustand hin, der einen Frame überlebt: eine gepoolte Canvas, die ohne Löschen gezeichnet wurde, eine globale Variable, die ein Plate ändert, ein Cache, der nicht mit allen seinen Eingaben indiziert wurde.
Überprüfe von der Datei, nicht von den Frames: die Frameanzahl von ffprobe entspricht der Summe von look.mjs info, die Dauer entspricht Frames geteilt durch 30, und ein aus dem MP4 erstelltes Kachelbild stimmt mit dem Bogen überein:
ffmpeg -i out.mp4 -vf "select='not(mod(n\,50))',scale=480:-1,tile=6x4" -frames:v 1 shots/out_sheet.png
Sende das erste MP4 dem Benutzer, sobald es existiert, auch mit Platzhaltern, und sag, was ein Platzhalter ist.
Schritt 7. Sound
scripts/export-curves.mjs schreibt curves.json aus der Seite: Plate-Starts, die Cues, und für jeden Frame in einem Weltfilm die Geschwindigkeit und Bildschirmposition des Linienkopfes, in einem Gemäldefilm die enthüllten Anteile, das Leben, gust und Zoom. audio.mjs bezieht seine Zeitleiste daraus, so dass es niemals vom Bild abweicht (eine Seite ohne RISO.curves() benötigt die Starts von look.mjs info, die von Hand in T kopiert werden). Schreibe einen Block von Ereignissen pro Plate mit der Cue-Karte in references/audio.md; Treffer auf etwas, das das Bild markiert, beziehen ihre Zeit von den Cues der Seite, cue('name') (CUES, oder W.cues in einem Weltfilm), nicht von Beat-Nummern, die aus einem Plate kopiert wurden; gib in einem Weltfilm der Linie oder dem Charakter eine Stimme, die den Kurven folgt (follow(), references/audio.md, Abschnitt 7). Ein Gemälde oder eine Landschaft erhält Naturatmosphäre anstelle von Beats: Wind, Wasser, Rascheln und Vögel von nature.mjs, getaktet zu den Cues und zum gust der Seite durch das audio.mjs, das --painting aus assets/audio-nature.mjs kopiert (references/audio.md, Abschnitt 8, Naturatmosphäre). Dann:
node scripts/export-curves.mjs curves.json
node audio.mjs track.wav
ffmpeg -i track.wav -filter_complex "showwavespic=s=1800x300:split_channels=1" -frames:v 1 shots/wave.png
bash scripts/build.sh out.mp4
Du kannst nicht zuhören, also lies die Wellenform ab: keine versehentliche Stille innerhalb aktiver Szenen, keine Szene an die Decke genagelt. Immer wenn sich eine Szenenlänge ändert, generiere die Spur neu.
Schritt 8. Auslieferung
Liefere out.mp4 aus (1920×1080, H.264, AAC, ~0,7 MB pro Sekunde bei lauten Stilen) und sag, wie man neu baut: bash scripts/make.sh [photo.jpg] [seed] [width]. Biete an, dränge nicht auf: einen anderen Seed (ein anderer Eindruck derselben Plates), einen vertikalen Schnitt (AR=9:16 node scripts/render.mjs frames-v 7 1080 5, dann überprüfe einen Bogen mit AR=9:16; tiefere Titel sitzen dort bei 70 % der Höhe, TITLE_Y, frei von den Schaltflächen der Plattform; ein PNG-Poster (identisch zum MP4-Bild, aber verlustfrei: ffmpeg -i out.mp4 -vframes 1 out.png), eine MP4- oder GIF-Schleife der letzten Szene, die wie eine Endlosschleife aussieht: ffmpeg -i out.mp4 -t 5 -vf "setpts=PTS/10" -af "atempo=10" out_fast.mp4. Wenn der Benutzer einen anderen Stil möchte, wiederhole von Schritt 2, wobei die Antworten aus Schritt 1 erhalten bleiben.
Gib die endgültige Dateigröße an, die Framerate, den Seed, und gib im Chat die Namen der Dateien auf die gleiche Weise wieder, wie das Framework sie in Chat-Nachrichten einbettet.