Animated Video Ad
Baue jede Einstellung eines Produktvideos als kleine HTML-Seite, die echtes Produktmaterial animiert (Screenshots, Videoframes, exportierte Daten, tatsächliche Zahlen), zeichne jede Seite als sauberes MP4 auf und übergib dem Nutzer ein Skriptdokument, das genau erklärt, wie die Clips geschnitten werden und was gesagt werden soll. Der Nutzer fügt die Clips zusammen und nimmt die Sprachaufnahme in seinem eigenen Editor (Camtasia, Premiere, CapCut...) auf.
Warum das besser funktioniert als eine Bildschirmaufnahme der echten App: Jede Einstellung ist für das Video kadriert, im Timing und Zoom optimiert; nichts flackert, lädt oder zeigt private Daten an; Text ist groß genug für ein Telefon; und eine Änderung („Mach diese Zeile kürzer“, „langsamerer Zoom“) ist eine Bearbeitung und Neuaufnahme, kein Neudreh. Es sieht trotzdem echt aus, weil die Pixel und Zahlen vom Produkt selbst stammen.
Arbeitsablauf
- Geschichte abstimmen (eine Nachricht, dann loslegen).
- Echtes Material aus dem Produkt sammeln.
- Eine Seite pro Einstellung aus
assets/bauen. - Seiten prüfen mit Screenshots, korrigieren, wiederholen.
- Clips aufnehmen mit
scripts/record.cjs; mit Kontaktabzügen überprüfen. - Das Skriptdokument schreiben (
references/script-doc.md). - Audio und Rough Cut: Musik (ElevenLabs, generiert oder eigene;
references/music.md), eine optionale ElevenLabs-Sprachaufnahme (references/voiceover.md); Vorschau mitscripts/roughcut.py. - Senden der Clips, des Dokuments und der Vorschau; iterativ durch Feedback Szene für Szene.
Alles in einem Ordner ablegen, den der Nutzer besitzt, z.B. video/ oder video-scripts/:
<name>-anim/ Seiten (eine .html pro Einstellung), base.css, logo, frames/ Unterordner
<name>-clips/ Aufgenommene MP4s: 01-hook-anim.mp4, 02-setup-anim.mp4 ... + README.md
<name>-music/ Hintergrund-WAVs, Roughcut-Vorschau, cuts.json
<Name>.md Das Skriptdokument
1. Geschichte
Wähle die Länge (Standard 30 s; biete einen 45–60 s Schnitt aus denselben Clips an) und den Aufruf zum Handeln (Call to Action).
Wähle das Bildformat basierend auf dem Ort, wo die Anzeige geschaltet wird, und baue die Seiten in dieser Größe (nicht 16:9 zuschneiden; Zuschneiden verliert Text und der Nutzer muss jede Einstellung neu kadrieren):
| Platzierung | Größe |
|---|---|
| Instagram Reels/Stories, TikTok, YouTube Shorts, Facebook Reels | 1080×1920 (9:16) |
| Instagram/Facebook Feed | 1080×1350 (4:5) |
| LinkedIn oder X Feed | 1080×1080 (1:1) oder 1080×1350 |
| YouTube, Webseite, Landingpage, Präsentationen, unspezifisch | 1920×1080 (16:9) |
Mehrere Platzierungen: Baue für die Hauptplatzierung und beschreibe im Dokument, wie für die anderen umgerahmt wird. Für vertikale Formate: Text außerhalb der oberen ~250 px und der unteren ~350 px halten, wo die Plattform ihre eigene UI zeichnet. Dann 6–9 Einstellungen skizzieren, ein Gedanke pro Einstellung, in diesem Bogen:
- Hook (≈3 s): das Problem, in den Worten des Betrachters. Eine Frage funktioniert („Rechnung nach 30 Tagen immer noch unbezahlt? Und jetzt?“).
- Setup (≈4 s): wie wenig es braucht, um loszulegen.
- Kernfunktionen (2–3 Einstellungen, 4–5 s pro Stück): das Produkt bei der Arbeit, wobei das Ergebnis auf dem Bildschirm erscheint.
- Proof (≈3 s): eine echte Zahl. Groß, einfach, wahr.
- Passt in deinen Workflow (≈3 s): Export, Integration, Teilen.
- Endkarte (≈3 s): Logo, einzeiliges Versprechen, CTA-Button, URL.
Mit Positivem führen. Zeige, was das Produkt gut kann und wie einfach es ist. Baue keine Einstellung um die Fehlerfälle oder Workarounds des Produkts, selbst nicht um einen cleveren; eine Anzeige, die erklärt, wie das Tool damit umgeht, wenn es falschliegt, liest sich als „es liegt oft falsch“. Eine schnelle, sichere Korrektur durch den Nutzer („falsche Kategorie? klick drauf, wähle die richtige, fertig“) ist in Ordnung; eine Einstellung über Randfall-Mechaniken nicht.
Wenn der Nutzer nichts gesagt hat, teile deinen Ablauf und deine Annahmen in einer kurzen Nachricht mit und beginne mit dem Bauen; es ist günstiger, eine aufgenommene Einstellung zu ändern, als sie abstrakt zu diskutieren.
In derselben Nachricht frage nach dem Audio (falls noch nicht erwähnt) und baue weiter, während sie entscheiden; Audio wird erst in Schritt 7 benötigt. Verwende das Fragen-Tool, falls vorhanden. Prüfe zuerst, ob ein ElevenLabs MCP verbunden ist (Tools wie creative_generate_speech, creative_list_voices, ein music-Knotentyp); falls ja, bevorzuge es.
Musik:
- ElevenLabs Music (empfohlen, wenn verbunden): du generierst einen instrumentalen Hintergrund, der auf den Schnitt abgestimmt ist, auf ihrem ElevenLabs-Konto. Nicht verbunden: du schreibst den Prompt, sie generieren es auf elevenlabs.io/music und senden die Datei zurück.
- Hier generieren: ein ruhiger, lizenzfreier Hintergrund, erstellt mit
make_bed.py, kostenlos und sofort. - Eigene Musik: ein Track, den sie bereits haben oder lizenziert haben.
Sprachaufnahme:
- ElevenLabs Stimme (wenn verbunden): du wählst eine Stimme und generierst den gelesenen Text, dann platzierst du jede Zeile an ihrem Stichwort. Der Nutzer kann später trotzdem selbst aufnehmen.
- Selbst aufnehmen nach dem sauber gelesenen Text im Dokument (Standard ohne ElevenLabs).
ElevenLabs verbraucht Credits auf ihrem Konto: Preise zuerst an (estimate_only) und nenne die Kosten in der Frage. Wenn sie nach Musik gefragt haben ohne Spezifikation, oder du zu Schritt 7 ohne Antwort kommst, generiere den kostenlosen Hintergrund, damit die Vorschau Ton hat, und wiederhole die Frage in der finalen Nachricht mit dem bereits geschriebenen ElevenLabs Prompt.
2. Echtes Material
Suche im Repository des Produkts, in Datenordnern, Exporten, Datenbanken, Test-Fixtures und Aufnahmen. Sammle für jede Einstellung:
- Bilder: Screenshots der echten UI, oder aus echtem Video extrahierte Frames
(
ffmpeg -ss T -i in.mp4 -frames:v N frames/f%03d.jpg). Mehrere Frames ergeben Bewegung. Für einen Screenshot einer Web-UI (lokales HTML oder URL), verwendescripts/grab.cjs: Vollansicht plus benannte Elemente in 3x, und es gibt die Box jedes Elements für die Kadrierung aus:node <skill>/scripts/grab.cjs app.html <pages>/shots --size 1280x800 goal=.goal logo="header .logo". Hinter einem Login, bitte den Nutzer um Screenshots, anstatt dich einzuloggen. - Zahlen: tatsächliche Werte aus der Ausgabe des Produkts (Zeiten, Anzahlen, Zeilen). Schreibe auf, woher jede stammt; das kommt in das Dokument unter „Woher die Zahlen kommen".
- Positionen: Koordinaten von Dingen, die hervorgehoben werden sollen, in Pixeln des Quellbildes (nach Möglichkeit aus der eigenen Ausgabe der App, z.B. Erkennungsboxen, oder durch Betrachten des Bildes gemessen).
Lücken sind normal; gehe offen damit um:
- Keine Logodatei: schneide es aus einem echten UI-Screenshot aus (Kopfzeile, App-Symbol) oder baue es exakt aus dem eigenen CSS/SVG des Produkts nach. Sage es im Dokument und frage nach der echten Datei.
- Ein benötigter Bildschirm existiert nicht in dem, was du hast (z.B. ein Setup-Formular): zeichne es im eigenen Stil des Produkts und kennzeichne die Einstellung als inszeniert im Dokument, damit der Nutzer den echten Bildschirm einfügen kann.
Erfinde niemals eine Statistik. Wenn ein Moment inszeniert werden muss (ein dramatisierter Hook), kennzeichne es im Dokument. Respektiere die eigene Terminologie des Produkts und Regeln für Aussagen (z.B. ein Produkt, das seine Zahlen niemals als „garantiert" oder „offiziell" bezeichnen darf) – überprüfe Projektdokumente/Speicher auf solche Regeln, bevor du Text formulierst.
3. Seiten bauen
Kopiere assets/base.css, assets/shot.js, assets/shot-template.html und assets/end-card.html in den Seitenordner. shot.js enthält die Hilfsfunktionen, die jede Seite verwendet (show/hide, cursorTo, click, typeText, countUp, frameTo, preload) und die Auto-Wiedergabe; lade es vor dem eigenen Skript der Seite und kopiere keine Hilfsfunktionen in die Seiten. Jede Seite:
- Ist eine feste Bühne in der gewählten Größe: setze
--w/--hinbase.css(z.B.1080px/1920px). Die Vorlagen sind für 16:9 ausgelegt, also positioniere für andere Formate neu (staple Panels vertikal für 9:16; bei vertikalen Formaten beginnen Titel unterhalb von ~250 px, nicht bei den 96 px der Vorlage). Textgrößen ≥ 26 px; Titel 80–100 px. Es wird auf einem Telefon angesehen. - Hat eine einzige Top-Level
async function start(), die die gesamte Zeitleiste enthält (await sleep(ms)zwischen den Beats).shot.jsstellt sie bereit; mit#manualin der URL wartet sie, normal geöffnet startet sie automatisch, sobald Schriftarten und Bilder bereit sind, damit der Nutzer durch Doppelklick eine Vorschau sehen kann. - Lädt jedes Bild vor, das später eingewechselt wird (
preload(frames)auf oberster Ebene), damit beim Filmen nichts nachgeladen wird; der Rekorder und der Checker warten darauf. - Endet in einem gehaltenen, vollständigen Zustand (Ergebnis gezeigt, aller Text an), damit der Editor irgendwo in der Nähe des Endes trimmen und das letzte Bild halten kann.
- Zeichnet Markierungen in einem
<svg viewBox="0 0 W H" preserveAspectRatio="xMidYMid slice">, das auf die Größe des Quellbildes skaliert ist, damit App-Koordinaten ohne Umrechnung übernommen werden und ausgerichtet bleiben, wenn die Bühnenform das Bild beschneidet. - Sieht aus wie das Produkt: ersetze die Tokens in
base.css(--accent,--hi,--ok,--ink, Schriftarten) durch die eigenen Farben und Schriftarten des Produkts. Bei einem Produkt mit hellem Farbschema wechsle auch die Textfarbe,.shade,.panelund.bgauf helle Versionen; eine dunkle Anzeige um eine helle App sieht aus wie ein fremdes Produkt. - Zeigt UI-Screenshots in ihrer nativen Pixelgröße innerhalb von
.world(setze.world { transform-origin:0 0 }und das<img>auf seine natürliche CSS-Größe) und rahme sie mitframeTo(x, y, scale), das den Screenshot-Punkt (x, y) in die Bühnemitte setzt; Boxen vongrab.cjsliefern die Punkte. Nichtobject-fit:cover, das einen Screenshot unvorhersehbar zuschneidet, wenn seine Form nicht zur Bühne passt. - Hält Produkttext lesbar: UI-Text, der auf der Bühne unter ~22 px liegt (ein 14 px Label in einem Screenshot, der nicht gezoomt ist), ist auf einem Telefon unlesbar. Zoome darauf, oder baue dieses Stück (eine Karte, einen Alert) im eigenen Stil des Produkts mit exaktem Text in lesbarer Größe nach und teile im Dokument mit, welche Stücke nachgebaut wurden.
Bewährte Einstellungsmuster:
| Muster | Wie |
|---|---|
| Produkt in Aktion | Framesequenz in <img> alle 33–40 ms eingewechselt, Boxen/Labels pro Frame gezeichnet, eine Ergebniszeile taucht im Moment ihres Auftretens in einem Seitenpanel auf. |
| Zoom zum Detail | .world mit transform-origin auf dem Detail, füge .zoom hinzu (Skalierung 1.5–1.6, 1.2 s ease). |
| Benutzer macht einen Schritt | await cursorTo(button) dann await click(button): der Cursor gleitet zur gemessenen Mitte des Elements, kräuselt sich, der Button wird gedrückt, dann das Ergebnis. Tippe mit typeText(el, text) (55 ms pro Zeichen; halte jeden getippten String unter ~1 s). |
| Durch Frames schreiten | Vor-/Zurück-Buttons im Rhythmus gedrückt, Frame-Nummernanzeige, der entscheidende Frame markiert. |
| Zwei Ansichten stimmen überein | Zwei Bilder nebeneinander, der gleiche Moment in beiden markiert, ein Badge, das die Übereinstimmung angibt. |
| Proof | Tabellenzeilen gleiten herein (120 ms Abstand), dann 2–3 Statistik-Karten; zähle die Heldennummer hoch mit countUp(el, n) (~700 ms). |
| Datenausgabe | Ergebnisliste und ein Datei-/Integrationspanel füllen sich im gleichen Beat, mit einem „Live"-Indikator. |
| Endkarte | Linien-Sweep, Logo, Tagline, CTA-Button erscheint, URL. |
Richte den Cursor auf Elemente, niemals auf manuell eingegebene Koordinaten: cursorTo(el) misst das Element, wenn der Cursor sich bewegt, sodass ein Zoom, ein neues Layout oder eine andere Bühnengröße nicht dazu führen kann, dass er leeren Raum anklickt. click(el) protokolliert einen Fehler, wenn die Spitze nicht auf el ist, und check.cjs schlägt fehl. Verwende cursorTo({x, y}) nur für einen Punkt, der kein Element ist. Mache einen Check-Screenshot im Moment jedes Klicks: Ein Klick, der sitzt, ist das Einzige, was Zuschauer bemerken, wenn er falsch ist.
Zeigen und verstecken mit show(el) / hide(el) aus shot.js. .on verwendet !important, sodass jede andere Klasse, die daneben hinzugefügt wird, um das Element zu verstecken oder zu bewegen, stillschweigend nichts bewirkt; entferne zuerst immer .on (das macht hide). Für einen benutzerdefinierten Ausgang, el.classList.replace('on', 'my-exit').
Halte die Zeitleiste flott: 0.3–0.7 s zwischen Beats, ≈1 s zum Lesen eines Titels. Jeder Clip ist normalerweise 4–8 s; eine schrittweise Einstellung kann 15 s laufen, weil der Editor sie trimmen und beschleunigen wird.
Beschneide alles, was die Quelle eingebrannt hat und nicht gezeigt werden soll (Kameradatum/-uhrzeit, Statusleisten, private Namen), indem du .world leicht vom gegenüberliegenden Rand skalierst (z.B. scale(1.08) von unten). Verlasse dich nicht auf backdrop-filter, um Dinge zu verstecken: Es wird im Screencast nicht gerendert.
4. Prüfen vor der Aufnahme
node <skill>/scripts/check.cjs <pages-dir> <chk-dir> hook:400:1500:3200 live:800:3000:6000
Macht Screenshots zu diesen Millisekunden nach start() (es wartet bewusst nicht auf start(); das Warten würde nur den Endzustand zeigen) und gibt Seitenfehler aus. Lies die Screenshots. Überprüfe insbesondere:
- Markierungen sitzen auf dem richtigen Ding (Box auf der richtigen Person, eine Linie auf der Vorderkante, nicht auf einem nachlaufenden Fuß oder einem Unbeteiligten; Detektor-Spuren können mitten in der Sequenz zu jemand anderem wechseln, also Boxten nähen oder von Hand korrigieren);
- nichts ist an den Rändern abgeschnitten, Text überlappt nicht das Motiv;
- jede Zahl auf dem Bildschirm stimmt mit der Quelle überein.
5. Aufnehmen
node <skill>/scripts/record.cjs <pages-dir> <clips-dir> hook=01-hook-anim=4200,setup=02-setup-anim=5000
page=outName=ms: nimm für ms auf (Animationslänge + ~1 s gehaltenes Ende). Jeder Clip kommt in der Größe seiner Seite heraus (--w×--h), 30 fps, H.264 CRF 16, stumm. check.cjs und roughcut.py folgen der gleichen Größe, daher sind keine Skriptbearbeitungen für vertikale oder quadratische Formate erforderlich. Benötigt Chrome (oder Playwrights Chromium) und Playwright (npm i -g playwright einmal, wenn pw.cjs es nicht findet), plus ffmpeg.
Clips laufen die angeforderte ms plus ~0,3 s. Der erste Frame ist die Seite vor dem ersten Beat von start(), also beginnen In-Punkte normalerweise bei 0,1–0,3 s; lies sie vom Kontaktabzug ab.
Überprüfe jeden Clip, ohne ihn anzusehen:
bash <skill>/scripts/sheet.sh <clips-dir>/03-live-anim.mp4 sheet.jpg 2
Lies den Kontaktabzug: Timing jedes Beats, nichts gestaucht oder abgeschnitten, letzter Frame vollständig. Notiere die In-/Out-Sekunden des nutzbaren Teils jedes Clips; sie kommen in die Schnitttabelle.
Benenne Clips NN-name-anim.mp4 in der Reihenfolge der Geschichte (04b, 04c für Alternativen) und schreibe eine kurze README.md im Clips-Ordner, die sie auflistet.
6. Skriptdokument
Folge references/script-doc.md. Die Schnitttabelle (Zeit, Clip, In–Out, Geschwindigkeit, Was du siehst, VO-Start, Zeile) ist das Herzstück; der Rest ist, was der Nutzer braucht, um ohne dich fertig zu werden. Sprachaufnahme: ~2,5 Wörter pro Sekunde, gesprächig, eine Zeile pro Einstellung, CTA laut ausgesprochen („Probier es kostenlos aus. Klick den Link oder besuche example.com.").
Die meisten Social-Media-Videos werden stumm angesehen, Reels und TikTok inklusive. Gib der Schnitttabelle immer eine Spalte „Auf dem Bildschirm" und lasse den Text auf dem Bildschirm die Geschichte alleine tragen. Wo eine Sprachaufnahme vorhanden ist (Reels, TikTok, Shorts), füge auch eingebrannte Untertitel in den Editorschritten hinzu, platziert in der vertikalen Sicherheitszone und auf Einstellungen gelegt, deren Titel bereits ...