# Prosody
URL: https://remocn.dev/docs/captions/components/caption-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

- Install: `npx shadcn@latest add @remocn/caption-prosody`
- Vibe: playful
- Natural length: transcript-driven

## Installation

```bash
npx 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](/docs/captions/getting-started/positioning) and
[Get a transcript](/docs/captions/getting-started/get-a-transcript).

```tsx
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

| 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`. |

## Use when
- The way something is said matters as much as what is said — a slow, stretched word reads as emphasis and a quick aside shrinks, so the captions carry the speaker's rhythm without audio.
- You want expressive kinetic type that comes from the recording itself rather than from a hand-picked keyword list.

## Don't use when
- You want to choose which word is emphasised yourself — use `caption-stack`, which enlarges the keywords you pass.
- The captions should read calmly at one size — use `caption-karaoke` or `caption-subtitle`.
- There is no voiceover and the text is scripted — use `word-stream`.