A API Web Speech: Reconhecimento e Síntese de Voz no Navegador

Aprenda a usar a API Web Speech nativa para adicionar reconhecimento de voz (Speech-to-Text) e síntese de voz (Text-to-Speech) às suas aplicações web, com exemplos práticos e estratégias de compatibilidade entre navegadores.

A API Web Speech: Reconhecimento e Síntese de Voz no Navegador
11 min de leitura
Nesta página

Resumo

As interfaces web evoluíram além de cliques e pressionamentos de tecla. A API Web Speech, desenvolvida pelo W3C, fornece dois subsistemas independentes para integrar voz em qualquer aplicação web: SpeechRecognition (speech-to-text) para capturar e transcrever a voz do usuário, e SpeechSynthesis (text-to-speech) para ler texto em voz alta usando as vozes disponíveis no sistema.

Este artigo cobre a arquitetura de ambos os subsistemas, mostra implementações práticas com código e aborda problemas críticos como a fragmentação entre navegadores, o bug dos 15 segundos no Chrome e as restrições do iOS.


1. Dois Subsistemas Independentes

A API é dividida em dois módulos completamente separados:

SubsistemaDireçãoInterfaceSuporte
SpeechRecognitionUsuário → App (entrada de microfone)SpeechRecognition / webkitSpeechRecognitionLimitado (principalmente Chromium)
SpeechSynthesisApp → Usuário (saída de áudio)window.speechSynthesis + SpeechSynthesisUtteranceUniversal (Baseline)

Nota: SpeechSynthesis tem adoção universal em todos os navegadores modernos. SpeechRecognition, no entanto, continua sendo experimental com suporte fragmentado. Sempre implemente detecção de recursos antes de usar qualquer um dos dois.


2. Reconhecimento de Voz (Speech-to-Text)

2.1 Detecção de Suporte e Instanciação

Devido ao uso de prefixos de vendors, o construtor deve ser resolvido de forma defensiva:

const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;

if (!SpeechRecognition) {
  console.error('SpeechRecognition não é suportado neste navegador.');
}

2.2 Propriedades de Configuração

Uma vez instanciado, o motor de reconhecimento é configurado através de quatro propriedades chave:

PropriedadeTipoDescrição
langstringTag BCP 47 do idioma (ex.: "en-US", "pt-BR"). Por padrão, usa o atributo lang do documento.
continuousbooleanSe true, continua ouvindo após retornar resultados. Por padrão: false (para após um resultado).
interimResultsbooleanSe true, emite transcrições provisórias em tempo real antes da finalização.
maxAlternativesnumberNúmero de transcrições alternativas por segmento de resultado. Por padrão: 1.
const recognition = new SpeechRecognition();
recognition.lang = 'pt-BR';
recognition.continuous = true;
recognition.interimResults = true;
recognition.maxAlternatives = 1;

2.3 A Hierarquia de Resultados

Os resultados não são strings puras. Chegam como uma árvore de objetos com três níveis:

  1. SpeechRecognitionResultList — uma coleção tipo lista acessível via event.results
  2. SpeechRecognitionResult — cada elemento contém um booleano isFinal e uma ou mais alternativas
  3. SpeechRecognitionAlternative — contém a string transcript e um valor de confidence (0 a 1)
recognition.onresult = (event) => {
  for (let i = event.resultIndex; i < event.results.length; i++) {
    const result = event.results[i];
    const transcript = result[0].transcript;
    const confidence = result[0].confidence;

    if (result.isFinal) {
      console.log(`Final: "${transcript}" (${(confidence * 100).toFixed(1)}%)`);
    } else {
      console.log(`Provisório: "${transcript}"`);
    }
  }
};

2.4 Ciclo de Vida de Eventos

O motor de reconhecimento dispara eventos em uma sequência determinística:

start → audiostart → soundstart → speechstart
  ↓
  result (se repete enquanto se fala)
  ↓
speechend → soundend → audioend → end

Eventos chave a manipular:

recognition.onspeechstart = () => {
  console.log('Voz detectada — transcrição em andamento...');
};

recognition.onerror = (event) => {
  switch (event.error) {
    case 'not-allowed':
      console.error('Acesso ao microfone negado pelo usuário ou política.');
      break;
    case 'network':
      console.error('Serviço de reconhecimento na nuvem inalcançável.');
      break;
    case 'no-speech':
      console.warn('Nenhuma voz detectada — apenas silêncio ou ruído de fundo.');
      break;
    case 'aborted':
      console.warn('O reconhecimento foi abortado.');
      break;
  }
};

