Produkt-Demo-Video
Wandeln Sie statische Produkt-Screenshots oder -Designs in eine polierte, animierte Demo um – kein Bildschirmrekorder, kein zittriger echter Cursor, keine UI-Versionsunterschiede. Rahmen Sie jeden Bildschirm ein, gleiten Sie einen synthetischen Cursor auf den exakten Pixel, lassen Sie den Klick wellenförmig auslaufen, zoomen Sie dann auf den relevanten Teil und beschriften Sie das Feature. Da die Eingabe statisch und die Bewegung Code ist, ist jeder Wert deterministisch und wird beim Ändern des Screenshots identisch neu gerendert.
Wann verwenden
- App-/SaaS-Demo, Feature-Announcement, Onboarding-Walkthrough (15–90s).
- Anzeigen eines Klickpfads durch eine UI, von der Sie nur Screenshots/Designs haben.
- Hervorheben einer Fokusregion eines dichten Dashboards (Zoom-auf-Region + Callout).
Warum Screenshots besser sind als Bildschirmaufnahmen
| Bildschirmaufnahme | Screenshot-basiert (dieses Skill) |
|---|---|
| Echter Cursor driftet, überschießt, zittert | Cursor folgt einem abgerundeten Pfad zu einem exakten Ziel |
| Muss den gesamten Take neu aufnehmen, um einen Schritt zu korrigieren | Ein PNG austauschen, denselben Code neu rendern |
| UI ändert sich während des Takes, Maus sucht | Jeder Bildschirm ist ein sauberes, finales Asset |
| Ohne Pixelbildung schwer zu zoomen | Screenshots in 2× (DPR 2) exportieren → scharfe Zooms |
Erfassen Sie Eingaben in doppelter Anzeigegröße (Retina / deviceScaleFactor: 2), damit ein 2×-Zoom scharf bleibt. Siehe references/screenshot-demo.md für ein Playwright-Erfassungsskript.
Der Demo-Loop
Jede Demo besteht aus demselben Rhythmus, wiederholt pro Feature. Halten Sie lange genug, um zu lesen, bewegen Sie sich mit Absicht, setzen Sie den Klick, enthüllen Sie das Ergebnis.
| Rhythmus | Budget | Aufgabe |
|---|---|---|
| Auf Bildschirm einstellen | 0.5–1s | Dem Betrachter Zeit geben, sich zu orientieren, bevor etwas passiert |
| Cursor reist zum Ziel | 0.4–0.8s | Abgerundete Bewegung (ease-in-out), nicht linear |
| Klickwellen + Zustandsänderung | 0.2–0.3s | Taktiles Feedback; Bildschirm reagiert im selben Frame |
| Zoom zur Fokusregion | 1–2s hinein, 2–4s halten | 2× ist der Sweet Spot – genug Detail, Kontext bleibt |
| Feature beschriften | mit dem Zoom halten | Ein Nutzen-Satz, z.B. „Export mit einem Klick“ |
Bewegen Sie die Kamera oder den Cursor, selten beides gleichzeitig – zwei gleichzeitige Bewegungen teilen die Aufmerksamkeit.
Screenshot einrahmen
Ein roher Screenshot sieht aus wie ein Bug-Report; ein eingerahmter wie ein Produkt. Jeden Bildschirm in einen Browser- oder Gerätechrome auf einem markierten Hintergrund einbetten.
// Browser-Frame – Screenshot hineinlegen, glaubwürdiges Fenster erhalten
const BrowserFrame = ({ url, children }) => (
<div style={{ borderRadius: 12, overflow: "hidden", background: "#1e1e22",
boxShadow: "0 40px 80px rgba(0,0,0,.35)" }}>
<div style={{ height: 40, display: "flex", alignItems: "center", gap: 8, padding: "0 14px" }}>
{["#ff5f57", "#febc2e", "#28c840"].map((c) => (
<span key={c} style={{ width: 12, height: 12, borderRadius: "50%", background: c }} />
))}
<div style={{ marginLeft: 12, flex: 1, height: 24, borderRadius: 6, background: "#2b2b30",
color: "#9aa", fontSize: 13, display: "flex", alignItems: "center", padding: "0 10px" }}>
{url}
</div>
</div>
<div style={{ background: "#fff" }}>{children}</div>
</div>
);
Eine echte URL in die Adressleiste setzen (Authentizität), einen weichen Schatten und einen Farbverlaufshintergrund hinzufügen. Für Mobilgeräte zu einem Telefonrahmen mit Notch und abgerundeten Ecken wechseln. Den Rahmenstil in einem Theme-Objekt halten, damit jeder Bildschirm übereinstimmt.
Animierter Cursor + Klickwelle (die Kernbewegung)
Die Position vom aktuellen Frame aus steuern, niemals von einer CSS-Transition oder Wanduhr – ein Video-Renderer benötigt jeden Frame als reine Funktion der Frame-Nummer, sonst desynchronisiert der Cursor und flackert.
import { useCurrentFrame, interpolate, spring, useVideoConfig } from "remotion";
// Abgerundete Reise von `from` nach `to` zwischen startFrame und startFrame+dur
const useCursor = (from, to, startFrame, dur) => {
const frame = useCurrentFrame();
const t = interpolate(frame, [startFrame, startFrame + dur], [0, 1],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp", easing: (x) => x * x * (3 - 2 * x) }); // smoothstep
return { x: from.x + (to.x - from.x) * t, y: from.y + (to.y - from.y) * t };
};
export const Cursor = ({ from, to, startFrame, dur, clickAt }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const { x, y } = useCursor(from, to, startFrame, dur);
// Ripple breitet sich einmal aus, zum Klick-Frame
const since = frame - clickAt;
const showRipple = since >= 0 && since < 18;
const r = spring({ frame: since, fps, config: { damping: 18 }, durationInFrames: 18 });
return (
<>
{showRipple && (
<div style={{ position: "absolute", left: x, top: y, transform: "translate(-50%,-50%)",
width: 12 + r * 60, height: 12 + r * 60, borderRadius: "50%",
border: "2px solid rgba(80,140,255,.9)", opacity: 1 - r }} />
)}
<svg width="28" height="28" viewBox="0 0 28 28"
style={{ position: "absolute", left: x, top: y, filter: "drop-shadow(0 2px 3px rgba(0,0,0,.4))" }}>
<path d="M3 2 L3 21 L9 15 L13 24 L16 23 L12 14 L20 14 Z" fill="#fff" stroke="#222" strokeWidth="1.4" />
</svg>
</>
);
};
Der Cursor verwendet smoothstep, damit er aus dem Ruhezustand beschleunigt und in das Ziel hinein abbremst (ein linearer Cursor wirkt roboterhaft). Lösen Sie die Welle und die Zustandsänderung des Bildschirms im selben clickAt-Frame aus, sodass Ursache und Wirkung übereinstimmen. Siehe references/screenshot-demo.md für die vollständige Komposition, die mehrere Klicks über mehrere Bildschirme hinweg verdrahtet.
In eine Region zoomen (Ken Burns für UI)
Um in ein fokussiertes Element zu zoomen, skalieren Sie den Bildschirm und setzen Sie transform-origin auf das Zentrum dieses Elements, sodass der Zoom darauf konvergiert, anstatt auf den Bildschirmmittelpunkt.
const ZoomToRegion = ({ target, scale = 2, startFrame, dur, children }) => {
const frame = useCurrentFrame();
const s = interpolate(frame, [startFrame, startFrame + dur], [1, scale],
{ extrapolateLeft: "clamp", extrapolateRight: "clamp", easing: (x) => 1 - Math.pow(1 - x, 3) }); // easeOutCubic
return (
<div style={{ transform: `scale(${s})`, transformOrigin: `${target.x}px ${target.y}px`,
willChange: "transform" }}>
{children}
</div>
);
};
Zoom auf ca. 2× begrenzen (über 3× verliert der Kontext und desorientiert). Den gezoomten Zustand 2–4s halten – lang genug, um das Detail zu lesen – dann sanft wieder herauszoomen, bevor zum nächsten Bildschirm gewechselt wird. Den Zoom mit einem Callout kombinieren, niemals roh.
Callout-Anmerkungen & Feature-Beschriftungen
Den Wendepunkt annotieren, dann ausblenden lassen. Ein Callout ist ein Label + eine Verbindungslinie (Pfeil, Kreis oder Unterstreichung), die auf das fokussierte Element zeigt.
- Heben Sie das Element durch einen weichen Ring oder abgedunkelte Umgebung hervor (
box-shadow: 0 0 0 9999px rgba(0,0,0,.45)erzeugt ein Spotlight-Loch). - Beschriften Sie einen Nutzen pro Schritt – kurze Nominalphrase („Echtzeit-Analysen“), kein ganzer Satz. Erscheint beim Zoom, verschwindet vor dem nächsten Schritt.
- Halten Sie Beschriftungen im unteren Drittel und innerhalb des titelsicheren Bereichs, damit sie Beschnitte auf 9:16 überleben.
Siehe references/annotation-and-transitions.md für Spotlight-Masken, Pfeilverbinder und eine getippte Feature-Beschriftungsspur.
Bildschirm-zu-Bildschirm-Übergänge
Zwischen den Schritten Bildschirme mit einer einheitlichen Sprache bewegen. Eine auswählen und beibehalten:
| Übergang | Gefühl | Verwendung |
|---|---|---|
| Schieben / Sliden | „nächster Schritt“ Vorwärtsbewegung | Lineare Walkthroughs |
| Cross-Zoom | Schwung, app-ähnlich | In eine Detailansicht springen |
| Überblendung | ruhig, neutral | Kontextwechsel (Einstellungen → Dashboard) |
| Match-Cut auf einem gemeinsamen Element | nahtlos | Dieselbe Karte bleibt über Bildschirme erhalten |
Den ausgehenden Bildschirm halten, bis sein Klick aufgelöst ist, dann übergehen – niemals mitten in der Bewegung schneiden. Siehe references/annotation-and-transitions.md.
Checkliste für die Ausgabe
- Eingaben in 2× (DPR 2) erfasst; Zooms bleiben bei 2× scharf.
- Jeder Cursor-/Zoom-Wert ist eine reine Funktion von
useCurrentFrame()– keine CSS-Transitionen für animierte Zustände. - Cursor bewegt sich auf einem abgerundeten Pfad (smoothstep); Welle und Zustandsänderung feuern im selben Klick-Frame.
- Jeder Bildschirm eingerahmt (Browser/Gerät) mit einer echten URL und konsistentem Theme.
- Eine Bewegung gleichzeitig (Kamera oder Cursor); Zoom ≤ 2×, 2–4s gehalten.
- Ein Nutzen-Label pro Schritt, im titelsicheren unteren Drittel; Übergänge verwenden eine einheitliche Sprache.
Liefern & verifizieren (gerenderte Standbilder → MP4)
Verpackter Helfer (
scripts/): Ihre Standbilder mitscripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.pnganordnen, dann die Kodierung mitscripts/probe-mp4.sh out.mp4 [WxH] [fps]überprüfen. Siehescripts/README.md.
Dies ist eine schwere Stufe: Die Ausgabe ist ein MP4, keine Live-Seite. Die Demo nimmt Benutzer-Assets (Screenshots/Designs, optionales Logo) auf, daher besteht die Verifikation hauptsächlich darin, ob das richtige Bild tatsächlich geladen und im Frame landet.
Ausgabevertrag:
- Ein Remotion-Projekt mit der registrierten Komposition (
<Composition>+ Zodschema+defaultProps); jeder Cursor-/Zoom-/Übergangswert ist eine reine Funktion vonuseCurrentFrame()(keine CSS-Transitionen, Timer,Date.now()). - Screenshots über
staticFile()geladen, mitdelayRender/continueRenderabgesichert, sodass das 2×-PNG (und jede Web-Schriftart) vorhanden ist, bevor der Frame gerendert wird – andernfalls erscheint der Bildschirm leer mitten in der Demo. - Ausgabe = das gerenderte
out/*.mp4plus das Projekt (neu gerendert, wenn der Screenshot sich ändert).
Verifizierungs-Loop – Standbilder rendern → inspizieren → kodieren. Zuerst günstige PNGs, Video erst, wenn diese richtig sind.
# Frame-genaue Standbilder MIT DEN PROPERTIES, DIE SIE AUSLIEFERN WERDEN (echte Screenshot-Pfade), nicht defaultProps
npx remotion still ProductDemo out/f-settle.png --frame=15 --props=demo.json # Bildschirm eingerahmt, vor Bewegung
npx remotion still ProductDemo out/f-click.png --frame=90 --props=demo.json # Cursor auf Ziel + Welle
npx remotion still ProductDemo out/f-zoom.png --frame=150 --props=demo.json # in Fokusregion gezoomt + Beschriftung
# End-Frame = durationInFrames - 1 (npx remotion compositions liest es aus)
Jedes PNG auf Wiedergabetreue prüfen (korrekter Screenshot geladen; Cursor auf exaktem Pixel; Klickwelle und Zustandsänderung im selben Frame; Beschriftungstext richtig) UND auf Artefakte (Bild leer/nicht geladen, Bildschirm außerhalb des Canvas, Zoom verpixelt oder über den Frame hinaus schießend, Beschriftung außerhalb des titelsicheren unteren Drittels, falsches Seitenverhältnis/Letterboxing).
# Erst nachdem die Standbilder sauber sind:
npx remotion render ProductDemo out/demo.mp4 --props=demo.json
npx remotion render ProductDemo out/demo.gif --props=demo.json --codec=gif # README-Erstbildschirm-Nachweis
Batch (eine Vorlage, viele Produkte): Wenn Sie die Demo pro Produkt neu einkleiden, überprüfen Sie die Properties EINES repräsentativen Produkts mittels Standbilder, bevor Sie den Katalog im Batch rendern – fangen Sie einen leeren Bild- oder Off-Canvas-Fehler einmal, nicht N-mal.
Bevor Sie abschließen:
- Standbilder werden sauber bei Einrichten / Klick / Ende gerendert – keine Fehler, Screenshots tatsächlich geladen (nicht leer).
- Cursor landet auf dem exakten Ziel; Welle + Zustandsänderung im selben Frame; Beschriftungstext korrekt und im titelsicheren unteren Drittel.
- Jede Bewegung frame-gesteuert – keine CSS-Transitionen / Timer /
Date.now()/Math.random(). - Die ausgelieferten Properties (echte Screenshot-Pfade) werden korrekt gerendert, nicht nur
defaultProps. - Vollständiges MP4 kodiert und abspielbar; (optional) GIF für die README gerendert.
Referenzdateien
references/screenshot-demo.md– eine vollständige, ausführbare Remotion-Demo: Erfassen von Screenshots in 2× mit Playwright, die Browser-/Telefonrahmen-Komponenten, eine mehrstufige Komposition, die einen Cursor mit Klickwellen über mehrere Bildschirme führt, und einen Render-Befehl.references/annotation-and-transitions.md– Spotlight-Masken, Pfeil-/Kreis-Callout-Verbinder, eine getippte Feature-Beschriftungsspur und die vier Bildschirm-zu-Bildschirm-Übergänge mit frame-gesteuerten Implementierungen und Multi-Aspect (16:9 / 9:16) Safe-Area-Notizen.