You are helping the user add animated word-by-word captions to their Remotion video.
Load the remotion-production skill for the captions-workflow rule.
Workflow
1. Identify the Audio Source
Find the voiceover or primary audio file:
# Check for existing audio files
ls public/audio/ public/ 2>/dev/null | grep -E '\.(mp3|wav|m4a|ogg)$'
If no audio exists, suggest running /add-voiceover first.
2. Transcribe with Whisper
Generate word-level timestamps:
Use remotion-media generate_subtitles:
- input_file: [audio filename inside public/, e.g. narration-intro.mp3]
Files land in public/ of the directory your agent was launched from, so start your agent (Claude Code, Codex, Gemini CLI…) from the Remotion project root.
This returns an SRT file with timestamps. Save to public/captions/.
3. Install Captions Package
Ensure @remotion/captions is installed:
npx remotion add @remotion/captions
4. Choose Caption Style
Ask the user what style they want:
- TikTok style (default) — Bold, centered, one word at a time, colored highlight
- YouTube style — Bottom of screen, sentence at a time
- Karaoke style — Words highlight as they're spoken
- Minimal — Small, subtle, bottom-left
5. Create the Captions Component
import { useCurrentFrame, useVideoConfig } from "remotion";
import { createTikTokStyleCaptions } from "@remotion/captions";
// Parse the transcription into Remotion Caption objects
// Apply createTikTokStyleCaptions() for word-by-word timing
export const Captions: React.FC<{ captions: Caption[] }> = ({ captions }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const currentTimeMs = (frame / fps) * 1000;
const { pages } = createTikTokStyleCaptions({
captions,
combineTokensWithinMilliseconds: 800,
});
// Find current page based on time
const currentPage = pages.find(
(p) => currentTimeMs >= p.startMs && currentTimeMs < p.startMs + p.durationMs
);
if (!currentPage) return null;
return (
<div style={{
position: "absolute",
bottom: "15%",
left: 0,
right: 0,
display: "flex",
justifyContent: "center",
padding: "0 40px",
}}>
<div style={{
fontSize: 64,
fontWeight: 800,
textAlign: "center",
textShadow: "2px 2px 8px rgba(0,0,0,0.8)",
color: "white",
}}>
{currentPage.tokens.map((token, i) => {
const isActive = currentTimeMs >= token.fromMs && currentTimeMs < token.toMs;
return (
<span key={i} style={{
color: isActive ? "#FFD700" : "white",
transition: "color 0.1s",
}}>
{token.text}
</span>
);
})}
</div>
</div>
);
};
6. Customize Appearance
Offer the user these customization options:
- Position: top (10%), center (50%), bottom (15%)
- Font size: Small (40px), Medium (56px), Large (72px)
- Highlight color: Yellow (#FFD700), Cyan (#00FFFF), Green (#00FF88), custom
- Background: None, semi-transparent box, blur
- Font: Inter, Poppins, or whatever the project uses
- Animation: Pop-in, fade-in, slide-up for each word group
7. Wire into Composition
Add the Captions component as an overlay layer on top of everything:
// In the main composition, as the LAST visual layer (on top):
<Captions captions={parsedCaptions} />
8. Preview & Adjust
Tell the user to check captions in the Remotion preview. Common adjustments:
- Text too small/large on mobile? → Adjust font size
- Words moving too fast? → Increase
combineTokensWithinMilliseconds - Wrong timing? → Check audio/caption sync
- Overlapping with visuals? → Change position (top vs bottom)