recognition.onnomatch = () => {
  console.warn('Fala detectada, mas não pôde ser reconhecida.');
};

recognition.onend = () => {
  console.log('Sessão de reconhecimento finalizada.');
};

2.5 Exemplo Completo: Ditado ao Vivo

const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;

if (!SpeechRecognition) {
  document.getElementById('status').textContent = 'Reconhecimento de voz não suportado.';
} else {
  const recognition = new SpeechRecognition();
  recognition.lang = 'pt-BR';
  recognition.continuous = true;
  recognition.interimResults = true;

  const finalOutput = document.getElementById('final');
  const interimOutput = document.getElementById('interim');
  let finalTranscript = '';

  recognition.onresult = (event) => {
    let interim = '';

    for (let i = event.resultIndex; i < event.results.length; i++) {
      const transcript = event.results[i][0].transcript;

      if (event.results[i].isFinal) {
        finalTranscript += transcript;
      } else {
        interim += transcript;
      }
    }

    finalOutput.textContent = finalTranscript;
    interimOutput.textContent = interim;
  };

  recognition.onerror = (event) => {
    document.getElementById('status').textContent = `Erro: ${event.error}`;
  };

  document.getElementById('start-btn').addEventListener('click', () => {
    finalTranscript = '';
    recognition.start();
    document.getElementById('status').textContent = 'Ouvindo...';
  });

  document.getElementById('stop-btn').addEventListener('click', () => {
    recognition.stop();
    document.getElementById('status').textContent = 'Parado.';
  });
}

3. Síntese de Voz (Text-to-Speech)

3.1 O Controlador Singleton

Diferente do SpeechRecognition, o motor de síntese é acessado através de uma única instância global:

const synth = window.speechSynthesis;

Este controlador gerencia uma fila FIFO de locuções e expõe três flags de estado somente leitura:

PropriedadeDescrição
synth.pendingtrue se houver locuções aguardando na fila
synth.speakingtrue se o áudio estiver sendo produzido atualmente
synth.pausedtrue se a reprodução tiver sido pausada

Métodos de controle:

synth.speak(utterance);
synth.pause();
synth.resume();
synth.cancel();

3.2 Criando Locuções

O objeto SpeechSynthesisUtterance é a unidade de fala. Contém o texto e toda a configuração prosódica:

PropriedadeIntervaloDescrição
textStringO conteúdo de texto a ser pronunciado
voiceSpeechSynthesisVoiceO perfil de voz a ser utilizado
langString (BCP 47)Sobrescrita do idioma
pitch0 – 2Tom de voz (1 = normal)
rate0.1 – 10Velocidade da fala (1 = normal)
volume0 – 1Volume do áudio (1 = máximo)
const utterance = new SpeechSynthesisUtterance('Olá, bem-vindo à aplicação.');
utterance.lang = 'pt-BR';
utterance.pitch = 1;
utterance.rate = 1;
utterance.volume = 0.8;

synth.speak(utterance);

3.3 Carregando Vozes de Forma Assíncrona

As vozes são carregadas assincronamente pelo navegador (especialmente no Chromium). Uma chamada síncrona para getVoices() durante o carregamento da página frequentemente retorna um array vazio. Você deve ouvir o evento voiceschanged:

const synth = window.speechSynthesis;
let voices = [];

const loadVoices = () => {
  voices = synth.getVoices();
  console.log(`${voices.length} vozes carregadas.`);
};

loadVoices();

if (synth.onvoiceschanged !== undefined) {
  synth.onvoiceschanged = loadVoices;
}

Cada objeto SpeechSynthesisVoice contém:

voices.forEach((voice) => {
  console.log(`${voice.name} (${voice.lang}) ${voice.default ? '— PADRÃO' : ''}`);
});

3.4 Exemplo Completo: Leitor de Texto com Seleção de Voz

const synth = window.speechSynthesis;
let voices = [];

const voiceSelect = document.getElementById('voice-select');
const textInput = document.getElementById('text-input');

