Browser Voice Synthesis Using Web Speech API
The Web Speech API provides built-in browser support for text-to-speech functionality via its Speech Synthesis interface. This allows developers to convert written text into spoken audio directly in client-side JavaScript without requiring external libraries or server-side audio generation. This article explains the core architecture of the Speech Synthesis API, demonstrates how to configure and execute voice commands, and outlines best practices for handling voices and lifecycle events.
Core Interfaces
Voice synthesis in the Web Speech API relies primarily on two main objects:
SpeechSynthesis: The controller interface accessed viawindow.speechSynthesis. It manages playback state (speaking, paused, pending), retrieves available system voices, and queues utterances to be spoken.SpeechSynthesisUtterance: The request object representing the speech unit. It contains the text content to be read and configurable properties such as pitch, rate, volume, language, and selected voice.
Basic Implementation
To perform speech synthesis, create an instance of
SpeechSynthesisUtterance with your text, configure its
properties if necessary, and pass it to
window.speechSynthesis.speak().
// Check for browser support
if ('speechSynthesis' in window) {
// 1. Create a speech utterance
const utterance = new SpeechSynthesisUtterance("Hello! This is browser-based speech synthesis.");
// 2. Configure basic properties
utterance.pitch = 1.0; // Range: 0 to 2
utterance.rate = 1.0; // Range: 0.1 to 10
utterance.volume = 1.0; // Range: 0 to 1
// 3. Queue the utterance for playback
window.speechSynthesis.speak(utterance);
} else {
console.warn("Speech Synthesis is not supported in this browser.");
}Selecting and Loading Voices
The browser provides a list of installed system and browser voices
through the speechSynthesis.getVoices() method. Because
voices are loaded asynchronously, you must listen for the
voiceschanged event to ensure the voice list is populated
before selection.
function speakWithCustomVoice(text) {
const utterance = new SpeechSynthesisUtterance(text);
const voices = window.speechSynthesis.getVoices();
// Find a specific English voice or fallback to default
const selectedVoice = voices.find(voice => voice.lang === 'en-US' && voice.name.includes('Google')) || voices[0];
if (selectedVoice) {
utterance.voice = selectedVoice;
utterance.lang = selectedVoice.lang;
}
window.speechSynthesis.speak(utterance);
}
// Ensure voices are loaded
if (window.speechSynthesis.onvoiceschanged !== undefined) {
window.speechSynthesis.onvoiceschanged = () => {
const availableVoices = window.speechSynthesis.getVoices();
console.log(`Loaded ${availableVoices.length} voices.`);
};
}Managing Playback and Listening to Events
The SpeechSynthesisUtterance object emits events during
its lifecycle, allowing you to track playback status and update user
interface elements accordingly.
onstart: Fires when speech playback begins.onend: Fires when speech playback completes.onerror: Fires when an error prevents speech completion.onpause/onresume: Fires when playback state changes.onboundary: Fires when the engine reaches a word or sentence boundary.
const utterance = new SpeechSynthesisUtterance("Monitoring speech events.");
utterance.onstart = () => console.log("Playback started.");
utterance.onend = () => console.log("Playback finished.");
utterance.onerror = (event) => console.error("Synthesis error:", event.error);
window.speechSynthesis.speak(utterance);To control playback globally across the browser session, use the
controller methods on window.speechSynthesis:
window.speechSynthesis.pause(): Pauses active speech.window.speechSynthesis.resume(): Resumes paused speech.window.speechSynthesis.cancel(): Stops playback immediately and clears the utterance queue.
Key Considerations
- User Interaction Requirements: Many browsers restrict automated audio playback. Ensure speech synthesis is triggered by direct user actions, such as button clicks, to prevent autoplay policies from blocking execution.
- Garbage Collection Bug: In some Chromium-based
browsers, long utterances may stop unexpectedly if the
SpeechSynthesisUtteranceinstance is garbage-collected mid-speech. To prevent this, keep a global reference to active utterance objects until theonendevent fires.