Prosody
Word size follows the delivery, so drawn-out words land large and rushed words stay small, and the rhythm of the voice is visible with the sound off
$ pnpm dlx shadcn@latest add @remocn/caption-prosodyUsage
CaptionProsody sizes every word by how long the speaker spent on each of its
letters, measured against the rest of the page. A word held twice as long as
the page's typical pace grows to maxScale, one said twice as fast shrinks to
minScale, and everything in between scales smoothly. Sizes are fixed when a
page starts, so the line never reflows: words then appear at the moment they
are spoken, and the word being spoken takes activeColor.
The effect comes straight from the transcript timing, so it is only as expressive as the timestamps are precise — word-level timestamps from whisper.cpp or the OpenAI API work best. The component renders only the text block; see Positioning and Get a transcript.
import { AbsoluteFill } from "remotion";
import { CaptionProsody } from "@/components/remocn/caption-prosody";
import captions from "./captions.json";
export const MyScene = () => (
<AbsoluteFill
style={{
justifyContent: "flex-end",
alignItems: "center",
paddingBottom: 108,
paddingInline: 192,
}}
>
<CaptionProsody captions={captions} />
</AbsoluteFill>
);| Prop | Type | Default | Description |
|---|---|---|---|
captions | Caption[] | — | The timed transcript, in the `Caption` shape from `@remotion/captions`. Times are relative to the start of the surrounding `Sequence`. |
minScale | number | 0.75 | Size of a word said twice as fast as the page's typical pace, relative to `fontSize`. |
maxScale | number | 1.6 | Size of a word held twice as long as the page's typical pace, relative to `fontSize`. |
combineTokensWithinMilliseconds | number | 1200 | How much speech one page may span before the next word starts a new page. Pages also break at the end of a sentence and on any pause of at least `holdMs` (300 ms minimum). |
holdMs | number | 600 | How long a page stays after its last word when the next page is not already due. It fades out at the end of the hold. |
fontSize | number | 64 | Base font size in pixels; a word at the typical pace renders at this size. |
fontWeight | number | 800 | Font weight. The font itself is inherited. |
color | string | "#ffffff" | Colour of every word that is not being spoken right now. |
activeColor | string | "#facc15" | Colour of the word being spoken. |
shadow | boolean | true | A tight text shadow that keeps the caption legible over bright footage. |
className | string | — | Classes for the text block — width, font family, `uppercase`. |