Communitygithub.com

Kreesten-hsh/video

Dieses Skill sollte verwendet werden, wenn der Nutzer „animierte Untertitel hinzufügen“, „TikTok-Untertitel erstellen“, „Karaoke-Untertitel“, „Wort-für-Wort-Untertitel“, „Auto-Untertitel“, „das aktive Wort hervorheben“, „Untertitel in ein Video einbrennen“, „Untertitel mit einem Voiceover/Sprecher synchronisieren“ oder „ein SRT/Transkript in animierten Text umwandeln“ verlangt. Es deckt Wort-für-Wort-Zeitsteuerung von Whisper, Einblendung pro Wort, Hervorhebung des aktiven Wortes, positionierung im 9:16-Safe-Area, lesbare Schriftart und Einbrennen vs. Sidecar-SRT/VTT ab.

Was ist video?

Dieses Skill, „Caption Animation“ (Untertitel-Animation), wandelt ein Transkript oder Voiceover in wortgenaue, dynamische Untertitel um. Jedes Wort erscheint mit einem sanften Einblendungseffekt zum richtigen Zeitpunkt, das aktuell gesprochene Wort wird hervorgehoben, und der Text bleibt präzise mit der Tonspur synchronisiert. Es ist speziell für kurze, vertikale Videoformate wie TikTok, Reels und Shorts im 9:16-Format optimiert. Es unterstützt sowohl das Einbrennen von Untertiteln (offene Untertitel) in MP4-Dateien als auch die Ausgabe von Sidecar-Dateien im SRT/VTT-Format für Barrierefreiheit und Web-Anwendungen. Die Implementierung basiert auf Technologien wie Whisper für die Transkription mit Wortzeitstempeln, Remotion oder CSS für die Animation und @remotion/captions für das Erstellen von Seiten. Es wird auf Plattformen wie Remotion, Web/DOM und über CLI-Befehle ausgeführt und folgt strikten Regeln für Lesbarkeit, Safe-Area-Platzierung und wortgenaue Synchronisation, um professionelle Ergebnisse zu liefern.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Kreesten-hsh/video/tree/HEAD/.agents/skills/caption-animation

In Ihrer bevorzugten KI fragen

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

Dokumentation

Untertitel-Animation

Verwandle ein Transkript oder Voiceover in wortgenaue, scroll-stoppende Untertitel: Jedes Wort erscheint im Takt, das aktive Wort wird hervorgehoben, und der Text bleibt fest mit der Erzählung verbunden. Erstelle die Art von Untertiteln, die die Sehzeit auf TikTok, Reels und Shorts steigern – lesbar, im Takt und innerhalb des Sicherheitsbereichs.

Wann verwenden

  • Wort-für-Wort- oder Karaoke-Untertitel in kurzen vertikalen Videos (9:16).
  • Einbrennen von offenen Untertiteln auf einer Voiceover-/Erzählspur aus einem Transkript.
  • Umwandlung eines Whisper/SRT-Transkripts in animierten Text in Remotion oder im Web.
  • Neugestaltung automatisch generierter Untertitel in eine markenbezogene „Hormozi-Stil“-Einblendung.

Der Workflow

SchrittAufgabeWerkzeug
TranskribierenAudio → Wort-ZeitstempelWhisper (@remotion/install-whisper-cpp), AssemblyAI
Normalisieren→ { text, startMs, endMs }[] Token@remotion/captions Caption-Typ
SeitierenWörter in 1–4-Wort-„Seiten“ gruppierencreateTikTokStyleCaptions()
AnimierenEinblendung pro Wort + Hervorhebung des aktiven WortesRemotion spring() / CSS
PlatzierenSicherer Bereich, lesbare SchriftartLayout-Regeln unten
ExportierenEinbrennen (MP4) und/oder Sidecar SRT/VTTRemotion-Rendering / Datei-Export

Zwei unverhandelbare Regeln

  1. Wortgenaue Zeitsteuerung, nicht auf Zeilenebene. Karaoke wirkt nur dann magisch, wenn jedes Wort auf die Silbe trifft. Transkribiere immer mit Wortzeitstempeln; teile niemals eine Zeile gleichmäßig über ihre Dauer auf – eine Abweichung ist sofort sichtbar.
  2. Lesbarkeit vor Stil. Untertitel werden auf einem Telefon, bei Sonnenlicht und stummgeschaltet gelesen. Kräftige serifenlose Schrift, starker Rand oder Schlagschatten, hoher Kontrast zuerst; Dekoration kommt danach. Ein Untertitel, den niemand lesen kann, ist Dekoration, kein Untertitel.

Wortzeitsteuerung aus einem Transkript

Whisper liefert direkt Wortzeitstempel. Normalisiere jede Quelle in eine einheitliche Token-Form, damit der Renderer nie wissen muss, woher die Wörter kommen:

// Untertitel-Token – die eine Form, die alles stromabwärts konsumiert
type Token = { text: string; startMs: number; endMs: number };

