Communitygithub.com

pedrojaques99/site-reels

Nimmt die besten Szenen einer Website oder Web-App (WebGL-Helden, 3D, Scroll) als 1080p-MP4 auf, lädt sie in Figma Slides hoch und erstellt Marken- oder Produkt-Showreels mit Remotion.

Was ist site-reels?

Site Reels ist ein KI-Agenten-Skill, der entwickelt wurde, um die visuell beeindruckendsten Szenen einer Website oder Web-App – wie WebGL-Heldenanimationen, 3D-Elemente und Scroll-Interaktionen – als hochwertige 1080p-MP4-Videos aufzunehmen. Die Videos werden automatisch in Figma Slides hochgeladen, und der Skill kann mit Remotion vollständige Marken- oder Produkt-Showreels erstellen. Er unterstützt Plattformen wie Claude Code und Cursor und basiert auf Puppeteer und puppeteer-screen-recorder für die Aufnahme. Der Skill erkennt Chrome automatisch, bietet Optionen für 60fps-Aufnahmen mit timeweb und enthält detaillierte Workflows für die Prüfung, Fehlerbehebung und Bereitstellung. Er ist ideal für Entwickler und Designer, die schnell ansprechende Videodemos für Pitches, Launch-Videos oder Showreels erstellen möchten.

Funktioniert mit~Claude Code~Codex CLI✓Cursor
npx skills add https://github.com/pedrojaques99/visant-skills-public/tree/HEAD/site-reels

In Ihrer bevorzugten KI fragen

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

Dokumentation

Site Reels: Website → MP4 → Deck

Voraussetzungen

Benötigt fürWasPrüfung
allesWindows, macOS oder Linux mit GPU und sichtbarem Desktop (Chrome läuft headful)n/a
allesNode.js 18+node -v
allesffmpeg + ffprobe im PATH (oder FFMPEG=<vollständiger Pfad>)ffmpeg -version
allesChrome, Chromium oder Edge (automatisch erkannt, siehe unten)node -e "import('./scripts/find-chrome.mjs').then(m=>console.log(m.findChrome()))"
record.mjspuppeteer-core + puppeteer-screen-recorder (npm, im Projekt)npm ls puppeteer-core puppeteer-screen-recorder
snap.mjspuppeteer-core + timesnap (bringt timeweb mit)npm ls timesnap
ShowreelRemotion-Projektnpx remotion --version
Audits (optional)ImageMagickmagick -version
Figma-UploadFigma MCP (use_figma, upload_assets) + Skills figma-use, figma-use-slidesMCP-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.png und anschauen.
  • 1 MB für 19 s = leere Szene. > 100 MB = passt nicht in Figma (Szenen-crf behebt 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-FPS mit langer Pause am Anfang/Ende ist ein Fehlalarm (der Zähler zählt Standbilder); Kontaktbogen prüfen.

Bekannte Fallstricke

  1. 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: false absichtlich: Headless fällt auf SwiftShader zurück und WebGL ruckelt.
  2. 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.
  3. Ein ungültiger Selektor killt die gesamte Regel: \[ in einem JS-Template-Literal wird zu [ und .z-[60] macht header, nav, .z-[60] ungültig. Eine Regel pro riskantem Selektor.
  4. [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).
  5. 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.
  6. ScreenCast liefert ~32 eindeutige Frames/s → bei 30fps aufnehmen (60 ergibt unregelmäßig wiederholte Frames). Standard trim 0.3s verwirft den ersten zwischengespeicherten Frame.
  7. ScreenCast ruckelt auf einem ausgelasteten Rechner (Photoshop geöffnet, paralleles Rendern): Das MP4 sagt "30fps" mit 9–18 eindeutigen Frames/s. record.mjs misst (mpdecimate=hi=64:lo=24:frac=0.05) und nimmt bis zu 2x unter MIN_FPS (24) neu auf. Für WebGL/3D-Aufnahmen snap.mjs bevorzugen.
  8. Cookie-Banner: In prepare akzeptieren (generischer Klick auf "Akzeptieren"/"OK") vor der Aufnahme.
  9. 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 synthetisches input.

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 ohne playing-Event → video.play().catch() wirft einen Fehler und die Seite wird leer.
  • preserveDrawingBuffer: true bei getContext erzwungen (ausschalten mit preserve: 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 MAE zwischen 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:

  1. Cover jedes Videos: ffmpeg -ss <t> -i x.mp4 -frames:v 1 -q:v 2 _capas/x.jpg.
  2. use_figma (Skills figma-use + figma-use-slides): createSlideRow(idx) vor der letzten Folie, row.name = 'WIP · Motion (hidden)', ein createSlide(idx, i) pro Video mit isSkippedSlide = true, schwarzem Hintergrund, einem 1920×1080 Rechteck namens VIDEO · x.mp4 und der Anweisung in speakerNotes.
  3. upload_assets mit den nodeIds der Rechtecke → Multipart-POST der Covers.
  4. 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 variabler wght- Achse, Palette in Bändern, Typografie, geschichtete Grafiken, 3D mit Titel in mix-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 mit magick compare -metric RMSE zwischen 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, Series mit 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 und block: ['assets/logo.png']; das leere PNG muss transparent sein; das Video nicht blockieren: Ein defektes Video friert timeweb ein). Verwenden Sie die Aufnahme als mix-blend-mode: multiply Overlay: 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 durch use_figma zurü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 / rate neu 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: trimBefore auf 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.mjs mit KEEP_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]

Verwandte Skills