Site Reels: Website → MP4 → Deck
Voraussetzungen
| Benötigt für | Was | Prüfung |
|---|---|---|
| alles | Windows, macOS oder Linux mit GPU und sichtbarem Desktop (Chrome läuft headful) | n/a |
| alles | Node.js 18+ | node -v |
| alles | ffmpeg + ffprobe im PATH (oder FFMPEG=<vollständiger Pfad>) | ffmpeg -version |
| alles | Chrome, Chromium oder Edge (automatisch erkannt, siehe unten) | node -e "import('./scripts/find-chrome.mjs').then(m=>console.log(m.findChrome()))" |
record.mjs | puppeteer-core + puppeteer-screen-recorder (npm, im Projekt) | npm ls puppeteer-core puppeteer-screen-recorder |
snap.mjs | puppeteer-core + timesnap (bringt timeweb mit) | npm ls timesnap |
| Showreel | Remotion-Projekt | npx remotion --version |
| Audits (optional) | ImageMagick | magick -version |
| Figma-Upload | Figma MCP (use_figma, upload_assets) + Skills figma-use, figma-use-slides | MCP-Tool-Liste |
Chrome-Erkennung (scripts/find-chrome.mjs, verwendet von record.mjs und snap.mjs): CHROME
Umgebungsvariable, falls gesetzt; sonst installiertes Chrome/Edge (Windows: Program Files und LocalAppData; macOS:
/Applications und ~/Applications; Linux: google-chrome, chromium, microsoft-edge im PATH);
sonst das neueste ms-playwright/chromium-<N> (oder PLAYWRIGHT_BROWSERS_PATH); sonst stoppt es und
sagt Ihnen, wie Sie CHROME setzen oder npx playwright install chromium ausführen. Unter Windows fügt es
--use-angle=d3d11 hinzu; auf anderen Betriebssystemen behält Chrome sein standardmäßiges GPU-Backend.
Funktionsweise
Zeichnet skriptgesteuerte Szenen einer lokal laufenden Website auf und liefert MP4-Dateien, die für Figma bereit sind (1920×1080, 30fps, x264, < 100 MB). Validierte Engine: puppeteer-core + puppeteer-screen-recorder (CDP ScreenCast + ffmpeg). Keine Bildschirmaufnahme.
Ausführung
mkdir <proj>-reels && cd <proj>-reels && npm init -y && npm i puppeteer-core puppeteer-screen-recorder
mkdir -p scripts && cp <skill-verz>/scripts/{record,find-chrome}.mjs scripts/
cp <skill-verz>/scripts/scenes.example.mjs scenes.mjs # Szenen bearbeiten
# Im Site-Repository: npx next build && npx next start -p 3100 (Produktions-Build: flüssigeres WebGL)
OUT="<Ausgabeordner>/Videos" node scripts/record.mjs scenes.mjs [szene ...]
Führen Sie die Skripte kopiert in das Projekt aus: ESM löst puppeteer-core aus dem Ordner des Skripts auf,
nicht aus dem aktuellen Arbeitsverzeichnis.
Umgebungsvariablen: BASE (Standard http://localhost:3100), OUT, CHROME, FFMPEG, W, H, DPR,
MOBILE=1 (Touch-Emulation), KEEP_UI=1 (Navigation beibehalten), MIN_FPS (24). Telefon 4:5:
W=360 H=450 DPR=2 MOBILE=1 ergibt 720x900.
Jede Szene = { name, path, theme?, trim?, crf?, css?, prepare(page, ctx), act(page, ctx) }.
prepare wird vor der Aufnahme ausgeführt (zum Abschnitt scrollen, auf 3D-Laden warten); act ist das, was
in das Video kommt. ctx hat wait, scrollTo(page, y|selector, ms) und
scrollThrough(page, selector|null, ms, from, to) mit Easing. Intro, das beim Laden läuft:
page.reload() innerhalb von act und trim verwenden, um das Laden abzuschneiden.
Marken-Skript, das funktioniert: Logo/Hero-Intro (hell und dunkel), Hero-Medienwechsel, 3D-Abschnitt mit Kamera, die beim Scrollen eintritt, immersive Route (Walk), Footer-Wordmark und ein voller Seiten-Durchlauf. 5–7 Szenen von 12–55s.
Prüfung vor der Auslieferung (dem Exit-Code nicht vertrauen)
- Kontaktbogen pro Video:
ffmpeg -i x.mp4 -vf "fps=6/<dur>,scale=480:-1,tile=3x2" -frames:v 1 sheet.pngund anschauen. - 1 MB für 19 s = leere Szene. > 100 MB = passt nicht in Figma (Szenen-
crfbehebt das; 24 für eine Szene mit Korn + Spuren). - Oberer Streifen (
crop=1920:120:0:0) zu mehreren Zeitpunkten, um eine auslaufende Navigation zu erwischen. LOW-FPSmit langer Pause am Anfang/Ende ist ein Fehlalarm (der Zähler zählt Standbilder); Kontaktbogen prüfen.
Bekannte Fallstricke
- Verstecktes Fenster friert rAF ein → eingefrorene Frames. Der Start übergibt bereits
--disable-backgrounding-occluded-windows --disable-renderer-backgrounding --disable-background-timer-throttling --disable-features=CalculateNativeWinOcclusion.headless: falseabsichtlich: Headless fällt auf SwiftShader zurück und WebGL ruckelt. - Hide-UI CSS verschwindet beim Neuladen und wenn React-Hydration
<head>löscht → das Skript hängt alle 100ms ein<style id="reels-hide">an den Body an. - Ein ungültiger Selektor killt die gesamte Regel:
\[in einem JS-Template-Literal wird zu[und.z-[60]machtheader, nav, .z-[60]ungültig. Eine Regel pro riskantem Selektor. [data-cursor]nicht ausblenden: Websites markieren die 3D-Leinwand und Karussells damit. Zielen Sie auf das benutzerdefinierte Cursor-Element (fixed + mix-blend + pointer-events-none).- Lazy-geladenes 3D (IntersectionObserver + dynamischer Import): 5–7 s warten, nachdem der
Abschnitt erreicht ist. Das Lesen der WebGL-Leinwand kommt leer zurück ohne
preserveDrawingBuffer: Verwenden Sie eine feste Wartezeit oder einen Screenshot. - ScreenCast liefert ~32 eindeutige Frames/s → bei 30fps aufnehmen (60 ergibt unregelmäßig wiederholte
Frames). Standard
trim0.3s verwirft den ersten zwischengespeicherten Frame. - ScreenCast ruckelt auf einem ausgelasteten Rechner (Photoshop geöffnet, paralleles Rendern): Das MP4 sagt
"30fps" mit 9–18 eindeutigen Frames/s.
record.mjsmisst (mpdecimate=hi=64:lo=24:frac=0.05) und nimmt bis zu 2x unterMIN_FPS(24) neu auf. Für WebGL/3D-Aufnahmensnap.mjsbevorzugen. - Cookie-Banner: In
prepareakzeptieren (generischer Klick auf "Akzeptieren"/"OK") vor der Aufnahme. - Eine Werkzeugseite ohne Scrollen (Generator, Effekt-Editor) zeichnet still auf: Die Szene muss
interagieren (Datei in
input[type=file]hochladen, Voreinstellungen per Schaltflächentext anklicken). Ein benutzerdefinierter Schieberegler reagiert nicht auf ein synthetischesinput.
60fps-sichere Aufnahmen: snap.mjs (deterministisch)
scripts/snap.mjs injiziert timeweb (timesnaps virtuelle Uhr: rAF, performance.now,
setTimeout und video), geht 1/60s pro Frame vor und macht Screenshots. Unabhängig von der
Rechnerauslastung. Szene: { name, path, theme, css, dur, wait (echte ms zum Laden von 3D), pre (virtuelle ms vor Scrolling, 2000; **0 für ein Intro, das beim Laden läuft**), start (übersprungene virtuelle s), scroll: { sel, from, to, at, len } | { bottom: [from, to] in Bildschirmhöhen }, frame(page, t) (z.B. Maus) }. Scrollen ist eine Funktion der virtuellen Zeit.
npm i puppeteer-core timesnap && mkdir -p scripts
cp <skill-verz>/scripts/{snap,find-chrome,patch-timeweb}.mjs scripts/
npm pkg set scripts.postinstall="node scripts/patch-timeweb.mjs" && npm run postinstall
FPS=60 OUT=... node scripts/snap.mjs scenes-snap.mjs [szene]
timesnap()selbst nicht verwenden: Bei Next + R3F + Lenis zeichnete es eine leere Seite auf und hinterließ eingefrorene Abschnitte auf dem WebGL-Helden (rAF läuft nicht). Die eigene Schleife (goTo + scrollTo + screenshot) funktionierte überall: 60/60 eindeutige Frames.- timeweb-Patch ist obligatorisch: Er ersetzt
video.play()durch eine Version ohne Promise und ohneplaying-Event →video.play().catch()wirft einen Fehler und die Seite wird leer. preserveDrawingBuffer: truebei getContext erzwungen (ausschalten mitpreserve: false).- Frames gehen in das temporäre Verzeichnis des Betriebssystems, niemals auf ein synchronisiertes Laufwerk (Tausende von JPGs synchronisieren).
- Keine GPU-Arbeit parallel (Remotion-Render, Photoshop): WebGL verliert seinen Kontext und zeichnet
nur den Hintergrund für Sekunden auf. Das Skript warnt
FLASH(Frame-Aufblitzen durch Luminanz); ein langer leerer Abschnitt wird nur auf dem Kontaktbogen sichtbar. Anschauen. - Realer Glätte-Check:
magick compare -metric MAEzwischen benachbarten Frames in voller Auflösung. 0 = wiederholter Frame. - Auslieferung:
scripts/deliver.example.sh(kopieren und die Aufnahmen umbenennen;V=erforderlich) erstellt Enthüllungen, Loops (loop.sh) und sauberes 3D, crf um 3 erhöhen, bis < 95 MB.
Hochladen zu Figma Slides (versteckt, ohne das Team zu stören)
Das MP4 geht nicht über MCP hinein: use_figma hat kein Netzwerk und die Plugin-API erstellt
kein Video aus Bytes; upload_assets akzeptiert nur Bilder (≤ 10 MB). Also:
- Cover jedes Videos:
ffmpeg -ss <t> -i x.mp4 -frames:v 1 -q:v 2 _capas/x.jpg. use_figma(Skillsfigma-use+figma-use-slides):createSlideRow(idx)vor der letzten Folie,row.name = 'WIP · Motion (hidden)', eincreateSlide(idx, i)pro Video mitisSkippedSlide = true, schwarzem Hintergrund, einem 1920×1080 Rechteck namensVIDEO · x.mp4und der Anweisung inspeakerNotes.upload_assetsmit dennodeIdsder Rechtecke → Multipart-POST der Covers.- Der Benutzer zieht das MP4 auf das Rechteck (ersetzt das Cover durch das Video). Sagen Sie dies klar; versprechen Sie niemals ein eingefügtes Video.
getSlideGrid() lässt übersprungene Folien aus: Überprüfen Sie die Struktur über SLIDE_GRID.children[i].children.
Marken-Showreel (Remotion): Aufnahmen + gestaltete Szenen
Für ein Marken-Showreel (Logo, Farben, Schrift, Layouts, Manifest, Mockups) nur saubere
Aufnahmen von der Website aufnehmen (kein Text: pro Szene CSS blendet Titel/Navigation aus) und den Rest in
Remotion komponieren (Frame-für-Frame-Render, kein Ruckeln). Wiederverwendbare Bausteine, die es wert sind, in
src/fx.tsx zu sein: Mist, MistWords, Grain, KenBurns, Plate, ein nevoa() (Nebel)-Übergang,
mit Markendaten in brand.ts.
- Die visuelle Identität kommt aus dem Deck, nicht von der Website: Überprüfen Sie die offizielle Schrift und Palette in den Folien (Websites weichen vom Markenbuch ab).
- Szenen, die funktionieren: Eröffnungssatz im Nebel, aufgezeichnete Enthüllung, Logo-Konstruktion (
evolvePath+ Hilfslinien, gemessen auf dem Logo-Raster), Manifest Satz für Satz, Säulen mit variablerwght- Achse, Palette in Bändern, Typografie, geschichtete Grafiken, 3D mit Titel inmix-blend-difference, scrollende Layout-Spalten, 1/2/3-fach Mockup-Montage, Produkt synchron zum Namen, Abschluss mit dem Wordmark. - Überprüfung anhand von Standbildern (
npx remotion still ... --frame=N+ Montage) vor dem Rendern. - Rendern mit
--concurrency=3: Bei 8, mit geöffnetem Photoshop, lief einem 32-GB-Rechner der Arbeitsspeicher aus. - Loops:
scripts/loop.sh in out [start] [dur] [crossfade]. Naht prüfen mitmagick compare -metric RMSEzwischen erstem und letztem Frame (< 0,5 % = unsichtbar). - Schlechte Aufnahme ist ein Ausschuss: Ein Rotationskörper, der sich dreht, zeigt keine Bewegung; eine Kamera, die für eine Portrait-Leinwand eingerahmt ist, wirkt in 16:9 klein.
- Hektischer Reel (genehmigter Rhythmus): 25–35s, 60fps,
Seriesmit harten Schnitten auf einem 120 BPM-Raster (30 Frames), Bild-Bursts alle 3–6 Frames, riesiger Logo-Ausschnitt über einem Raster, blinkende Schriftgewichte, Palettenblöcke, die sich neu anordnen, Portrait in einer kreisförmigen Maske, grafischer Burst, kinetischer Satz, ein-Beat- Mockups, Abschlusslogo ~2,5s gehalten. Null Labels: Text nur als Grafiken. Ein langsames 1:40 Showreel wurde abgelehnt. - Weiches/rauchiges Showreel: Den Hero-Rauch ohne Logo oder Foto aufnehmen, schwarz auf reinem Weiß
(Szene mit
storage= Parametern in localStorage undblock: ['assets/logo.png']; das leere PNG muss transparent sein; das Video nicht blockieren: Ein defektes Video friert timeweb ein). Verwenden Sie die Aufnahme alsmix-blend-mode: multiplyOverlay: beginnt am Anfang der Szene, eine invertierte Version (ffmpeg -vf reverse) schließt sie ab; der Schnitt endet auf Schwarz. Übergang bei ~1,1s (Rate 2), oder die Hälfte der Szene wird schwarz. - Figma Slides Layouts zusammenstellen: Die Folie klonen (übersprungen), jede Karte 3x duplizieren mit nur
Hintergrund / Logo / Text sichtbar, jeden Knoten mit
download_assets(Skalierung 2) herunterladen und die Klone löschen. Slides exportiert keine Transparenz: Logo/Text mit schwarzer und dann weißer Folie exportieren und Alpha durch Differenz extrahieren (ImageMagick). Niemals base64 durchuse_figmazurückgeben (sprengt den Kontext).
Produkt-Showreel (reale App gefilmt)
Bildschirme werden real gefilmt (Playwright + CDP ScreenCast bei 2x, Schritte
goto/move/click/type/scroll/wait, Mauszeiger-Spur gespeichert als <id>.cursor.json) und der
Cursor wird in Remotion oben neu gezeichnet.
Abgelehnt, nicht wiederholen:
- Ein gestaltetes Showreel auf den echten Bildschirm kleben (illustrierter Anfang + gefilmte Mitte + gezeichneter CTA): zwei Sprachen, die Figur verschwindet und kehrt zurück, die schwarze Karte zwischen den Akten stoppt den Rhythmus. Eine Sprache pro Video.
- Kleiner, beschleunigter Produktbildschirm in einem 16:9-Fenster: Feed und Ranking werden zu einer unleserlichen Wand.
- Zoom, der den Bildschirm beschneidet und Text, der über das Video gestempelt ist (riesiges Verb, 01/07-Zähler, Verlauf): aufdringlich.
- Leeres Warten auf dem Bildschirm ("warten…"): direkt zur Aktion schneiden.
- Vage Texte: Es muss offensichtlich und in der einfachen gesprochenen Sprache des Publikums sein.
Die Form, die funktioniert: Pro Aktion zuerst nur Text, zentriert und kurz (2 Takte), dann der gesamte Bildschirm, unbeschnitten, in einem zentrierten Fenster im Aufnahme-Seitenverhältnis, auf einem sauberen Hintergrund. Diskreter Text (Gewicht 700, pro Wort Eingang mit leichter Unschärfe), kein Zähler, kein Verlauf. Schnitte auf dem Track-Raster (14 Frames = 128,57 BPM). Maximal 40s.
Technik, die sich ausgezahlt hat:
- Eine Aufnahme beschleunigen:
<OffthreadVideo trimBefore={from} playbackRate={rate}>und Klick-Frames/ rateneu berechnen (Ton und Cursor-Ring). - Eine andere Komposition mittendrin starten:
<Sequence from={X}><Sequence from={-start}><Comp/></Sequence></Sequence>. Den ersten Frame der Strecke prüfen: Ein gemeinsames Element, das "hereinfliegt", erscheint lose. Ein paar Frames überspringen und das Impact-Geräusch neu platzieren, das davor gefallen ist. - Sprache pro Komposition mit einem modularen Timeline: ein
globalThis-Flag, das von seiner eigenen Eintragsdatei gesetzt wird, die vor dem Root importiert wird. - Kontinuierlicher Track über Schnitte hinweg:
trimBeforeauf dem Bett, sodass es dort weiterläuft, wo die vorherige Strecke aufgehört hat. - Lautstärke: Rendern, messen und normalisieren mit 2-Pass
loudnorm(-14 LUFS / -1 dBTP), wobei das Video kopiert wird. Nur am Ende messen: Remotions roher Mix geht über 0 dBFS. - Audit: Zeitgestempelter Kontaktbogen (
fps=2,drawtext=%{pts\:hms},tile=8x6) jedes Renderings und ein dichter Bogen (fps=6) an jedem Streckenübergang. - Ein Projekt ohne Git, gleichzeitig von einer anderen Sitzung bearbeitet: Neuer Schnitt in einer neuen Datei,
*.bak-*-Sicherung vor dem Bearbeiten einer gemeinsam genutzten Datei, und den Benutzer informieren.
Portfolio-Karten und Studio-Reel
record.mjsmitKEEP_UI=1(die Karte zeigt die Website, wie sie ist) und dem Telefon-Preset oben.- Szenen generiert aus einer Inventardatei (nur öffentliche Elemente mit einer Live-URL); keine Live-URL = Ken Burns des Screenshots.
- Karten-Paket: Crossfade-Loop von Ende zu Anfang, 2-Pass-Bitrate bis zu einem KB-Limit, VP9 WebM daneben, WebP-Poster von Frame 1.
- Logo mit Alpha für das Web: `-
…
[gekürzt für die Übersetzung]