// Einen SRT-Cue-Block "00:00:01,200 --> 00:00:01,640" in ms parsen
const toMs = (t: string) => {
  const [h, m, rest] = t.split(":");
  const [s, ms] = rest.split(",");
  return ((+h * 60 + +m) * 60 + +s) * 1000 + +ms;
};

Wenn nur Cues auf Satzebene existieren, transkribiere neu für Wortzeitsteuerung – interpoliere nicht. Whisper medium.en ist ~2x schneller als large und genau genug für saubere Sprachaufnahmen.

Einblendung pro Wort (Remotion)

Treibe Skalierung und Deckkraft jedes Wortes mit einer Feder, die an seinem eigenen startMs verankert ist. Das kleine Überschwingen lässt es „lebendig“ wirken.

import { useCurrentFrame, useVideoConfig, spring, interpolate } from "remotion";

const Word: React.FC<{ token: Token; active: boolean }> = ({ token, active }) => {
  const frame = useCurrentFrame();
  const { fps } = useVideoConfig();
  const enter = (token.startMs / 1000) * fps;            // eigener Einblend-Frame des Wortes
  const p = spring({ frame: frame - enter, fps, config: { damping: 12, mass: 0.6 } });
  const scale = interpolate(p, [0, 1], [0.6, 1]);        // Einblendung von 60% → 100% (überschwingt)
  return (
    <span style={{
      display: "inline-block",
      transform: `scale(${scale})`,
      opacity: interpolate(p, [0, 1], [0, 1]),
      color: active ? "#FFE45E" : "#FFFFFF",             // Hervorhebung des aktiven Wortes
      transition: "color 80ms linear",
    }}>{token.text}&nbsp;</span>
  );
};

Berechne active durch Testen von frame gegen die [startMs, endMs] jedes Tokens. Behalte die Hervorhebung als eine einzelne Akzentfarbe oder eine gefüllte „Box“ hinter dem aktuellen Wort bei – animiere niemals die Farbe jedes Wortes gleichzeitig.

Seiten, keine Textwände

Zeige 1–4 Wörter gleichzeitig an. Verwende Remotions createTikTokStyleCaptions() (oder gruppiere manuell) und stimme combineTokensWithinMilliseconds ab: ~200–500ms für echte Wort-für-Wort-Darstellung, ~1000–1500ms für kurze lesbare Phrasen.

import { createTikTokStyleCaptions } from "@remotion/captions";
const { pages } = createTikTokStyleCaptions({
  captions, combineTokensWithinMilliseconds: 1200,       // ~2–3 Wörter pro Seite
});

Web/CSS-Einblendung (ohne Remotion)

Für einen DOM/Live-Player gib jedem Wort seine eigene animation-delay gleich startMs:

