Impara

Sintesi vocale in JavaScript con SpeechSynthesis

Rene, Kabir Goel 
Sintesi vocale in JavaScript con SpeechSynthesis

Per trasformare testo in parlato in JavaScript, crea una SpeechSynthesisUtterance e passala a window.speechSynthesis.speak(). Il browser gestisce la riproduzione. Non servono una chiave API, un server o un pacchetto JavaScript.

Funziona bene per un prototipo o un controllo di lettura ad alta voce in cui le voci del dispositivo sono accettabili. Se sviluppi un agente vocale che richiede la stessa voce su dispositivi diversi o audio in streaming verso una telefonata, usa invece un’API di sintesi vocale ospitata. SpeechSynthesis del browser e l’API di Cartesia sono modi distinti di generare parlato.

Come funziona la sintesi vocale del browser

window.speechSynthesis è il controller della Web Speech API per il parlato sintetizzato. Un’utterance contiene testo e impostazioni di una richiesta. Chiamare speak() aggiunge l’utterance a una coda; non restituisce un file audio.1

Il browser espone le voci disponibili sul dispositivo tramite getVoices(). L’elenco può caricarsi dopo l’esecuzione dello script, quindi leggilo subito e aggiornalo quando si attiva voiceschanged.2 Non inserire nel codice il nome di una voce del tuo portatile aspettandoti di trovarla anche sul telefono di un cliente.

1. Salva una demo di sintesi vocale funzionante

Salva il codice seguente come speech.html e aprilo in un browser. Inserisci una frase breve, scegli una voce se il browser ne elenca una e premi Speak. Usa testo di esempio non sensibile: alcune voci impiegano servizi vocali remoti.3

<!doctype html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1" />
		<title>JavaScript text-to-speech demo</title>
	</head>
	<body>
		<h1>Read text aloud</h1>
		<p>
			<label for="text">Text to speak</label><br />
			<textarea id="text" rows="4" cols="30" maxlength="500">
Hello from your browser.</textarea
			>
		</p>
		<p>
			<label for="voice">Voice</label>
			<select id="voice"></select>
		</p>
		<button id="speak" type="button">Speak</button>
		<button id="stop" type="button">Stop</button>
		<p id="status" role="status" aria-live="polite">Ready.</p>

		<script>
			const text = document.querySelector("#text");
			const voiceSelect = document.querySelector("#voice");
			const speakButton = document.querySelector("#speak");
			const stopButton = document.querySelector("#stop");
			const status = document.querySelector("#status");
			let voices = [];
			let currentUtterance = null;

			if (
				!("speechSynthesis" in window) ||
				!("SpeechSynthesisUtterance" in window)
			) {
				status.textContent = "This browser does not support speech synthesis.";
				speakButton.disabled = true;
				stopButton.disabled = true;
			} else {
				const synth = window.speechSynthesis;

				function loadVoices() {
					const previousVoice = voiceSelect.value;
					voices = synth.getVoices();
					voiceSelect.replaceChildren(new Option("Browser default", ""));
					for (const voice of voices) {
						voiceSelect.add(
							new Option(`${voice.name} (${voice.lang})`, voice.voiceURI),
						);
					}
					if (voices.some((voice) => voice.voiceURI === previousVoice)) {
						voiceSelect.value = previousVoice;
					}
				}

				loadVoices();
				synth.addEventListener("voiceschanged", loadVoices);

				speakButton.addEventListener("click", () => {
					const transcript = text.value.trim();
					if (!transcript) {
						status.textContent = "Enter some text first.";
						return;
					}

					// Ignore callbacks from the utterance being replaced.
					currentUtterance = null;
					synth.cancel();
					const utterance = new SpeechSynthesisUtterance(transcript);
					const voice = voices.find(
						(item) => item.voiceURI === voiceSelect.value,
					);
					if (voice) {
						utterance.voice = voice;
						utterance.lang = voice.lang;
					} else {
						utterance.lang = document.documentElement.lang;
					}
					utterance.rate = 1;
					currentUtterance = utterance;
					status.textContent = "Starting speech...";
					utterance.onstart = () => {
						if (currentUtterance === utterance)
							status.textContent = "Speaking...";
					};
					utterance.onend = () => {
						if (currentUtterance !== utterance) return;
						currentUtterance = null;
						status.textContent = "Finished.";
					};
					utterance.onerror = (event) => {
						if (currentUtterance !== utterance) return;
						currentUtterance = null;
						status.textContent = `Speech failed: ${event.error}`;
					};
					synth.speak(utterance);
				});

				stopButton.addEventListener("click", () => {
					currentUtterance = null;
					synth.cancel();
					status.textContent = "Stopped.";
				});
			}
		</script>
	</body>
</html>

La demo limita l’input a 500 caratteri per mantenere breve il primo test. È un limite della demo, non della Web Speech API. Prova passaggi più lunghi separatamente su ogni browser supportato.

2. Verifica selezione della voce e riproduzione

Il selettore mostra nome e lingua di ogni voce disponibile. Scegli una voce corrispondente alla lingua del testo: impostare utterance.lang non traduce le parole.

