Skip to content

Events

ResponsiveVoice gives you two ways to observe speech:

  • Per-call callbacks — passed inline to speak(), bound to that one utterance, and reset on the next speak().
  • Global events — registered once with addEventListener(), firing across every speak() call.

Pass these in the options object of speak(text, voice?, options?). They belong to a single utterance.

CallbackSignatureFires
onstart() => voidspeech begins
onend() => voidspeech completes
onerror(error: Error) => voidan error occurs
onboundary(charIndex: number, name: string) => voidcrosses a word/sentence boundary (native only)
rv.speak('Hello world', 'UK English Female', {
onstart: () => console.log('Started speaking'),
onend: () => console.log('Finished speaking'),
onerror: (error) => console.error('Error:', error.message),
onboundary: (charIndex, name) => {
console.log(`Boundary (${name}) at character ${charIndex}`);
},
});

Register once with addEventListener(name, handler); they fire across every speak() call. Event names are PascalCase:

EventPayloadFires
OnReadyclient initialized and ready
OnLoadalias of OnReady (legacy)
OnStartan utterance starts
OnEndan utterance ends
OnPausespeech is paused
OnResumespeech resumes
OnError{ error, message? }an error occurs
OnVoiceResolvedvoice-resolution detailsa voice is resolved for an utterance
OnServiceSwitched{ from, to }engine switches between native and fallback
OnPartStart{ partIndex, totalParts, text }a text chunk starts speaking
OnPartEnd{ partIndex, totalParts, text }a text chunk finishes
OnClickEventa user gesture (click) is detected
OnAllowSpeechClicked{ allowed }user responds to the permission prompt
rv.addEventListener('OnStart', () => console.log('Speech started'));
rv.addEventListener('OnPause', () => console.log('Speech paused'));
rv.addEventListener('OnResume', () => console.log('Speech resumed'));

Pause and resume are only global events — they are not per-call callbacks. See the RVEventType reference for the complete list of event names.

Pass the same handler reference you registered:

const handler = () => console.log('Speech started');
rv.addEventListener('OnStart', handler);
rv.removeEventListener('OnStart', handler);

The per-call onerror callback receives a standard Error. The OnError event delivers a payload { error, message? } whose error is that same Error:

rv.speak('Hello', 'UK English Female', {
onerror: (error) => {
console.error('Speech failed:', error.message);
},
});

Wrap speak() in a Promise using onend and onerror:

function speakAsync(text: string, voice: string): Promise<void> {
return new Promise((resolve, reject) => {
rv.speak(text, voice, {
onend: () => resolve(),
onerror: (error) => reject(error),
});
});
}
// Usage
async function readParagraphs(paragraphs: string[]) {
for (const paragraph of paragraphs) {
await speakAsync(paragraph, 'UK English Female');
}
console.log('All paragraphs read');
}

Use onboundary to update a progress bar as speech advances (native voices only):

let startTime: number;
rv.speak(longText, 'UK English Female', {
onstart: () => {
startTime = Date.now();
progressBar.style.width = '0%';
},
onboundary: (charIndex) => {
const progress = (charIndex / longText.length) * 100;
progressBar.style.width = `${progress}%`;
},
onend: () => {
progressBar.style.width = '100%';
console.log(`Completed in ${Date.now() - startTime}ms`);
},
});

Each speak() call carries its own callbacks:

rv.speak('First sentence', 'UK English Female', {
onend: () => console.log('First done, starting second'),
});
rv.speak('Second sentence', 'UK English Female', {
onstart: () => console.log('Second starting'),
onend: () => console.log('Queue complete'),
});