Voice Selection
The second argument to speak() is a voice selector — a string, RegExp, or structured query that tells ResponsiveVoice which voice to use:
rv.speak(text: string, voice?: VoiceSelectorInput, params?: SpeakOptions): void;VoiceSelector (the post-parse, on-the-wire form) is a union of three JSON-serializable shapes:
| Type | JS form | Wire form | Description |
|---|---|---|---|
string | 'UK English Female' | same | Exact voice name |
RegexSelector | /Portuguese/i | { regex: 'Portuguese', flags: 'i' } | First voice matching the pattern |
VoiceQuery | { lang: 'pt' } | same | Structured filter (AND logic) |
In JS code, pass a real RegExp literal and the schema normalizes it to the JSON-clean { regex, flags } form on parse — the resolver, server payloads, and SDKs all see the wire form, so the contract is identical across every language.
By Name
Section titled “By Name”The simplest form — pass a ResponsiveVoice voice name as a string:
rv.speak('Hello', 'UK English Female');rv.speak('Hello', 'US English Male');rv.speak('Bonjour', 'French Female');rv.speak('Hallo', 'Deutsch Male');If the exact name isn't available on the current platform, the voice matching chain kicks in to find the closest alternative.
By Pattern
Section titled “By Pattern”Pass a RegExp to match against all non-deprecated voice names. The first match wins:
rv.speak('Olá', /Portuguese/);rv.speak('Hello', /English.*Female/i);In server-side config or non-JS SDKs (Python, Go, PHP, Java), use the JSON literal form instead — it's the same selector after the schema normalizes:
{ "regex": "Portuguese" }{ "regex": "English.*Female", "flags": "i" }By Query
Section titled “By Query”Pass a VoiceQuery object to filter voices by attributes. All conditions are AND-ed — a voice must match every specified field:
| Field | Type | Behavior |
|---|---|---|
name | string | Case-insensitive; exact match first, then substring |
lang | string | BCP-47 prefix match ("pt" matches "pt-BR") |
gender | 'f' | 'm' | 'male' | 'female' | Gender filter |
isByok | boolean | Filter to BYOK (Bring Your Own Key) voices only |
provider | string | Provider name, case-insensitive |
// By languagerv.speak('Bonjour', { lang: 'fr' });
// By language + genderrv.speak('Olá', { lang: 'pt', gender: 'f' });
// By provider (BYOK voices)rv.speak('Hello', { provider: 'Google Cloud WaveNet', lang: 'en-GB', gender: 'm',});Direct Voice Override
Section titled “Direct Voice Override”When you have a raw SpeechSynthesisVoice object from the browser's Web Speech API, you can pass it directly via the params.voice option:
const nativeVoices = speechSynthesis.getVoices();const samantha = nativeVoices.find((v) => v.name === 'Samantha');
rv.speak('Hello', undefined, { voice: samantha });Default Voice
Section titled “Default Voice”When no voice selector is passed to speak(), the configured default voice is used. The built-in default is 'UK English Female'.
// Set at initconst rv = await getResponsiveVoice({ apiKey: 'YOUR_KEY', defaultVoice: 'US English Female',});
rv.speak('Uses US English Female');
// Change at runtimerv.setDefaultVoice('French Male');rv.speak('Uses French Male now');Language-Aware Website Widgets
Section titled “Language-Aware Website Widgets”ResponsiveVoice accepts a language query, but your interface decides when to change languages. A multilingual website widget should normally use this precedence:
- A voice explicitly chosen by the visitor
- Language detected from text the visitor typed
- The browser's preferred language
- English fallback using
UK English Female
function browserLanguage(): string { return (navigator.languages?.[0] || navigator.language || 'en') .toLowerCase() .split('-')[0];}
function selectorForLanguage(language: string) { const lang = language.toLowerCase().split('-')[0];
// Keep the English experience consistent and premium by default. if (lang === 'en') return 'UK English Female';
return { lang, gender: 'f' } as const;}
function speakVisitorText(text: string, selectedVoice?: string) { // Supply this with your preferred client-side or server-side detector. const typedLanguage = text.length >= 20 ? detectLanguage(text) : undefined; const language = typedLanguage || browserLanguage(); const voice = selectedVoice || selectorForLanguage(language);
rv.speak(text, voice);}Use browser language to initialize placeholder text and the first suggested voice. Re-run language detection after the visitor types enough text to make a useful decision, but do not override a manual voice selection.
Force Fallback
Section titled “Force Fallback”Set forceFallback to skip native browser voices entirely and always use server-side HTTP audio. This provides consistent voice quality across all browsers:
const rv = await getResponsiveVoice({ apiKey: 'YOUR_KEY', forceFallback: true,});
// Toggle at runtimerv.setForceFallback(false);Resolution Precedence
Section titled “Resolution Precedence”When speak() is called, voice selection resolves in this order (highest priority first):
params.voiceoverride — directSpeechSynthesisVoiceobject; bypasses everything belowresolveVoicehook — intercepts and transforms the selector before resolution- Explicit
VoiceSelector— thestring,RegExp(or its{ regex, flags? }wire form), orVoiceQuerypassed tospeak() defaultVoiceconfig — used when no selector is provided
Voice Matching Chain
Section titled “Voice Matching Chain”Once a voice name is determined, ResponsiveVoice walks the voice's internal chain of system voice IDs and tries these matching strategies in order across all chain entries (strategy-first):
- Exact match — direct name comparison against browser voices
- Whitespace normalized — handles Chrome's Unicode non-breaking spaces (U+00A0) in Asian voice names
- Parenthetical stripped — handles Apple Safari's "(Enhanced)" / "(Premium)" suffixes
- Partial match — case-insensitive substring match
- Language fallback — any browser voice matching the target language
- HTTP fallback — server-side TTS via the ResponsiveVoice API
Listing Voices
Section titled “Listing Voices”Use getVoices() to list all available voices on the current platform:
const voices = rv.getVoices();console.log(voices);// [// { name: 'UK English Female', lang: 'en-GB', gender: 'f' },// { name: 'UK English Male', lang: 'en-GB', gender: 'm' },// { name: 'US English Female', lang: 'en-US', gender: 'f' },// ...// ]Platform-Specific Voices
Section titled “Platform-Specific Voices”Some voices are only available on specific platforms:
| Platform | Notes |
|---|---|
| Chrome | Google voices, best selection |
| Safari | Apple voices, "(Enhanced)" variants |
| Firefox | Limited native voices, uses fallback |
| iOS | Version-specific voice sets |
| Android | Device-dependent voices |
Language Codes
Section titled “Language Codes”Voices use BCP-47 language codes:
| Code | Language |
|---|---|
en-GB | British English |
en-US | American English |
fr-FR | French |
de-DE | German |
es-ES | Spanish |
ja-JP | Japanese |
zh-CN | Chinese (Simplified) |
Example: Voice Selector UI
Section titled “Example: Voice Selector UI”Try all three selector forms side by side in the live Voice Selector demo — type a name, type a regex, or build a query and watch the snippet rewrite as you switch tabs. The accompanying docs page explains the design choices.
The minimal pattern below shows the simplest case (build a dropdown from getVoices()):
// Build a voice selector dropdownconst select = document.createElement('select');
rv.getVoices().forEach((voice) => { const option = document.createElement('option'); option.value = voice.name; option.textContent = `${voice.name} (${voice.lang})`; select.appendChild(option);});
select.addEventListener('change', () => { rv.speak('Sample text', select.value);});