Communitygithub.com

caption-animation

Dieses Skill sollte verwendet werden, wenn der Nutzer nach animierten Untertiteln für Kurzvideos fragt.

Was ist caption-animation?

Das Skill „caption-animation“ wandelt ein Transkript oder einen Voiceover in wortgenaue, aufmerksamkeitsstarke Untertitel um – jedes Wort erscheint im Takt der Silben, das aktive Wort wird hervorgehoben, und der Text bleibt stets synchron zur Erzählung. Es ermöglicht die Erstellung von Karaoke-Untertiteln, die die Verweildauer auf Plattformen wie TikTok, Instagram Reels und YouTube Shorts steigern. Das Skill unterstützt sowohl das Rendering mit Remotion als auch die Umsetzung in reinem CSS/HTML für Web-Player. Die Pipeline umfasst die Transkription mit Whisper oder AssemblyAI, die Normalisierung in ein einheitliches Token-Format, die Aufteilung in Seiten zu 1–4 Wörtern, die Animation mit `spring()`- oder CSS-Keyframes, die Platzierung im sicheren Bereich des 9:16-Formats und die Ausgabe als eingebrannte MP4-Datei sowie als separate SRT/VTT-Datei. Es stellt sicher, dass Wort-für-Wort-Zeitsteuerung, Lesbarkeit (fette serifenlose Schrift, Kontrast, Strich/Schatten) und ein Überprüfungs-Workflow mit Einzelbildausgaben eingehalten werden.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/iart-ai/tiktok-video-skills/tree/main/skills/caption-animation

In Ihrer bevorzugten KI fragen

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

Dokumentation

Was macht caption-animation?

Verwandle ein Transkript oder einen Voiceover in wortgenaue, aufmerksamkeitsstarke Untertitel: Jedes Wort erscheint silbensynchron, das aktive Wort wird hervorgehoben, und der Text bleibt fest mit der Erzählung verbunden. Erstelle die Art von Untertiteln, die die Verweildauer auf TikTok, Reels und Shorts steigern – lesbar, im Takt und im sicheren Bereich.

Wann verwenden

  • Wort-für-Wort- oder Karaoke-Untertitel für kurze vertikale Videos (9:16).
  • Einbrennen offener Untertitel auf einer Voiceover-/Narration-Spur von einem Transkript.
  • Umwandlung eines Whisper-/SRT-Transkripts in animierten Text in Remotion oder im Web.
  • Neugestaltung automatisch generierter Untertitel in eine markenkonforme „Hormozi“-Enthüllung.

Die Pipeline

SchrittAufgabeWerkzeug
TranskribierenAudio → Wort-ZeitstempelWhisper (@remotion/install-whisper-cpp), AssemblyAI
Normalisieren→ { text, startMs, endMs }[] Tokens@remotion/captions Caption-Typ
SeiteWörter in Gruppen von 1–4 Wörtern einteilencreateTikTokStyleCaptions()
AnimierenWort-Erscheinen + Hervorhebung des aktiven WortesRemotion spring() / CSS
PlatzierenSicherer Bereich, lesbare SchriftLayout-Regeln unten
ExportierenEinbrennen (MP4) und/oder separate SRT/VTTRemotion-Rendering / Datei-Ausgabe

Zwei unverhandelbare Regeln

  1. Wortgenaue Zeitsteuerung, nicht auf Zeilenebene. Karaoke wirkt nur dann magisch, wenn jedes Wort auf die Silbe fällt. Transkribiere immer mit Wort-Zeitstempeln; teile niemals eine Zeile gleichmäßig über ihre Dauer auf – Drift ist sofort sichtbar.
  2. Lesbarkeit vor Stil. Untertitel werden auf dem Handy, im Sonnenlicht und stumm gelesen. Fette serifenlose Schrift, starker Strich oder Schatten, hoher Kontrast zuerst; Dekoration ist zweitrangig. Ein Untertitel, den niemand lesen kann, ist Dekoration, kein Untertitel.

Wort-Zeitsteuerung aus einem Transkript

Whisper liefert direkt Wort-Zeitstempel. Normalisiere jede Quelle in ein einheitliches flaches Token-Format, damit der Renderer nie wissen muss, woher die Wörter kamen:

// Caption-Token – die eine Form, die alles nachgelagert konsumiert
 type Token = { text: string; startMs: number; endMs: number };

// Parst einen SRT-Cue-Block "00:00:01,200 --> 00:00:01,640" in ms
 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 Zeitstempel auf Satzebene existieren, transkribiere erneut, um Wort-Zeitstempel zu erhalten – interpoliere nicht. Whispers medium.en ist etwa 2× schneller als large und genau genug für saubere Sprachaufnahmen.

Wort-für-Wort-Erscheinen (Remotion)

Treibe die Skalierung und Deckkraft jedes Wortes über einen spring()-Wert an, der auf seinem eigenen startMs basiert. Der mikroskopische Überschwinger verleiht ihm das Gefühl von „Lebendigkeit“.

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 Eintrittsframe des Wortes
   const p = spring({ frame: frame - enter, fps, config: { damping: 12, mass: 0.6 } });
   const scale = interpolate(p, [0, 1], [0.6, 1]);        // erscheint von 60% → 100% (mit Überschwinger)
   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, indem du frame gegen die [startMs, endMs] jedes Tokens prüfst. Behalte die Hervorhebung in einer einzigen Akzentfarbe oder als gefüllte „Box“ hinter dem aktiven Wort – animiere niemals die Farbe jedes Wortes gleichzeitig.