.word { display:inline-block; opacity:0; animation: pop .26s cubic-bezier(.34,1.56,.64,1) forwards; }
@keyframes pop { from { opacity:0; transform:translateY(.18em) scale(.7) } to { opacity:1; transform:none } }
.word.active { color:#FFE45E; }
words.forEach(w => { const el = mk(w.text); el.style.animationDelay = `${w.startMs}ms`; track.append(el); });

Lesbare Schriftart und Platzierung im sicheren Bereich (9:16, 1080×1920)

EigenschaftWertGrund
SchriftartFett/ExtraFett serifenlos (Montserrat, Inter, Helvetica)Lesbar auf kleinen, überladenen Bildschirmen
Größe56–80px (≈8% der Bildhöhe), mindestens 45pxLesbar bei Stummschaltung auf einem Telefon
Rand2–6px durchgezogener schwarzer UmrissÜbersteht jeden Hintergrund
SchlagschattenWeicher Schlagschatten als Alternative zum RandAbhebung auf hellen Bildern
Groß-/KleinschreibungGroßbuchstaben oder Satz; hoher Kontrast in der FüllungWirkung + Lesbarkeit
Vertikale PositionMittlerer Bereich, ~62–70% von obenOberhalb der UI, unterhalb der Handlung
Unterer SicherheitsabstandFreihalten von den unteren ~280px / 15%Vermeidung von Untertitel-/CTA/Audio-UI
Seitlicher SicherheitsabstandInnerhalb der mittleren 80% BreiteVermeidung von rechten Symbolleisten

Platziere Untertitel im mittleren Bereich – nicht ganz unten, wo die Plattform-UI (Benutzername, Audio, Buttons) ist. Begrenze den Text auf maximal ~2 Zeilen.

Synchronisation mit einem Voiceover / einer Erzählung

Das Transkript trägt bereits die Zeitsteuerung des exakten Audios. Lege dasselbe Audio unter die Komposition und behalte die Zeitstempel in der Zeitbasis des Audios – Untertitel und Stimme bleiben ohne manuelles Nachjustieren synchron. Wenn der Voiceover neu aufgenommen wird, transkribiere neu; verschiebe niemals Offsets von Hand.

Einbrennen vs. Sidecar SRT/VTT

Einbrennen (offen)Sidecar SRT/VTT (geschlossen)
WoPixel im MP4Separate .srt/.vtt-Datei
Social Media (TikTok/Reels)Erforderlich – garantiert, stylbarWird von der Plattform oft ignoriert
Barrierefreiheit (ADA/WCAG)Erfüllt die Anforderungen allein nichtErforderlich (umschaltbar)
Best PracticeAnimierte Untertitel für Social Media einbrennenAuch eine Sidecar-Datei für Web/SEO ausliefern

Gib beides aus: Brenne die animierten Untertitel für Social Media ein und schreibe eine einfache SRT/VTT-Datei aus denselben Token für barrierefreie/SEO-Wiedergabe.

Checkliste für die Ausgabe

  • Wortzeitstempel (echt, aus der Transkription) – keine gleichmäßig aufgeteilte Fälschung.
  • Einblendung pro Wort mit leichtem Überschwingen; eine Akzentfarbe für das aktive Wort.
  • 1–4 Wörter pro Seite; niemals eine Textwand.
  • Fette serifenlose Schrift, 56–80px, 2–6px Rand + Schatten, hoher Kontrast.
  • Mittlerer Bereich, freigehalten von unteren ~280px und seitlichen Rändern.
  • Audio unter der Komposition angelegt; Zeitstempel in der Audio-Zeitbasis.
  • Einbrennen für Social Media und eine Sidecar-SRT/VTT für Barrierefreiheit ausliefern.

Ausliefern und überprüfen (gerenderte Standbilder → MP4)

Verpacktes Hilfsskript (scripts/): Ordne deine Standbilder mit scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png an und überprüfe dann die Kodierung mit scripts/probe-mp4.sh out.mp4 [WxH] [fps]. Siehe scripts/README.md.

Untertitel werden als Remotion-Komposition (<Composition> + Zod-schema + defaultProps) ausgeliefert – alle Wortbewegungen werden framegetrieben durch useCurrentFrame(), niemals Date.now() / Math.random() / Timer. Liefergegenstand = out/*.mp4 (eingebrannt) + das Projekt + die Sidecar-SRT/VTT. 9:16 vertikal (1080×1920) ist der Standard.

Überprüfungsschleife – Standbilder rendern → prüfen → kodieren. Die Wort-Zeitsteuerung ist das, was kaputtgeht; überprüfe sie an genauen Frames, bevor du eine Kodierung startest.

# Standbilder am Anfang / einem Beispiel-Frame mit aktivem Wort / Ende – MIT AUSGELIEFERTEN PROPS (echte Token + Audio)
npx remotion still Captions out/f-start.png --frame=0   --props='{"captionsSrc":"vo.json"}'
npx remotion still Captions out/f-mid.png   --frame=90  --props='{"captionsSrc":"vo.json"}'
npx remotion still Captions out/f-end.png   --frame=N   --props='{"captionsSrc":"vo.json"}'  # N = durationInFrames-1

# Jedes PNG überprüfen:
#  - Das bei Frame 90 hervorgehobene Wort ist das Wort, dessen [startMs,endMs] 90/fps enthält (keine Abweichung)
#  - Eingebrannt lesbar: fette serifenlose Schrift, Rand+Schatten hält, kein Beschneiden
#  - 9:16-Sicherheitsbereich: Untertitel sitzt im mittleren Bereich, frei von den oberen ~12 % und den unteren ~20–35 % (Untertitel/CTA/Audio-UI) und der rechten Aktionsleiste

npx remotion render Captions out/captions.mp4 --props='{"captionsSrc":"vo.json"}'   # kodieren, sobald die Standbilder stimmen
npx remotion render Captions out/demo.gif --codec=gif                                # README-Nachweis-Clip

Verwende npx remotion compositions, um durationInFrames/fps auszulesen und die Frames mit aktivem Wort sowie den Endframe auszuwählen.

Bevor du abschließt:

  1. Standbilder werden sauber bei Frame 0, einem mittleren Frame mit aktivem Wort und dem letzten Frame gerendert – keine fehlende Schriftart/fehlendes Audio.
  2. Das richtige Wort wird beim abgetasteten Frame hervorgehoben (Frame/fps liegt innerhalb seines Tokens); keine gleichmäßig aufgeteilte Fälschung.
  3. Die Einbrennung ist lesbar (Rand+Schatten) und der Untertitel befindet sich bei jedem überprüften Frame vollständig im 9:16-Sicherheitsbereich.
  4. Nur framegetrieben – kein Date.now() / Math.random() / Timer.
  5. Ausgelieferte Props (echte Token, nicht nur defaultProps) werden korrekt gerendert; MP4 + Sidecar-SRT/VTT ausgegeben, GIF optional.

Referenzdateien

  • references/word-timed-captions.md – End-to-End-Aufbau: Whisper-Transkription und der Caption-Typ, ein vollständiger SRT-Parser, Remotion-Komponente mit Wortzeitsteuerung und aktiver Hervorhebung, manuelle Seitierung, ein SRT/VTT-Export, plattformspezifische Safe-Area-Karten und ein Spezifikationsblatt für lesbare Schriftarten.

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

Verwandte Skills