Para convertir texto en voz con JavaScript, crea un SpeechSynthesisUtterance y pásalo a window.speechSynthesis.speak(). El navegador se encarga de reproducirlo. No necesitas una clave de API, un servidor ni un paquete de JavaScript.
Este enfoque funciona bien para un prototipo o un control de lectura en voz alta si te sirven las voces del dispositivo. Si desarrollas un agente de voz que necesita la misma voz en todos los dispositivos o transmitir audio a una llamada telefónica, usa una API de texto a voz alojada. SpeechSynthesis del navegador y la API de Cartesia son formas distintas de generar voz.
Cómo funciona la síntesis de voz del navegador
window.speechSynthesis es el controlador de voz sintetizada de la API Web Speech. Un enunciado contiene el texto y los ajustes de una solicitud. Al llamar a speak(), el enunciado se añade a una cola. No se devuelve un archivo de audio.1
El navegador expone las voces disponibles en el dispositivo actual mediante getVoices(). Esa lista puede cargarse después de ejecutar el script. Consúltala de inmediato y actualízala cuando se active voiceschanged.2 No fijes en el código el nombre de una voz de tu portátil esperando que también exista en el teléfono de un cliente.
1. Guarda una demo funcional de texto a voz
Guarda lo siguiente como speech.html y ábrelo en un navegador. Introduce una frase breve, elige una voz si el navegador muestra alguna y pulsa Speak. Usa texto de ejemplo que no sea confidencial. Algunas voces utilizan servicios remotos.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 la entrada a 500 caracteres para que la primera prueba sea breve. Es un límite de la demo, no de la API Web Speech. Prueba pasajes más largos por separado en cada navegador compatible con tu producto.
2. Comprueba la selección de voz y la reproducción
El selector muestra el nombre y el idioma de cada voz disponible. Elige una voz que coincida con el idioma del texto. Configurar utterance.lang no traduce las palabras.
Speak se ejecuta al pulsar un botón, en lugar de al cargar la página. Así el oyente tiene el control y no dependes de la reproducción automática. Pulsar Speak de nuevo cancela la solicitud actual antes de iniciar otra, en lugar de añadir cada clic a la cola. Stop llama a cancel(), que elimina los enunciados pendientes y detiene el habla actual.4
La comprobación de currentUtterance evita que una devolución de llamada tardía de una solicitud cancelada sobrescriba el estado de otra más reciente. Si adaptas este código a un componente, elimina el listener de voiceschanged cuando se desmonte. Cancela la reproducción al desmontarlo solo si ese componente controla la cola de voz de la página.
3. Resuelve problemas de silencio o habla inconsistente
| Síntoma | Qué comprobar |
|---|---|
| El selector solo muestra Browser default | Espera a voiceschanged. Si la lista sigue vacía, comprueba la compatibilidad del navegador y las voces instaladas. La demo aún puede solicitar una voz predeterminada. |
| No se oye nada al pulsar Speak | Comprueba el volumen y la salida de audio, prueba una frase breve en un idioma compatible y lee el mensaje de estado por si hay un error de síntesis. |
| Una voz existe en el ordenador, pero no en el móvil | Las listas dependen del dispositivo. Conserva una opción predeterminada y prueba los sistemas operativos reales que admites. |
| Varios clics seguidos retrasan el habla | speak() encola solicitudes. Cancela primero la anterior si el texto nuevo debe sustituirla. |
| El habla falla sin conexión a internet | La voz seleccionada puede usar un servicio remoto. Inspecciona voice.localService y prueba sin conexión en vez de asumir síntesis local. |
En una función de lectura en voz alta, conserva visible el texto original y permite acceder a Stop con el teclado. No inicies el habla automáticamente ni trates la voz sintetizada como un sustituto de la compatibilidad con lectores de pantalla.
Cuándo usar una API de TTS alojada
Elige la voz del navegador cuando basten las voces disponibles del oyente y solo necesites reproducir audio en ese dispositivo. Elige una API alojada cuando tu aplicación necesite bytes de audio generado, una identidad de voz gestionada por el proveedor o entregar audio fuera del navegador.
| Requisito | SpeechSynthesis del navegador | API de TTS alojada como Cartesia |
|---|---|---|
| Credenciales | No necesita clave de API de la aplicación | Credenciales de API y una cuenta con acceso de uso |
| Selección de voz | Lista de voces del navegador y del sistema operativo | Biblioteca del proveedor o voz personalizada autorizada |
| Destino del audio | Reproducción en el navegador | La aplicación recibe audio para reproducir, guardar o transportar |
| Comportamiento sin conexión | Depende de la voz y del dispositivo | Las solicitudes a la API de Cartesia necesitan conexión a la red |
| Tratamiento del texto | Local o remoto, según la voz | El texto se envía al proveedor; revisa los requisitos de datos antes de integrar |
Para usar la síntesis de voz de Cartesia, llama a la API desde tu backend. Mantén ahí la clave de API, fuera de archivos HTML públicos y del código del cliente. Una aplicación de navegador puede enviar texto a tu backend autenticado, que aplica límites de entrada y devuelve audio al cliente. Limita la frecuencia de solicitudes a ese endpoint para que los visitantes no consuman tus créditos de API sin límite.
Empieza por generar un archivo WAV con el SDK de Python de Cartesia. Esa guía prueba la generación alojada; no es un reproductor de streaming para navegador. Una aplicación conversacional también necesita búferes de reproducción y gestión de interrupciones. Compara ese trabajo con Cartesia Managed Agents antes de desarrollar tú todo el flujo del agente de voz.
Referencias relacionadas
- Compatibilidad de navegadores y referencia de la API SpeechSynthesis
- Cartesia Sonic
- Precios de la API de Cartesia
Notas al pie
-
MDN, SpeechSynthesis. ↩
-
MDN, SpeechSynthesis.getVoices(). ↩
-
MDN, SpeechSynthesis.cancel(). ↩