<h1>SpeechSynthesis demonstration</h1>
<form id="voiceForm">
  <p>
    Text to speech: <input id="inputTxt" type="text">
  </p>
  <p>
    Voice: <select id="voiceSelect"></select>
  </p>
  <p>
    Pitch: <input id="pitch" type="number" min="0" max="2" step="any" value="1"> (default 1)
  </p>
  <p>
    Speed (rate): <input id="rate" type="number" min="0.1" max="10" step="any" value="1"> (default 1)
  </p>
  <p>
    Volume: <input id="volume" type="number" min="0" max="1" step="any" value="1"> (default 1)
  </p>
  <button id="playBtn" type="submit">
    Play
  </button>
  <button id="pauseBtn" type="button">
    Pause
  </button>
  <button id="resumeBtn" type="button">
    Resume
  </button>
  <button id="cancelBtn" type="button">
    Cancel
  </button>
</form>

<hr>
<h3>Debug</h3>
<div id="synth-events">
  <h4>Synth events:</h4>
</div>
<div id="utterance-events">
  <h4>Utterance events:</h4>
</div>

button {
    border: 1px solid #bbb;
    border-radius: 5px;
    padding: 8px 6px;
}
input, select {
    border: 1px solid #bbb;
    border-radius: 5px;
    padding: 4px 3px;
}

#playBtn {
    background-color: blue;
    border: 1px solid blue;
    color: #fff;
}

#synth-events,
#utterance-events {
    border: 1px dashed #ccc;
    padding: 5px;
    margin: 0 0 10px 0;
}
#synth-events h4,
#utterance-events h4 {
    margin: 0 0 10px 0;
}
const synth = window.speechSynthesis;
let voices;


const voiceSelect = document.getElementById('voiceSelect');
function loadVoices() {
  voices = synth.getVoices();
  for (let i = 0; i < voices.length; i++) {
    const option = document.createElement("option");
    option.textContent = `${voices[i].name} (${voices[i].lang})`;
    option.value = i;
    voiceSelect.appendChild(option);
  }// endfor;
}// loadVoices


// listen form submit. ====================
const voiceForm = document.getElementById('voiceForm');
const inputTxt = document.getElementById('inputTxt');
const utteranceEventsPh = document.getElementById('utterance-events');
voiceForm?.addEventListener('submit', (event) => {
  event.preventDefault();
  const utterance = new SpeechSynthesisUtterance(inputTxt?.value);
  utterance.voice = voices[voiceSelect.value];
  utterance.lang = voices[voiceSelect.value].lang;
  utterance.pitch = document.getElementById('pitch')?.value;
  utterance.rate = document.getElementById('rate')?.value;
  utterance.volume = document.getElementById('volume')?.value;
  synth.speak(utterance);
  inputTxt?.blur();

  // utterance events. ----------------
  utterance.addEventListener("boundary", (event) => {
    console.log(
      `${event.name} (${utterance.text[event.charIndex]}) boundary reached after ${event.elapsedTime} seconds.`,
    );
    utteranceEventsPh.insertAdjacentHTML('beforeend', `${event.name} (${utterance.text[event.charIndex]}) boundary reached after ${event.elapsedTime} seconds.<br>`);
  });
  utterance.addEventListener("end", (event) => {
    console.log(
      `Utterance has finished being spoken after ${event.elapsedTime} seconds.`,
    );
    utteranceEventsPh.insertAdjacentHTML('beforeend', `Utterance has finished being spoken after ${event.elapsedTime} seconds.<br><br>`);
  });
  utterance.addEventListener("error", (event) => {
    console.log(
      `An error has occurred with the speech synthesis: ${event.error}`,
    );
    utteranceEventsPh.insertAdjacentHTML('beforeend', `<span style="color: red;">An error has occurred with the speech synthesis: ${event.error}</span><br><br>`);
  });
  utterance.addEventListener("mark", (event) => {
    console.log(`A mark was reached: ${event.name}`);
    utteranceEventsPh.insertAdjacentHTML('beforeend', `A mark was reached: ${event.name}<br>`);
  });
  utterance.addEventListener("pause", (event) => {
    console.log(`Speech paused after ${event.elapsedTime} seconds.`);
    utteranceEventsPh.insertAdjacentHTML('beforeend', `Speech paused after ${event.elapsedTime} seconds.<br>`);
  });
  utterance.addEventListener("resume", (event) => {
    console.log(`Speech resumed after ${event.elapsedTime} seconds.`);
    utteranceEventsPh.insertAdjacentHTML('beforeend', `Speech resumed after ${event.elapsedTime} seconds.<br>`);
  });
  utterance.addEventListener("start", (event) => {
    console.log(`We have started uttering this speech: ${event.utterance.text}`);
    utteranceEventsPh.insertAdjacentHTML('beforeend', `We have started uttering this speech: ${event.utterance.text}<br>`);
  });
  // end utterance events. ------------
});
// end listen form submit. ================

// control buttons. ======================
const pauseBtn = document.getElementById('pauseBtn');
if (pauseBtn) {
  pauseBtn.addEventListener('click', (event) => {
    event.preventDefault();
    synth.pause();
  });
}

const resumeBtn = document.getElementById('resumeBtn');
if (resumeBtn) {
  resumeBtn.addEventListener('click', (event) => {
    event.preventDefault();
    synth.resume();
  });
}

const cancelBtn = document.getElementById('cancelBtn');
if (cancelBtn) {
  cancelBtn.addEventListener('click', (event) => {
    event.preventDefault();
    synth.cancel();
  });
}
// end control buttons. ==============

// synth event(s). ===================
synth.addEventListener('voiceschanged', (event) => {
  console.log('synth voice changed.');
  const synthEventsPh = document.getElementById('synth-events');
  synthEventsPh.insertAdjacentHTML('beforeend', '<p>voices changed.</p>');
});
// end synth event(s). ===============


loadVoices();
if (
  typeof speechSynthesis !== "undefined" &&
  speechSynthesis.onvoiceschanged !== undefined
) {
  speechSynthesis.onvoiceschanged = loadVoices;
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.