Para converter texto em fala em JavaScript, crie um SpeechSynthesisUtterance e passe-o para window.speechSynthesis.speak(). O navegador cuida da reprodução. Você não precisa de chave de API, servidor ou pacote JavaScript.
Isso funciona bem para um protótipo ou um controle de leitura em voz alta quando as vozes do dispositivo são suficientes. Se você está criando um agente de voz que precisa manter a mesma voz entre dispositivos ou transmitir áudio para uma chamada telefônica, use uma API de texto em fala hospedada. O SpeechSynthesis do navegador e a API da Cartesia são formas distintas de gerar fala.
Como funciona a síntese de fala no navegador
window.speechSynthesis é o controlador de fala sintetizada da Web Speech API. Um enunciado contém o texto e as configurações de uma solicitação. Chamar speak() adiciona o enunciado a uma fila; não retorna um arquivo de áudio.1
O navegador expõe as vozes disponíveis no dispositivo atual por meio de getVoices(). Essa lista pode carregar depois que seu script for executado, então leia-a imediatamente e atualize-a quando o evento voiceschanged ocorrer.2 Não fixe no código o nome de uma voz do seu notebook esperando que ela exista no celular de um cliente.
1. Salve uma demonstração funcional de texto em fala
Salve o código abaixo como speech.html e abra-o em um navegador. Digite uma frase curta, escolha uma voz se o navegador listar alguma e pressione Speak. Use texto de exemplo sem informações sensíveis: algumas vozes usam serviços de fala 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>
A demonstração limita a entrada a 500 caracteres para manter o primeiro teste curto. Esse é um limite da demonstração, não da Web Speech API. Teste trechos mais longos separadamente em cada navegador compatível com sua aplicação.
2. Verifique a seleção de voz e a reprodução
O seletor de vozes mostra o nome e o idioma de cada voz disponível. Escolha uma voz que corresponda ao idioma do texto; definir utterance.lang não traduz as palavras.
Speak é executado ao clicar no botão, não ao carregar a página. Isso dá controle ao ouvinte e evita depender da reprodução automática. Pressionar Speak novamente cancela a solicitação atual antes de iniciar outra, em vez de enfileirar cliques repetidos. Stop chama cancel(), que remove os enunciados da fila e interrompe a fala atual.4
A verificação de currentUtterance impede que um callback tardio de uma solicitação cancelada sobrescreva o estado de uma solicitação mais recente. Se adaptar esse código para um componente, remova o listener de voiceschanged quando o componente for desmontado. Cancele a reprodução na desmontagem apenas se esse componente for responsável pela fila de fala da página.
3. Resolva problemas de silêncio ou fala inconsistente
| Sintoma | O que verificar |
|---|---|
| O seletor mostra apenas Browser default | Aguarde voiceschanged. Se a lista continuar vazia, verifique o suporte do navegador e as vozes instaladas no sistema. A demonstração ainda pode solicitar uma voz padrão. |
| Nenhum som após pressionar Speak | Verifique o volume e a saída de áudio do dispositivo, tente uma frase curta no idioma correspondente e leia a mensagem de estado para identificar um erro de síntese. |
| Uma voz existe no computador, mas não no celular | As listas de vozes dependem do dispositivo. Mantenha uma opção padrão e teste nos sistemas operacionais reais que você oferece suporte. |
| Cliques repetidos atrasam a fala | speak() enfileira solicitações. Cancele primeiro a solicitação antiga se o novo texto precisar substituí-la. |
| A fala falha sem conexão com a internet | A voz selecionada pode usar um serviço remoto. Inspecione voice.localService e teste offline em vez de presumir síntese local. |
Para uma função de leitura em voz alta, mantenha o texto original visível e torne Stop acessível pelo teclado. Não inicie a fala automaticamente nem trate a fala sintetizada como substituta do suporte a leitores de tela.
Quando usar uma API de TTS hospedada
Escolha a fala do navegador quando as vozes disponíveis para o ouvinte forem suficientes e você só precisar reproduzir áudio naquele dispositivo. Escolha uma API hospedada quando sua aplicação precisar dos bytes do áudio gerado, de uma identidade de voz gerenciada pelo provedor ou de áudio entregue fora do navegador.
| Requisito | SpeechSynthesis do navegador | API de TTS hospedada, como a Cartesia |
|---|---|---|
| Credenciais | Sem chave de API da aplicação | Credenciais de API e conta com acesso de uso |
| Seleção de voz | Lista de vozes do navegador e do sistema operacional | Biblioteca de vozes do provedor ou voz personalizada autorizada |
| Destino do áudio | Reprodução no navegador | Sua aplicação recebe áudio para reprodução, armazenamento ou transporte |
| Funcionamento offline | Depende da voz e do dispositivo | Solicitações à API da Cartesia exigem conexão de rede |
| Tratamento do texto | Local ou remoto, conforme a voz | O texto é enviado ao provedor; revise os requisitos de dados antes da integração |
Para usar a síntese de fala da Cartesia, chame a API pelo seu backend. Mantenha a chave de API lá, não em um arquivo HTML público ou no bundle do cliente. Uma aplicação no navegador pode enviar texto ao seu backend autenticado, que aplica limites de entrada e retorna áudio ao cliente. Limite a taxa de solicitações desse endpoint para que visitantes não possam consumir seus créditos de API sem restrição.
Comece gerando um arquivo WAV com o SDK Python da Cartesia. Esse guia testa o caminho de geração hospedada; não é um reprodutor de streaming no navegador. Para uma aplicação conversacional, você também precisará de buffer de reprodução e tratamento de interrupções. Compare esse trabalho com o Cartesia Managed Agents antes de construir sozinho todo o pipeline do agente de voz.
Referências relacionadas
- Compatibilidade com navegadores e referência da API SpeechSynthesis
- Cartesia Sonic
- Preços da API da Cartesia
Notas de rodapé
-
MDN, SpeechSynthesis. ↩
-
MDN, SpeechSynthesis.getVoices(). ↩
-
MDN, SpeechSynthesis.cancel(). ↩