Skip to content

fix: voice selection on iOS/Mac (Web Speech API)#43

Open
klokie wants to merge 1 commit into
joethei:masterfrom
klokie:fix/ios-voice-selection
Open

fix: voice selection on iOS/Mac (Web Speech API)#43
klokie wants to merge 1 commit into
joethei:masterfrom
klokie:fix/ios-voice-selection

Conversation

@klokie
Copy link
Copy Markdown

@klokie klokie commented Apr 12, 2026

Fixes #42, fixes #20.

Problem

Two bugs prevent the selected voice from being used on iOS and macOS:

Bug 1: Voice matched by name but stored as voiceURI

getVoices() stores voice identity as voiceURI, which becomes the saved setting value. But sayWithVoice looked up the voice by name:

// before
msg.voice = window.speechSynthesis.getVoices()
    .filter(otherVoice => otherVoice.name === voice)[0];

On desktop (Chrome/Firefox), voiceURI and name are often identical so this accidentally worked. On iOS/macOS they differ — Siri and enhanced voices have URIs like com.apple.voice.compact.en-US.Samantha vs display name "Samantha (Enhanced)" — so the filter returns undefined and playback falls back to the system default every time.

Bug 2: Synchronous getVoices() on iOS/Safari

window.speechSynthesis.getVoices() returns an empty array synchronously on iOS/Safari. Voices load asynchronously and are only available after the voiceschanged event fires. Both getVoices() and sayWithVoice called it synchronously, so on iOS the settings dropdown could appear empty and msg.voice was always undefined.

Fix

  • Match voices by voiceURI instead of name in sayWithVoice
  • Add a loadVoices() helper that returns voices immediately if already available, otherwise awaits voiceschanged
  • Use loadVoices() in both getVoices() and sayWithVoice

Two bugs prevented the selected voice from being used on iOS and macOS:

1. sayWithVoice matched voices by `name` but the stored setting value is
   `voiceURI`. On iOS/macOS, these differ (e.g. Siri voices have a URI like
   `com.apple.voice.compact.en-US.Samantha` vs display name "Samantha").
   Fixed by matching on `voiceURI` instead.

2. `getVoices()` was called synchronously. On iOS/Safari, the Web Speech API
   loads voices asynchronously and `getVoices()` returns an empty array until
   the `voiceschanged` event fires. Added a `loadVoices()` helper that returns
   immediately if voices are already available, otherwise waits for the event.

Fixes joethei#42, fixes joethei#20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

can't get it to use the chosen voice (iOS, Mac) Siri voice does not work

1 participant