Text to speech in the browser with the Web Speech API
Tags: javascript, webdev, texttospeech
Published: 2019-08-26 05:55:10

---
title: Text to speech in the browser with the Web Speech API
published: true
description: Learn how to turn text to speech in the browser with the Web Speech API.
tags: javascript, webdev, text-to-speech
cover_image: https://thepracticaldev.s3.amazonaws.com/i/oiahrlqypkg90hwhthij.png
canonical_url: https://www.twilio.com/blog/speech-to-text-browser-web-speech-api?
---

The [Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API) has two functions, [speech synthesis](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis), otherwise known as text to speech, and [speech recognition](https://developer.mozilla.org/en-US/docs/Web/API/SpeechRecognition). With the [`SpeechSynthesis API`](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis) we can command the browser to read out any text in a number of different voices.

From a vocal alerts in an application to bringing an [Autopilot powered](https://www.twilio.com/docs/autopilot) chatbot to life on your website, the Web Speech API has a lot of potential for web interfaces. Follow on to find out how to get your web application speaking back to you.

## What you'll need

If you want to build this application as we learn about the `SpeechSynthesis` API then you'll need a couple of things:

*   A modern browser ([the API is supported across the majority of desktop and mobile browsers](https://caniuse.com/#feat=speech-synthesis))
*   A text editor

Once you're ready, create a directory to work in and download [this HTML file](https://github.com/philnash/web-assistant/blob/master/speech-synthesis/browser-voices-starter.html) and [this CSS file](https://github.com/philnash/web-assistant/blob/master/speech-synthesis/browser-voices-style.css) to it. Make sure they are in the same folder and the CSS file is named `style.css`. Open the HTML file in your browser and you should see this:

![](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/4LcA2N5fPD153TZqvW6QAOfBp2L54sS7O7_JEUHkOn8MvY.width-500.png)

Let's get started with the API by getting the browser to talk to us for the first time.

## The Speech Synthesis API

Before we start work with this small application, we can get the browser to start speaking using the browser's developer tools. On any web page, open up the developer tools console and enter the following code:


```javascript
speechSynthesis.speak(new SpeechSynthesisUtterance("Hello, this is your browser speaking."));
```

![](https://s3.amazonaws.com/com.twilio.prod.twilio-docs/images/kdOBGAsfWuOC7SgTfHj12NKzm8Qg7JHgdDvYPqt91VP-ek.width-500.png)

Your browser will speak the text "_Hello, this is your browser speaking._" in its default voice. We can break this down a bit though.

We created a `SpeechSynthesisUtterance` which contained the text we wanted to be spoken. Then we passed the utterance to the `speak` method of the `speechSynthesis` object. This queues up the utterance to be spoken and then starts the browser speaking. If you send more than one utterance to the `speak` method they will be spoken one after another.

Let's take the starter code we downloaded earlier and turn this into a small app where we can input the text to be spoken and choose the voice that the browser says it in.

## Speech Synthesis in a web application

Open up the HTML file you downloaded earlier in your text editor. We'll start by connecting the form up to speak whatever you enter in the text input when you submit. Later, we'll add the ability to choose the voice to use.

Between the `<script>` tags at the bottom of the HTML we'll start by listening for the `DOMContentLoaded` event and then selecting some references to the elements we'll need.


```javascript
<script>
  window.addEventListener('DOMContentLoaded', () => {
    const form = document.getElementById('voice-form');
    const input = document.getElementById('speech');
  });
</script>
```

We then need to listen to the submit event on the form and when it fires, grab the text from the input. With that text we'll create a `SpeechSynthesisUtterance` and then pass it to `speechSynthesis.speak`. Finally, we empty the input box and wait for the next thing to say.

```javascript
<script>
  window.addEventListener('DOMContentLoaded', () => {
    const form = document.getElementById('voice-form');
    const input = document.getElementById('speech');

    form.addEventListener('submit', event => {
      event.preventDefault();
      const toSay = input.value.trim();
      const utterance = new SpeechSynthesisUtterance(toSay);
      speechSynthesis.speak(utterance);
      input.value = '';
    });
  });
</script>
```

Open the HTML in your browser and enter some text in the input. You can ignore the `<select>` box at this point, we'll use that in the next section. Hit "_Say it_" and listen to the browser read out your words.

It's not much code to get the browser to say something, but what if we want to pick the voice that it uses. Let's populate the dropdown on the page with the available voices and use it to select the one we want to use.

## Picking voices for text to speech

We need to get references to the `<select>` element on the page and initialise a couple of variables we'll use to store the available voices and the current voice we are using. Add this to the top of the script:

```javascript
<script>
  window.addEventListener('DOMContentLoaded', () => {
    const form = document.getElementById('voice-form');
    const input = document.getElementById('speech');
    const voiceSelect = document.getElementById('voices');
    let voices;
    let currentVoice;

    form.addEventListener('submit', event => { //... })
  });
</script>
```

Next up we need to populate the select element with the available voices. We'll create a new function to do this, as we might want to call it more than once (more on that in a bit). We can call on `speechSynthesis.getVoices()` to return the available `[SpeechSynthesisVoice](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisVoice)` [objects](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisVoice).

Whilst we are populating the voice options we should also detect the currently selected voice. If we have already chosen a voice we can check against our `currentVoice` object and if we haven't yet chosen a voice then we can detect the default voice with the `voice.default` property.

```javascript
    let voices;
    let currentVoice;

    const populateVoices = () => {
      const availableVoices = speechSynthesis.getVoices();
      voiceSelect.innerHTML = '';

      availableVoices.forEach(voice => {
        const option = document.createElement('option');
        let optionText = `${voice.name} (${voice.lang})`;
        if (voice.default) {
          optionText += ' [default]';
          if (typeof currentVoice === 'undefined') {
            currentVoice = voice;
            option.selected = true;
          }
        }
        if (currentVoice === voice) {
          option.selected = true;
        }
        option.textContent = optionText;
        voiceSelect.appendChild(option);
      });
      voices = availableVoices;
    };

    form.addEventListener('submit', event => { //... })
```

We can call `populateVoice` straight away. Some browsers will load the voices page load and will return their list straight away. Other browsers need to load their list of voices asynchronously and will emit a "voiceschanged" event once they have loaded. Some browsers do not emit this event at all though.

To account for all the potential scenarios we'll call `populateVoices` immediately and also set it as the callback to the "voiceschanged" event.

```javascript
      voices = availableVoices;
    };

    populateVoices();
    speechSynthesis.onvoiceschanged = populateVoices;

    form.addEventListener('submit', event => { //... })
  });
</script>
```

Reload the page and you will see the `<select>` element populated with all the available voices, including the language the voice supports. We haven't hooked up selecting and using the voice yet though, that comes next.

Listen to the "change" event of the select element and whenever it is fired, select the `currentVoice` using the `selectedIndex` of the `<select>` element.

```javascript
    populateVoices();
    speechSynthesis.onvoiceschanged = populateVoices;

    voiceSelect.addEventListener('change', event => {
      const selectedIndex = event.target.selectedIndex;
      currentVoice = voices[selectedIndex];
    });

    form.addEventListener('submit', event => { //... })
  });
```

Now, to use the voice with the speech utterance we need to set the voice on the utterance that we create.

```javascript
    form.addEventListener('submit', event => {
      event.preventDefault();
      const toSay = input.value.trim();
      const utterance = new SpeechSynthesisUtterance(toSay);
      utterance.voice = currentVoice;
      speechSynthesis.speak(utterance);
      input.value = '';
    });
  });
</script>
```

Reload the page and play around selecting different voices and saying different things.

## Bonus: build a visual speaking indicator

We've built a speech synthesiser that can use different voices, but I wanted to throw one more thing in for fun. [Speech utterances emit a number of events](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance#Events) that you can use to make your application respond to speech. To finish this little app off we're going to make an animation show as the browser is speaking. I've already added [the CSS for the animation](https://github.com/philnash/web-assistant/blob/master/speech-synthesis/browser-voices-style.css#L35-L50) so to activate it we need to add a "speaking" class to the `<main>` element while the browser is speaking.

Grab a reference to the `<main>` element at the top of the script:

```javascript
<script>
  window.addEventListener('DOMContentLoaded', () => {
    const form = document.getElementById('voice-form');
    const input = document.getElementById('speech');
    const voiceSelect = document.getElementById('voices');
    let voices;
    let currentVoice;
    const main = document.getElementsByTagName('main')[0];
```

Now, we can listen to the start and end events of the utterance to add and remove the "speaking" class. But, if we remove the class in the middle of the animation it won't fade out smoothly, so we should listen for the end of the animation's iteration, using the ["animationiteration" event](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/animationiteration_event), and then remove the class.

```javascript
    form.addEventListener('submit', event => {
      event.preventDefault();
      const toSay = input.value.trim();
      const utterance = new SpeechSynthesisUtterance(toSay);
      utterance.voice = currentVoice;
      utterance.addEventListener('start', () => {
        main.classList.add('speaking');
      });
      utterance.addEventListener('end', () => {
        main.addEventListener(
          'animationiteration',
          () => main.classList.remove('speaking'),
          { once: true }
        );
      });
      speechSynthesis.speak(utterance);
      input.value = '';
    });
  });
</script>
```

Now when you start the browser talking the background will pulse blue and when the utterance is over it will stop.

## Your browser is getting chatty

In this post you've seen how to get started and work with the [Speech Synthesis API](https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesis) from the [Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API). All [the code for this application can be found on GitHub](https://github.com/philnash/web-assistant/tree/master/speech-synthesis) and you can [see it in action or remix it on Glitch](https://glitch.com/~browser-voices).

I'm excited about the potential of this API for building my own in browser bots, so look out for more of this in the future.

Have you used the Speech Synthesis API or have any plans for it? I'd love to hear in the comments below, or drop me a note at [philnash@twilio.com](mailto:philnash@twilio.com) or on Twitter at [@philnash](https://twitter.com/philnash).