const populateVoices = () => {
  voices = synth.getVoices();
  voiceSelect.innerHTML = '';

  voices.forEach((voice, index) => {
    const option = document.createElement('option');
    option.value = index;
    option.textContent = `${voice.name} (${voice.lang})`;
    voiceSelect.appendChild(option);
  });
};

populateVoices();
if (synth.onvoiceschanged !== undefined) {
  synth.onvoiceschanged = populateVoices;
}

document.getElementById('speak-btn').addEventListener('click', () => {
  synth.cancel();

  const utterance = new SpeechSynthesisUtterance(textInput.value);
  utterance.voice = voices[voiceSelect.value];
  utterance.pitch = parseFloat(document.getElementById('pitch').value);
  utterance.rate = parseFloat(document.getElementById('rate').value);

  utterance.onstart = () => {
    document.getElementById('status').textContent = 'Falando...';
  };

  utterance.onend = () => {
    document.getElementById('status').textContent = 'Finalizado.';
  };

  utterance.onerror = (event) => {
    document.getElementById('status').textContent = `Erro: ${event.error}`;
  };

  synth.speak(utterance);
});

document.getElementById('stop-btn').addEventListener('click', () => {
  synth.cancel();
  document.getElementById('status').textContent = 'Cancelado.';
});

4. Processamento na Nuvem vs. no Dispositivo

SpeechRecognition pode rotear o áudio através de duas pipelines radicalmente diferentes:

4.1 Processamento na Nuvem (Por Padrão)

Por padrão, o Chrome serializa o fluxo de áudio e o envia aos servidores na nuvem do Google para transcrição. Isso oferece alta precisão (modelos neurais profundos, enormes conjuntos de dados de treinamento), mas introduz comprometimentos:

  • Latência: viagens de ida e volta pela rede degradam experiências em tempo real
  • Privacidade: os dados brutos de áudio saem do dispositivo
  • Sem conexão: completamente inoperante sem internet

4.2 Processamento no Dispositivo (Experimental)

Navegadores modernos estão introduzindo inferência local usando modelos no dispositivo. Isso elimina a latência de rede, funciona sem conexão e mantém o áudio privado:

const recognition = new SpeechRecognition();

const availability = await SpeechRecognition.available();

if (availability === 'available') {
  recognition.start();
} else if (availability === 'downloadable') {
  await SpeechRecognition.install();
  recognition.start();
} else {
  console.warn('Reconhecimento no dispositivo não suportado — usando a nuvem como fallback.');
  recognition.start();
}

Nota: SpeechRecognition.available() e SpeechRecognition.install() são APIs experimentais. Permitem verificar se os modelos de idioma estão armazenados em cache localmente e ativar seu download. O suporte limita-se a versões recentes do Chromium.


5. Modelo de Segurança

A API Web Speech implementa restrições de segurança rigorosas para prevenir a interceptação passiva de áudio:

  • Gesto do usuário necessário: recognition.start() deve ser invocado dentro de um evento iniciado pelo usuário (clique, toque). Invocá-lo programaticamente sem um gesto lança um erro.
  • Apenas HTTPS: toda a API é restrita a contextos seguros. Páginas HTTP não podem acessar o microfone.
  • Indicadores visuais: navegadores exibem indicadores de gravação persistentes e inalteráveis (ícones nas abas, alertas na barra de status) enquanto o microfone está ativo.
  • Permissions-Policy: administradores de servidores podem bloquear o acesso ao microfone no nível de cabeçalho HTTP para iframes de origem cruzada:
Permissions-Policy: microphone=()

6. Compatibilidade entre Navegadores (2026)

NavegadorSpeechSynthesis (TTS)SpeechRecognition (STT)Notas
Chrome✅ Completo (v33+)✅ Completo (v33+)Implementador principal. Baseado na nuvem por padrão.
Edge✅ Completo (v14+)⚠️ Não confiávelExpõe a interface, mas os eventos podem nunca disparar.
Firefox✅ Completo (v49+)❌ Não implementadoObjeção filosófica ao roteamento de biometria vocal para nuvens de terceiros.
Safari✅ Completo (v7+)⚠️ Parcial (v14.1+)Requer prefixo webkit. Depende do Siri estar ativado.
Opera✅ Completo (v21+)❌ Não implementadoReflete as limitações do Edge/Firefox.
Brave⚠️ Variável❌ BloqueadoBloqueia conexões STT à nuvem por privacidade.

