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

Installation
$ pnpm dlx shadcn@latest add @remocn/caption-prosody

Usage

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>
);
Props
PropTypeDefaultDescription
captions
Caption[]—The timed transcript, in the `Caption` shape from `@remotion/captions`. Times are relative to the start of the surrounding `Sequence`.
minScale
number0.75Size of a word said twice as fast as the page's typical pace, relative to `fontSize`.
maxScale
number1.6Size of a word held twice as long as the page's typical pace, relative to `fontSize`.
combineTokensWithinMilliseconds
number1200How 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
number600How 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
number64Base font size in pixels; a word at the typical pace renders at this size.
fontWeight
number800Font 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
booleantrueA tight text shadow that keeps the caption legible over bright footage.
className
string—Classes for the text block — width, font family, `uppercase`.