Seiten, keine Textwände

Zeige 1–4 Wörter gleichzeitig an. Verwende createTikTokStyleCaptions() von Remotion (oder gruppiere manuell) und passe combineTokensWithinMilliseconds an: ~200–500ms für wortgenaue Anzeige, ~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-Erscheinen (ohne Remotion)

Für einen DOM/Live-Player gib jedem Wort eine 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 Schrift und Platzierung im sicheren Bereich (9:16, 1080×1920)

EigenschaftWertWarum
SchriftartFett/Extrafett serifenlos (Montserrat, Inter, Helvetica)Lesbar auf kleinen, überladenen Bildschirmen
Größe56–80px (≈8% der Rahmenhöhe), min 45pxLesbar bei Stummschaltung auf dem Handy
Strich2–6px schwarzer, durchgehender RandÜberlebt jeden Hintergrund
SchattenWeicher Schlagschatten als Backup zum StrichTrennung bei hellen Rahmen
Groß-/KleinschreibungGroßbuchstaben oder Satz; hoher KontrastfüllungPrägnanz + Lesbarkeit
Vertikale PositionMittlerer Bereich, ~62–70% von obenÜber der UI, unter der Handlung
Unterer SicherheitsabstandFrei von unteren ~280px / 15%Vermeidet Untertitel-/CTA-/Audio-UI
Seitlicher SicherheitsabstandInnerhalb der mittleren 80% BreiteVermeidet Symbole in der rechten Leiste

Platziere Untertitel im mittleren Bereich – nicht am unteren Rand, wo die Plattform-UI (Benutzername, Audio, Buttons) ist. Polstere den Text auf maximal ~2 Zeilen.

Synchronisation mit einem Voiceover / einer Narration

Das Transkript trägt bereits die Zeitsteuerung der exakten Audio. Lege dieselbe Audio unter die Komposition und behalte die Zeitstempel in der Zeitbasis der Audio – Untertitel und Stimme bleiben ohne manuelle Anpassung fest synchron. Wenn der VO neu aufgenommen wird, transkribiere erneut; verschiebe niemals Offsets von Hand.

Einbrennen vs. separate SRT/VTT

Einbrennen (Open)Separate SRT/VTT (Closed)
WoPixel in der MP4Separate .srt/.vtt-Datei
Social (TikTok/Reels)Erforderlich – garantiert, gestaltbarWird oft von der Plattform ignoriert
Barrierefreiheit (ADA/WCAG)Erfüllt allein nicht die AnforderungenErforderlich (umschaltbar)
Beste PraxisAnimierte Untertitel für Social einbrennenAuch eine separate SRT/VTT für Web/SEO ausgeben

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

Checkliste für die Ausgabe

  • Wort-Zeitstempel (echt, aus Transkription) – keine gleichmäßig aufgeteilte Fälschung.
  • Wort-Erscheinen mit mikroskopischem Überschwinger; eine Akzentfarbe für das aktive Wort.
  • 1–4 Wörter pro Seite; niemals eine Textwand.
  • Fette serifenlose Schrift, 56–80px, 2–6px Strich + Schatten, hoher Kontrast.
  • Mittlerer Bereich, frei von unten ~280px und den seitlichen Leisten.
  • Audio unter der Komposition; Zeitstempel in der Zeitbasis der Audio.
  • Einbrennen für Social und eine separate SRT/VTT für Barrierefreiheit ausliefern.

Ausliefern & verifizieren (gerenderte Standbilder → MP4)

Verpackter Helfer (scripts/): erstelle eine Kontaktseite mit scripts/contact-sheet.sh sheet.png f-hook.png f-mid.png f-end.png 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 frame-gesteuert über useCurrentFrame(), niemals Date.now() / Math.random() / Timer. Lieferbar = out/*.mp4 (eingebrannt) + Projekt + separate SRT/VTT. 9:16 vertikal (1080×1920) ist die Voreinstellung.

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

# Standbilder am Start / einem ausgewählten aktiven-Wort-Frame / Ende – MIT AUSGELIEFERTEN PROPS (echte Tokens + 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

# Überprüfe jedes PNG:
#  - das bei Frame 90 hervorgehobene Wort ist dasjenige, dessen [startMs,endMs] 90/fps enthält (kein Drift)
#  - Einbrennung lesbar: fette serifenlose Schrift, Strich+Schatten hält, kein Beschneiden
#  - 9:16 sicherer Bereich: Untertitel sitzt im mittleren Bereich, frei von oben ~12% und unten ~20-35% (Untertitel/CTA/Audio-UI) und der rechten Aktionsleiste

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

Verwende npx remotion compositions um durationInFrames/fps auszulesen und die Frames für das aktive Wort und das Ende auszuwählen.

Bevor du abschließt:

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

Referenzdateien

  • references/word-timed-captions.md – End-to-End-Build: Whisper-Transkription und der Caption-Typ, ein vollständiger SRT-Parser, Remotion-Komponente mit Wort-Zeitsteuerung und aktiver Hervorhebung, manuelle Seiteneinteilung, ein SRT/VTT-Emittent, plattformspezifische Karten des sicheren Bereichs und ein Datenblatt für lesbare Schrift.

Individual skills in this repo

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

Verwandte Skills