2

In Chrome, the voiceschanged is firing on page load, so I don't need to call the function that has speechSynthesis.getVoices() initially in order for my empty array to be filled with voices as long as I have an event listener that calls it when voiceschanged is fired.

// Store voices
let voices = [];

function getVoices() {
  voices = speechSynthesis.getVoices();

  // Create an option for each voice in array
  voices.forEach((voice) => {
    const option = document.createElement('option');

    option.value = voice.name;
    option.innerText = `${voice.name} ${voice.lang}`;

    voicesSelect.appendChild(option);
  });
}

// Voices changed
speechSynthesis.addEventListener('voiceschanged', getVoices);

// Not needed (in Chrome at least) because voiceschanged event fires on page load, calling getVoices due to event listener (trying to figure out why)
// getVoices();

I'm just trying to understand this behavior - MDN's explanation of when voiceschanged fires doesn't explain it as far as I can tell:

The voiceschanged event of the Web Speech API is fired when the list of SpeechSynthesisVoice objects that would be returned by the SpeechSynthesis.getVoices() method has changed (when the voiceschanged event fires.)

nCardot
  • 5,992
  • 6
  • 47
  • 83
  • Does this behavior also occur in Firefox/Edge, or is this unique to Chrome? This is an experimental API, so I wouldn't expect it to behave consistently between browsers, or in a way that makes sense at this moment of time. –  Jan 12 '21 at 16:34
  • The same behavior occurs in Edge. In Firefox apparently the event is fired but I only get two of the voices loaded. – nCardot Jan 12 '21 at 16:39
  • Side note - even with getVoices(), only two of the voices are added to the array in Firefox. (I'm using VS Code with Live Server, I wonder if that has anything to do with it.) – nCardot Jan 12 '21 at 16:44

1 Answers1

4

The event fires, because the list of voices changes when Chrome finishes making an API call to get the list of voices available only to Chrome users. Proof:

  • If I load my Speech Synthesis API-based web app, with Internet connection, I have 21 available voices, a few months ago, I only remember 10 or 15 or so.
  • If I do the same, without Internet connection, I only have two voices: Microsoft David Desktop and Microsoft Zira Desktop.

You probably notice that the two voices without Internet connection are rather boring and almost recognizable for being used in cheap audio production. But the Google Chrome ones are fluid and almost inflective. This event has to fire when the voices are loaded, of course. Take a quick glance at the W3C Errata in the Web Speech API Specification. Any time the voices are loaded, the voiceschanged event is fired....

voiceschanged: Fired when the contents of the SpeechSynthesisVoiceList, that the getVoices method will return, have changed. Examples include: server-side synthesis where the list is determined asynchronously, or when client-side voices are installed/uninstalled.

And, in fact, look at the last line of the MDN web docs you linked...

With Chrome however, you have to wait for the event to fire before populating the list, hence the bottom if statement seen below.

Speech Synthesis API-Based Source Code (from my open-source project PronounceThat)

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
  • Microsoft David Desktop and Microsoft Zira Desktop - those are the only ones I got when I tried loading in Firefox by the way. – nCardot Jan 12 '21 at 17:20