6.1 Detecção Defensiva de Recursos

Sempre verifique o suporte antes de usar qualquer um dos dois subsistemas:

const hasSpeechRecognition = 'SpeechRecognition' in window || 'webkitSpeechRecognition' in window;
const hasSpeechSynthesis = 'speechSynthesis' in window;

if (!hasSpeechRecognition) {
  console.warn('SpeechRecognition não disponível. Considere um fallback na nuvem (Google Cloud STT, Azure, AWS Transcribe).');
}

if (!hasSpeechSynthesis) {
  console.warn('SpeechSynthesis não disponível.');
}

7. Problemas Conhecidos e Soluções

7.1 Bug dos 15 Segundos no Chrome (TTS)

Vozes baseadas na nuvem no Chrome abortam locuções com mais de ~15 segundos sem disparar eventos end ou error, deixando a interface em um estado de “falando” permanente. A solução é dividir textos longos em locuções menores:

const speakLongText = (text) => {
  const synth = window.speechSynthesis;
  synth.cancel();

  const sentences = text.match(/[^.!?]+[.!?]+/g) || [text];

  sentences.forEach((sentence) => {
    const utterance = new SpeechSynthesisUtterance(sentence.trim());
    utterance.voice = voices[0];
    synth.speak(utterance);
  });
};

7.2 Safari iOS: TTS Requer um Gesto de “Aquecimento”

O Safari no iOS bloqueia o áudio TTS até que o usuário execute um gesto. Para resolver isso, “aqueça” silenciosamente o contexto de áudio na primeira interação do usuário:

document.addEventListener('click', () => {
  const primer = new SpeechSynthesisUtterance('');
  window.speechSynthesis.speak(primer);
}, { once: true });

Após isso, chamadas subsequentes para synth.speak() funcionarão sem exigir gestos adicionais.

7.3 SpeechRecognition em PWAs do iOS

Quando uma página web é adicionada à tela de início no iOS (“Adicionar à tela de início”), o webkitSpeechRecognition é completamente desativado. Todas as chamadas lançam erros NotAllowed sem sequer exibir a caixa de permissões. Não há solução dentro da API Web Speech — deve-se usar um serviço STT baseado na nuvem em seu lugar.


8. Benefícios de Acessibilidade

A API Web Speech melhora diretamente a acessibilidade para:

  • Usuários com deficiência motora: comandos de voz substituem interações de teclado e mouse
  • Usuários com deficiência visual: TTS lê o conteúdo em voz alta, complementando leitores de tela
  • Usuários com baixa alfabetização: o feedback auditivo reduz a dependência da leitura
  • Cenários mãos livres: cozinha, direção ou ambientes médicos onde a entrada tátil/digitação é impraticável

Melhores práticas ao integrar voz em interfaces acessíveis:

  • Desvincular o teclado virtual chamando element.blur() quando o reconhecimento inicia, recuperando espaço do viewport no celular
  • Exibir resultados provisórios visualmente para que o usuário veja feedback em tempo real durante o ditado
  • Usar o evento boundary do TTS para destacar o texto enquanto é lido, oferecendo uma experiência visual-auditiva sincronizada

9. Conclusão

A API Web Speech fornece uma ponte poderosa entre a voz e a plataforma web. O SpeechSynthesis está pronto para produção em todos os navegadores, enquanto o SpeechRecognition requer detecção cuidadosa de recursos e estratégias de fallback devido ao suporte fragmentado.

Comece com TTS para melhorias de acessibilidade — funciona em todos os lugares. Para STT, foque em navegadores Chromium com reconhecimento baseado na nuvem, implemente tratamento robusto de erros e avalie serviços de fallback na nuvem (Google Cloud Speech-to-Text, Azure Cognitive Services, AWS Transcribe) para cobertura entre navegadores.


Fontes: Especificação W3C Web Speech API, MDN Web Docs, rastreador de problemas do Chromium, dados de compatibilidade (caniuse.com). Os exemplos correspondem ao estado da API em março de 2026.

Arraste para mover o diagrama. Use + e − para dar zoom, as setas para movê-lo e 0 para redefinir a visualização.

Vamos trabalhar juntos

Engenheiro de Software e desenvolvedor Senior Full-Stack, trabalho remotamente a partir de Santa Cruz, Bolívia. Aberto a novas oportunidades.