* Add remotion-video skill Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019yhnsd9RDz7jDSUUX7oEcH * Add Phase 2 templates to remotion-video skill Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019yhnsd9RDz7jDSUUX7oEcH * Fix captions template background; document browser flag after test render Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019yhnsd9RDz7jDSUUX7oEcH * Add audio/voiceover template (Phase 3), tested with real render Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019yhnsd9RDz7jDSUUX7oEcH * Document font loading and its network requirement; captions-over-video verified Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019yhnsd9RDz7jDSUUX7oEcH --------- Co-authored-by: Claude <noreply@anthropic.com>
1.6 KiB
1.6 KiB
Template: Captions
Words appear in time with speech. Give each word a start time in seconds.
src/Captions.tsx
import { AbsoluteFill, useCurrentFrame, useVideoConfig } from "remotion";
type Word = { text: string; start: number; end: number }; // seconds
export const Captions = ({ words, background = "#111" }: { words: Word[]; background?: string }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const t = frame / fps;
// Show a window of up to 5 words around the current one.
const current = words.findIndex((w) => t >= w.start && t < w.end);
const i = current === -1 ? 0 : current;
const visible = words.slice(Math.floor(i / 5) * 5, Math.floor(i / 5) * 5 + 5);
return (
<AbsoluteFill style={{ backgroundColor: background, justifyContent: "flex-end", alignItems: "center", paddingBottom: 200 }}>
<div style={{ fontSize: 72, fontWeight: 800, color: "white", textShadow: "0 4px 12px black", textAlign: "center", maxWidth: "80%" }}>
{visible.map((w, k) => (
<span key={k} style={{ color: t >= w.start && t < w.end ? "#ffd400" : "white", marginRight: 16 }}>
{w.text}
</span>
))}
</div>
</AbsoluteFill>
);
};
Tips
- Get word times from a transcription tool (e.g.
@remotion/captionsor Whisper) and save them as JSON inpublic/. backgroundis the backdrop when there is no video. Pass"transparent"when layering over a video.- Layer it over the video:
<AbsoluteFill><OffthreadVideo src={staticFile("clip.mp4")} /><Captions words={words} background="transparent" /></AbsoluteFill>.