Read aloud
Read a message aloud. An icon button in the message actions turns into a compact player with play and pause, progress, speed and stop, through idle, preparing, playing, paused and failed states. Plays a URL, your own text to speech, or the browser's Web Speech API.
On this page
Installation
Usage
import { ReadAloud } from "@/components/ui/chatcn/read-aloud"
<MessageActions>
<MessageActionCopy value={text} />
<ReadAloud text={text} />
</MessageActions>With only text, it speaks through the Web Speech API, one sentence at a time. That makes pause and speed changes reliable across browsers, and the index of the sentence being read comes for free. Where there is no speech engine, the button stays visible but inert, with a tooltip that says why.
For your own voices, pass src, or onSynthesize, which is called once on first play and may return a URL or a URL with sentence timings. The result is reused for replays, and the call is aborted if the user stops first.
<ReadAloud
text={text}
onSynthesize={async (text, { signal }) => {
const res = await fetch("/api/speech", { method: "POST", body: text, signal })
return URL.createObjectURL(await res.blob())
}}
/>To mark the sentence being read, keep the index from onSentenceChange and render plain text with ReadAloudText, which splits sentences exactly as the reader does.
const [sentence, setSentence] = React.useState(-1)
<MessageContent><ReadAloudText text={text} active={sentence} /></MessageContent>
<ReadAloud text={text} onSentenceChange={setSentence} />Examples
Your own speech
Player states
ReadAloudPlayer is the presentational player on its own, for driving from another engine or for showing states.
Props
| Prop | Type | Notes |
|---|---|---|
text | string | Required. Spoken by speech, and split into sentences |
src | string | A ready audio URL. Wins over the rest |
timings | { start, end }[] | Sentence timings in seconds for src |
onSynthesize | (text, { signal }) => Promise<string | { url, timings? }> | Your text to speech |
speech | boolean | Fall back to Web Speech. Defaults to true |
lang, voice | string | Speech language and preferred voice name |
rate, defaultRate, onRateChange | number | Controlled or uncontrolled speed |
rates | number[] | Speeds the button cycles. Defaults to 1, 1.25, 1.5, 2 |
onSentenceChange | (index: number) => void | -1 when nothing is being read |
onEnd, onError | () => void, (error) => void | |
label | string | Tooltip and name of the button. Defaults to "Read aloud" |
useChatcnReadAloud exposes the same engine as a hook: status, progress, elapsed, duration, rate, sentence, and play, pause, toggle, stop, retry.
Keyboard
| Key | Action |
|---|---|
Enter, Space | On the button, start reading and move focus to the player's play button |
Tab | Move between play, speed and stop |
Enter on stop | Stop and return focus to the read aloud button |
Accessibility
- The player is a group named Read aloud. Progress is a
progressbarwith a percentage, and time is announced as 0:12 of 1:04. - Preparing audio and Paused are announced politely. Errors use
role="alert"and say how to fix the problem. - The speed button names both the current and the next speed. Every button has a 44 px hit area on touch screens.
Motion
- Opened by pointer, the player grows from the button's edge: opacity and scale from 0.96, 150 ms. Opened from the keyboard it appears at once. Under reduced motion it only fades.
- Play, pause and the spinner swap with the kit's 150 ms blur crossfade. Progress moves by transform only.
- The sentence mark changes instantly: it follows speech, not the eye.