Speak viene eseguito al clic sul pulsante, non al caricamento della pagina. Questo dà il controllo all’ascoltatore ed evita di affidarsi alla riproduzione automatica. Premere di nuovo Speak annulla la richiesta corrente prima di avviarne un’altra, anziché accodare clic ripetuti. Stop chiama cancel(), che rimuove le utterance in coda e interrompe il parlato corrente.4

Il controllo currentUtterance impedisce a un callback tardivo di una richiesta annullata di sovrascrivere lo stato di una più recente. Se adatti il codice in un componente, rimuovi il listener voiceschanged quando il componente viene smontato. Annulla la riproduzione allo smontaggio solo se quel componente controlla la coda vocale della pagina.

3. Risolvi parlato assente o incoerente

SintomoCosa verificare
Il selettore mostra solo Browser defaultAttendi voiceschanged. Se l’elenco resta vuoto, controlla supporto del browser e voci installate. La demo può comunque richiedere una voce predefinita.
Nessun suono dopo aver premuto SpeakControlla volume e uscita audio, prova una frase breve nella lingua corrispondente e leggi il messaggio di stato per eventuali errori di sintesi.
Una voce esiste su desktop ma non su mobileGli elenchi dipendono dal dispositivo. Mantieni un’opzione predefinita e prova sui sistemi operativi effettivamente supportati.
Clic ripetuti causano parlato ritardatospeak() accoda le richieste. Annulla prima quella vecchia se il nuovo testo deve sostituirla.
Il parlato non funziona senza internetLa voce scelta può usare un servizio remoto. Esamina voice.localService e prova offline anziché presumere una sintesi locale.

Per una funzione di lettura ad alta voce, lascia visibile il testo originale e rendi Stop accessibile da tastiera. Non avviare automaticamente il parlato e non trattarlo come un sostituto del supporto agli screen reader.

Quando usare un’API TTS ospitata

Scegli il parlato del browser quando bastano le voci disponibili all’ascoltatore e serve solo riproduzione sul suo dispositivo. Scegli un’API ospitata quando l’applicazione richiede byte audio generati, un’identità vocale gestita dal fornitore o audio inviato fuori dal browser.

RequisitoSpeechSynthesis del browserAPI TTS ospitata come Cartesia
CredenzialiNessuna chiave API applicativaCredenziali API e account con accesso all’utilizzo
Selezione della voceElenco del browser e del sistema operativoCatalogo del fornitore o voce personalizzata autorizzata
Destinazione audioRiproduzione nel browserL’applicazione riceve audio da riprodurre, conservare o trasportare
Comportamento offlineDipende da voce e dispositivoLe richieste API di Cartesia richiedono una connessione di rete
Gestione del testoLocale o remota, secondo la voceIl testo viene inviato al fornitore; verifica i requisiti sui dati prima dell’integrazione

Per la sintesi vocale di Cartesia, chiama l’API dal backend. Conserva lì la chiave API, non in un file HTML pubblico o in un bundle client. Un’applicazione browser può inviare testo al backend autenticato, che applica limiti di input e restituisce audio al client. Limita la frequenza delle richieste a quell’endpoint per evitare che i visitatori consumino crediti API senza limiti.

Inizia generando un file WAV con l’SDK Python di Cartesia. La guida verifica il percorso di generazione ospitata; non è un lettore browser in streaming. Per un’applicazione conversazionale servono anche buffering di riproduzione e gestione delle interruzioni. Confronta questo lavoro con Cartesia Managed Agents prima di costruire l’intera pipeline dell’agente vocale.

Riferimenti correlati

Note a piè di pagina

  1. MDN, SpeechSynthesis. ↩

  2. MDN, SpeechSynthesis.getVoices(). ↩

  3. MDN, SpeechSynthesisVoice.localService. ↩

  4. MDN, SpeechSynthesis.cancel(). ↩

Domande frequenti

JavaScript può convertire testo in parlato senza una chiave API?

Sì. Nei browser compatibili, window.speechSynthesis pronuncia il testo con le voci disponibili sul dispositivo. Per questo esempio nel browser non servono account Cartesia o chiave API. Disponibilità e comportamento delle voci variano per browser e sistema operativo.

Perché speechSynthesis.getVoices() restituisce un array vuoto?

Il browser potrebbe non aver ancora caricato le voci. Chiama getVoices all'avvio e ascolta voiceschanged per aggiornare l'elenco. Se resta vuoto, verifica il supporto vocale del browser e le voci installate nel sistema operativo.

SpeechSynthesis può salvare un file MP3 o WAV?

SpeechSynthesis riproduce il parlato tramite il browser; non offre un'API che restituisca l'audio generato come file o flusso di byte. Se l'applicazione deve salvare o trasmettere il risultato, usa un servizio vocale con un endpoint di output audio.

La sintesi vocale del browser funziona offline?

Dipende dalla voce. SpeechSynthesisVoice.localService indica se una voce proviene da un servizio locale o remoto. Non presumere che ogni voce del browser funzioni offline o mantenga il testo sul dispositivo. Verifica la voce scelta sui dispositivi di destinazione.