vid-produkteinführung
Erzeugt ein narratives MP4-Produkteinführungsvideo aus einer Produktbeschreibung und einem Einführungskontext. Pipeline: HTML/CSS-Animationen → Headless Chromium (Playwright, frame-by-frame) → FFmpeg (H.264 MP4). Kein React. Keine KI-Video-APIs. Kein Python. Null laufende Kosten außer Playwright + FFmpeg.
Kritische Regeln (vor jeder Generierung lesen)
-
Der Slogan ist nicht optional. Überspringe ihn nicht. Er ist das gesamte Versprechen des Produkts in 4–6 Wörtern. Wenn der Benutzer keinen angegeben hat, leite einen aus der Beschreibung ab – eine scharfe Distillation in aktiver Stimme. Schreibe niemals „[Slogan hier einfügen]“ oder lasse ihn leer.
-
Der Enthüllungsmoment muss sich verdient anfühlen. Die Abschnitte „Tease“ und „Build“ dienen dazu, Spannung aufzubauen. Wenn der Produktname in den ersten 20 % des Videos erscheint, bricht die Erzählung zusammen. Enthülle den Produktnamen niemals vor dem Abschnitt „Reveal“.
-
product_name Schriftgröße: mindestens 120px für 16:9, mindestens 80px für 9:16. Der Name muss das größte Element im Enthüllungsmoment sein. Wenn er nicht dominant ist, schlägt die Enthüllung fehl.
-
Ein Proof-Stat. Keine Liste. Wenn der Benutzer mehrere Statistiken angibt, wähle die stärkste aus. Eine Liste von 5 Zahlen zerstört die Wirkung. Eine übergroße Zahl erzeugt sie.
-
Verwende
window.renderFrame(t)– kein CSS@keyframesfür Szenenübergänge. Das Suchen von CSScurrentTimewird für Rückwärtssuchen in Chromium stillschweigend ignoriert. Die renderFrame-Funktion berechnet Element-Stile direkt aus Millisekunden. Playwright ruft sie einmal pro Frame auf. -
Kein
animation-delayfür IRGENDEIN Element. Nicht nötig bei renderFrame. Wenn duanimation-delayschreibst, hör auf – du verwendest die falsche Architektur. -
window.__videoReady = truenur innerhalb vondocument.fonts.ready.then(...). Niemals synchron setzen. -
window.__stopPreview()bereitstellen. Die rAF-Vorschauschleife konkurriert mit den evaluate/screenshot-Aufrufen von Playwright. Das Erfassungsskript ruft__stopPreview()vor der Frameschleife auf. -
Verwende
t < startMs(nichtt <= startMs) in Szenengrenzenprüfungen.t <= 0bei Frame 0 lässt Szene 1 schwarz erscheinen. -
Body = exakte Pixelmaße. Breite und Höhe sind ganze Zahlen. Keine
%,vw/vh,rem. -
Lies
references/scene-library.mdUNDreferences/style-presets.md, bevor du IRGENDWELCHEN HTML generierst. -
HTML niemals in den Chat werfen. In Datei speichern. Nur Zusammenfassung zeigen.
-
Die Filmkorn-Leinwand MUSS 240×135 sein, nicht B×H. Setze
width="240" height="135"auf das Canvas-Element, dann strecke mitstyle="width:[B]px;height:[H]px". Vollauflösendes Korn bei 1920×1080 ist 64× langsamer – 8 MB ImageData pro Frame – und lässt 1800-Frame-Exporte Stunden dauern. -
Der Tease-Abschnitt MUSS dunkel (#000 oder beinahe-schwarz) sein, unabhängig vom Ton. Weiße Hintergründe im Tease-Abschnitt wirken wie Demo-Folien, nicht wie Produkteinführungsvideos. Der dunkel-zu-hell-Erzählbogen (dunkler Tease → dunkler Build → Produktenthüllung) erzeugt die Dramatik in Einführungsvideos. Selbst die Voreinstellungen
minimalundenergeticsollten für Tease-Problem- und Spannungsaufbau-Szenenbackground: #000verwenden. -
Jedes Tease-Wort muss sein eigener Beat mit 200px+ Schriftgröße sein. Wort-für-Wort in einer einzigen Zeile ist nicht kraftvoll genug. Jedes Problemwort (z. B. „Research.“ / „Write.“ / „Outreach.“ / „Repeat.“) erhält 1500–1800 ms Bildschirmzeit bei
font-size: 200px; font-weight: 900zentriert, eines nach dem anderen. Siehe scene-library.mdtease-words-Muster. -
Der Build-Abschnitt muss Inhalte zeigen, die das Problem beschreiben – nicht nur Partikel. Verwende das
terminal-card-Muster: 3 Karten erscheinen nacheinander mit einer Schreibmaschinenanimation, die die manuelle Arbeit zeigt, die erledigt wird. Karten habenborder: 1px solid rgba(255,255,255,0.07), blauen Punkt-Akzent, Monospace-Text. Nur Partikel für 20 Sekunden ist leere Bildschirmzeit. -
Punktgitter-CSS-Hintergrund auf dunklen Szenen. Füge
background-image: radial-gradient(circle, rgba(255,255,255,0.035) 1px, transparent 1px); background-size: 60px 60px;zu Build-, Reveal-, Proof- und CTA-Szenen hinzu. Es kostet keine Rechenleistung und verleiht Tiefe.
Schritt 1: Datenerfassung
Erforderlich:
product_name– der Name des Produkts oder der Funktion, das/die eingeführt wirdproduct_description– 2–3 Sätze: was es tut, für wen es gedacht ist, Hauptvorteil
Optionale Parameter und Standardwerte:
| Parameter | Standard | Beschreibung |
|---|---|---|
| tagline | auto | 4–6 Wörter – das Kernversprechen des Produkts |
| problem_statement | automatisch abgeleitet | 1 Satz für den Tease-Abschnitt (das Problem, das das Produkt löst) |
| proof_stat | automatisch abgeleitet | Einzelne Metrik (z. B. "500+ Teams", "10× schneller", "2 Mio. $ gespart") |
| cta | auto | URL oder Aktionsphrase (z. B. "opendirectory.dev", "Warteliste beitreten") |
| launch_date | — | ISO-Datumszeichenfolge (aktiviert Countdown-Karten-Szene) |
| tone | cinematic | cinematic / energetic / minimal / emotional |
| duration | 60 | 30 / 60 / 90 (Sekunden) |
| aspect_ratio | 16:9 | 16:9 (1920×1080) / 9:16 (1080×1920) |
| letterbox | false | Kinematografische 2,35:1-Schwarzbalken – nur 16:9 |
| music | — | Pfad zur Audiodatei (mp3/m4a/wav) |
| fps | 30 | Bilder pro Sekunde (24, 30 oder 60) |
Wenn product_name oder product_description fehlt, frage genau:
„Um das Einführungsvideo zu erstellen, benötige ich zwei Dinge:
- Produktname – wie heißt das Produkt?
- Produktbeschreibung – 2–3 Sätze: Was tut es, für wen ist es gedacht, was ist der Hauptvorteil?
Optional: Slogan (4–6 Wörter), Proof-Stat (eine Zahl), CTA (URL oder Aktionsphrase), Ton (cinematic / energetic / minimal / emotional), Dauer (30 / 60 / 90 s)."
Wenn beides vorhanden ist → fahre sofort mit Schritt 2 fort.
Schritt 2: Interne Architektur (dem Benutzer nie gezeigt)
1. Fehlende Parameter ableiten:
- tagline (falls nicht angegeben): Eine aus product_description schreiben. 4–6 Wörter. Aktive Stimme. Kein Füllmaterial („Die Zukunft von…“, „Wir stellen vor…“). Leite das schärfste mögliche Versprechen ab.
- problem_statement (falls nicht angegeben): Aus product_description ableiten. 1 Satz, der das Problem beschreibt. Niemals den Produktnamen erwähnen. So geschrieben, dass das Publikum zustimmend nickt.
- proof_stat (falls nicht angegeben): Aus product_description ableiten. Wenn keine Statistik verfügbar ist, einen glaubwürdigen Skalenindikator verwenden („Von über 500 Teams vertrauenswürdig“, „Von Tagen zu Minuten“).
- cta (falls nicht angegeben): Produktdomäne verwenden, wenn aus product_name ableitbar, oder „Mehr erfahren“ als Aktionsphrase.
2. Abschnittszeitsteuerung aus duration berechnen:
| Abschnitt | 30s | 60s | 90s | Startformel |
|---|---|---|---|---|
| Tease | 0–5s | 0–10s | 0–15s | 0ms |
| Build | 5–12s | 10–30s | 15–40s | Tease-Ende |
| Reveal | 12–20s | 30–45s | 40–60s | Build-Ende |
| Proof | 20–25s | 45–55s | 60–75s | Reveal-Ende |
| CTA | 25–30s | 55–60s | 75–90s | Proof-Ende |
Jede Grenze in Millisekunden umrechnen. Konstanten zuweisen:
TEASE_START_MS, TEASE_END_MS
BUILD_START_MS, BUILD_END_MS
REVEAL_START_MS, REVEAL_END_MS
PROOF_START_MS, PROOF_END_MS
CTA_START_MS, CTA_END_MS
3. Szenen pro Abschnitt auswählen:
- Tease: Beginnt immer mit
blackout-opener(1500ms). Danntease-words(jedes Problemschlüsselwort als eigener 1600–1800ms-Beat mit 200px+). Fallslaunch_dateangegeben, optionalcountdown-cardam Ende des Tease hinzufügen. - Build:
terminal-card-Sequenz (3 Karten, die manuelle Aufgaben zeigen, die eingetippt werden) + optional abschließende Akzentzeile. Dauer ≥ 30s erhält alle 3 Karten. Dauer = 30s verwendet 2 Karten. Verwende niemals nur Partikel für mehr als 5s – der Bildschirm muss Inhalte zeigen, die das Problem beschreiben. - Reveal:
reveal-hero– immer zuerst in diesem Abschnitt. Optional gefolgt vontagline-card, wenn Dauer ≥ 60s. - Proof:
proof-stat, wenn eine numerische Statistik verfügbar ist. Ansonstenfeature-bullet. - CTA:
cta-card– immer, immer zuletzt.
Typische Szenenanzahlen: 30s = 4 Szenen, 60s = 6–7 Szenen, 90s = 7–8 Szenen.
4. Zeitsteuerung pro Szene berechnen:
Innerhalb jedes Abschnitts die Zeit gleichmäßig auf die Szenen in diesem Abschnitt aufteilen.
Ausnahme: blackout-opener erhält immer genau die ersten 1500ms des Tease.
5. Pixelmaße bestimmen:
16:9→ B=1920, H=10809:16→ B=1080, H=1920
6. Letterbox-Berechnung (falls aktiviert, nur 16:9):
- 2,35:1-Inhaltshöhe = B / 2,35 = 1920 / 2,35 ≈ 817px
- Balkenhöhe = (H - 817) / 2 = (1080 - 817) / 2 ≈ 132px
- Oberer Balken:
top: 0; height: 132px - Unterer Balken:
bottom: 0; height: 132px
7. Kinematografische Effekt-Flags (nach Voreinstellung):
cinematic: Filmkorn=EIN (Canvas 240×135, Opazität 0,025), Vignette=EIN, Lichtlecks=EIN (warmes Gold), Punktgitter=EIN auf dunklen Szenenenergetic: Filmkorn=AUS, Vignette=AUS, Punktgitter=EIN, weißer Blitz bei Enthüllungminimal: Filmkorn=EIN (Canvas 240×135, Opazität 0,022), Vignette=EIN (dezent, 0,5 radial), Punktgitter=EIN, kein Lichtleck – Akzentfarbe = #4B9FFF (elektrisches Blau)emotional: Filmkorn=EIN (Canvas 240×135, Opazität 0,018, warmer Farbstich-Überblendung), Vignette=EIN (weich), Lichtlecks=EIN (Kupfer warm), Punktgitter=AUS
Alle Voreinstellungen: Tease- und Build-Szenen verwenden IMMER dunklen Hintergrund (#000 oder beinahe-schwarz), unabhängig von der Voreinstellung.
8. window.__sfxTimeline in das HTML einbetten (immer – auch wenn keine Musik bereitgestellt wird):
Das Exportskript liest dieses Array, synthetisiert jeden SFX-Typ nativ mit FFmpeg aevalsrc/anoisesrc und platziert Ereignisse mit adelay an exakten Millisekunden-Offsets. Keine externen Audiodateien erforderlich.
SFX-Typ-Referenz:
| Typ | Klang | Dauer | Hinweise |
|---|---|---|---|
word-hit | Sub-Punch (50Hz) + transienter Klick (2,2kHz) + Rauschburst | 180ms | Einer pro Tease-Wort – 3 Schichten via amix |
type-sequence | Mechanische Tastaturklicks bei 15Hz (sin^30-Puls-Hüllkurve) | 1,9s | Eine pro Karte – läuft für die gesamte TYPE_DUR |
whoosh | Zwei-Band-Rausch-Wisch (1,1kHz Körper + 4–8kHz Luft) | 700ms | Am BUILD_CLOSE-Übergang |
tension-riser | Tiefer Grollen (250Hz) + Sub-Ton, der mit t/2,8-Rampe wächst | 2,9s | Start bei BUILD_CLOSE + 700ms – erreicht Höhepunkt bei REVEAL_START |
reveal-boom | Sub (45Hz) + Körper (90Hz) + Schimmern (4,5–11kHz) + 85ms Echo | 900ms | Genau bei REVEAL_START – DER Einschlag |
counter-tick | Harmonischer Klick (880Hz + 440Hz + 1760Hz + 2640Hz) | 80ms | 3 Schläge – Decrescendo 0,32 → 0,26 → 0,20 |
cta-chime | A-Dur-Akkord (440 + 554 + 659 + 880Hz) + aecho-Glockenschimmer | 1,2s | Genau bei CTA_START |
SFX-Zeitplan-Karte – ms-Werte aus Abschnittskonstanten berechnen, niemals fest codieren:
window.__sfxTimeline = [
// ── TEASE: ein word-hit pro Wort-Beat, letzter lauter ───────────────────────
{ ms: WORD_BEATS[0].start, sfx: 'word-hit', vol: 0.55 },
{ ms: WORD_BEATS[1].start, sfx: 'word-hit', vol: 0.55 },
{ ms: WORD_BEATS[2].start, sfx: 'word-hit', vol: 0.55 },
{ ms: WORD_BEATS[N].start, sfx: 'word-hit', vol: 0.65 }, // letztes Wort lauter
// ── BUILD: type-sequence pro Karte (1,9s, entspricht TYPE_DUR 1800ms) ────────
{ ms: CARDS[0].start + 180, sfx: 'type-sequence', vol: 0.28 },
{ ms: CARDS[1].start + 180, sfx: 'type-sequence', vol: 0.28 },
{ ms: CARDS[2].start + 180, sfx: 'type-sequence', vol: 0.28 },
// ── ÜBERGANG: whoosh dann 2,9s Riser, der bei REVEAL_START seinen Höhepunkt erreicht ───────────
{ ms: BUILD_CLOSE, sfx: 'whoosh', vol: 0.50 },
{ ms: BUILD_CLOSE + 700, sfx: 'tension-riser', vol: 0.35 },
// ── REVEAL: der kinematografische Einschlag ─────────────────────────────────────────────
{ ms: REVEAL_START, sfx: 'reveal-boom', vol: 0.88 },
// ── PROOF: harmonische Ticks, Decrescendo ───────────────────────────────────
{ ms: PROOF_START, sfx: 'counter-tick', vol: 0.32 },
{ ms: PROOF_START + 2200, sfx: 'counter-tick', vol: 0.26 },
{ ms: PROOF_START + 4400, sfx: 'counter-tick', vol: 0.20 },
// ── CTA: A-Dur-Akkord landet ────────────────────────────────────────────
{ ms: CTA_START, sfx: 'cta-chime', vol: 0.62 },
];
Platziere diesen Block unmittelbar vor der Vorschauschleife, nach window.renderFrame.
Schritt 3: HTML-Generierung
Lies references/scene-library.md UND references/style-presets.md, bevor du Code schreibst.
Verwende die genauen CSS-Klassennamen, HTML-Strukturen und renderFrame-Muster aus diesen Dateien.
Erforderliches HTML-Grundgerüst:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
[font CDN link from style preset]
<style>
:root {
[all CSS tokens from style preset]
}
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
width: [W]px; height: [H]px;
overflow: hidden;
background: var(--bg);
font-family: var(--font-body);
position: relative;
}
.scene {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80px;
opacity: 0;
will-change: opacity, transform;
}
.scene-inner {
width: 100%;
max-width: 1200px;
}
[paste CSS from each scene type in scene-library.md]
</style>
</head>
<body>
<!-- Kinematografische Overlay-Elemente – werden über allen Szenen dargestellt -->
[if film-grain: ON]
<canvas id="grain-overlay"
width="[W]" height="[H]"
style="position:fixed;inset:0;pointer-events:none;opacity:0.025;mix-blend-mode:overlay;z-index:50"></canvas>
[end if]
[if vignette: ON]
<div id="vignette-overlay"
style="position:fixed;inset:0;background:radial-gradient(ellipse at center,transparent 35%,rgba(0,0,0,0.65) 100%);pointer-events:none;z-index:51"></div>
[end if]
[if light-leak: ON]
<div id="light-leak"
style="position:fixed;inset:0;background:linear-gradient(135deg,rgba(255,220,140,0.5) 0%,rgba(255,255,255,0.3) 45%,transparent 70%);opacity:0;pointer-events:none;z-index:52"></div>
[end if]
[if letterbox: ON]
<div id="lbox-top" style="position:fixed;top:0;left:0;width:[W]px;height:132px;background:#000;z-index:100"></div>
<div id="lbox-bot" style="position:fixed;bottom:0;left:0;width:[W]px;height:132px;background:#000;z-index:100"></div>
[end if]
<!-- Szenen -->
[scene HTML from scene-library.md templates, one per selected scene]
<script>
window.__videoReady = false;
window.TOTAL_DURATION_MS = [duration * 1000];
// ── Abschnittszeitkonstanten ──────────────────────────────────────────────────
const TEASE_START_MS = [N];
const TEASE_END_MS = [N];
const BUILD_START_MS = [N];
const BUILD_END_MS = [N];
const REVEAL_START_MS = [N];
...