> ## Documentation Index
> Fetch the complete documentation index at: https://docs.responsivevoice.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Voice Selection

The second argument to `speak()` is a **voice selector** — a `string`, `RegExp`, or structured query that tells ResponsiveVoice which voice to use:

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

The simplest form — pass a ResponsiveVoice voice name as a string:

```typescript
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](#voice-matching-chain) kicks in to find the closest alternative.

## By Pattern

Pass a `RegExp` to match against all non-deprecated voice names. The first match wins:

```typescript
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:

```json
{ "regex": "Portuguese" }
{ "regex": "English.*Female", "flags": "i" }
```

> [!TIP]
> Pattern matching is useful when you want any voice from a family (e.g., any Portuguese variant) rather than a specific voice name.

## 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                     |

```typescript
// By language
rv.speak('Bonjour', { lang: 'fr' });

// By language + gender
rv.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

When you have a raw `SpeechSynthesisVoice` object from the browser's Web Speech API, you can pass it directly via the `params.voice` option:

```typescript
const nativeVoices = speechSynthesis.getVoices();
const samantha = nativeVoices.find((v) => v.name === 'Samantha');

rv.speak('Hello', undefined, { voice: samantha });
```

> [!WARNING]
> This bypasses the entire ResponsiveVoice resolution chain, including the [`resolveVoice` hook](/guides/voice-resolver/). The voice must be a valid `SpeechSynthesisVoice` from the current browser.

## Default Voice

When no voice selector is passed to `speak()`, the configured default voice is used. The built-in default is `'UK English Female'`.

```typescript
// Set at init
const rv = await getResponsiveVoice({
  apiKey: 'YOUR_KEY',
  defaultVoice: 'US English Female',
});

rv.speak('Uses US English Female');

// Change at runtime
rv.setDefaultVoice('French Male');
rv.speak('Uses French Male now');
```

## 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:

1. A voice explicitly chosen by the visitor
2. Language detected from text the visitor typed
3. The browser's preferred language
4. English fallback using `UK English Female`

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

> [!NOTE]
> The SDK does not guess the language of arbitrary text. Keep detection in the
> application so you can choose the detector, confidence threshold, privacy
> behavior, and fallback policy that fit your site.

## 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:

```typescript
const rv = await getResponsiveVoice({
  apiKey: 'YOUR_KEY',
  forceFallback: true,
});

// Toggle at runtime
rv.setForceFallback(false);
```

## Resolution Precedence

When `speak()` is called, voice selection resolves in this order (highest priority first):

1. **`params.voice` override** — direct `SpeechSynthesisVoice` object; bypasses everything below
2. **[`resolveVoice` hook](/guides/voice-resolver/)** — intercepts and transforms the selector before resolution
3. **Explicit `VoiceSelector`** — the `string`, `RegExp` (or its `{ regex, flags? }` wire form), or `VoiceQuery` passed to `speak()`
4. **`defaultVoice` config** — used when no selector is provided

## 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):

1. **Exact match** — direct name comparison against browser voices
2. **Whitespace normalized** — handles Chrome's Unicode non-breaking spaces (U+00A0) in Asian voice names
3. **Parenthetical stripped** — handles Apple Safari's "(Enhanced)" / "(Premium)" suffixes
4. **Partial match** — case-insensitive substring match
5. **Language fallback** — any browser voice matching the target language
6. **HTTP fallback** — server-side TTS via the ResponsiveVoice API

> [!TIP]
> Strategy-first iteration means an exact match later in the chain always beats a partial match earlier in the chain. This ensures the most specific match wins.

## Listing Voices

Use `getVoices()` to list all available voices on the current platform:

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

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

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

Try all three selector forms side by side in the [**live Voice Selector demo**](https://examples.responsivevoice.org/browser/voice-selector/) — type a name, type a regex, or build a query and watch the snippet rewrite as you switch tabs. The accompanying [docs page](/examples/voice-selector) explains the design choices.

The minimal pattern below shows the simplest case (build a dropdown from `getVoices()`):

```typescript
// Build a voice selector dropdown
const 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);
});
```
