Speech-to-Text Local: Arquitetura Offline-First com Whisper e WebAssembly
APIs de transcrição como Google Cloud Speech e AWS Transcribe cobram entre $0.006 a $0.024 por minuto de áudio processado. Em escala, isso representa custos recorrentes significativos. Mais importante: cada áudio enviado para servidores externos cria um ponto de vazamento de dados sensíveis. Para aplicações de saúde, jurídicas ou empresariais, essa arquitetura é frequentemente inaceitável.
Processar transcrição completamente no cliente não é trivial. Modelos de machine learning modernos são grandes e computacionalmente intensivos. Até recentemente, essa abordagem seria considerada impraticável. A combinação de Whisper da OpenAI com WebAssembly mudou esse cenário — mas com trade-offs específicos que precisam ser compreendidos.
Este artigo explora a implementação técnica real de speech-to-text local, incluindo as limitações quantificadas que a documentação raramente explicita.
Fundamentos da Arquitetura: Whisper e Suas Implementações
Whisper, lançado pela OpenAI em setembro de 2022, é um modelo de reconhecimento de fala baseado em Transformer treinado em 680,000 horas de dados multilíngues. A implementação original usa PyTorch — irrelevante para aplicações web, já que você não vai rodar Python no navegador.
O que importa é whisper.cpp, criada por Georgi Gerganov. Ela reescreve Whisper em C/C++ puro, eliminando dependências pesadas e permitindo quantização. Quantização reduz a precisão numérica dos pesos do modelo (de float32 para int8, por exemplo), cortando drasticamente o tamanho em disco e requisitos de memória. O modelo tiny, que originalmente requer 390 MB em formato f16, encolhe para 75 MB quando quantizado em q5_1. Para base, a redução vai de 480 MB para 140 MB.
Quantização também reduz latência em 10-30% com perda mínima de precisão — tipicamente 1-3% de aumento no Word Error Rate. Essa é a diferença entre “impraticável” e “utilizável” em dispositivos com recursos limitados.
WebAssembly Como Camada de Execução
WebAssembly permite executar código compilado em navegadores com performance entre 50-80% da velocidade nativa. Para Whisper, a implementação WASM baseada em whisper.cpp oferece a única rota viável para transcrição local sem plugins ou instalação de software. Mas há limitações arquiteturais fundamentais.
WASM em navegadores ainda não tem acesso completo a multi-threading. Implementações típicas rodam em single-thread, usando apenas um core da CPU. Isso contrasta diretamente com executáveis nativos que podem distribuir carga entre todos os cores disponíveis. Em um MacBook Pro M1, o modelo base processa áudio a ~6x tempo real nativamente. Em WASM, esse fator cai para aproximadamente 20-40 segundos para processar 30 segundos de áudio — uma diferença de 3-5x.
Não há aceleração GPU via WebGPU ainda. A API está em desenvolvimento, mas implementações estáveis e amplamente disponíveis não existem. Enquanto uma RTX 3090 processa 30 segundos de áudio em 1-2 segundos usando GPU, o navegador usa CPU exclusivamente, aumentando latência dramaticamente.
Navegadores impõem limites de memória. Tipicamente, você tem entre 2-4GB disponíveis para processos WASM. Modelos large do Whisper, com 1550M parâmetros, excedem facilmente esse limite e são completamente impraticáveis para uso em browser.
Implementações Práticas: Transformers.js vs whisper.wasm
Duas implementações dominam o espaço, cada uma com trade-offs distintos.
Transformers.js usa ONNX Runtime Web, convertendo modelos PyTorch para formato ONNX otimizado. O ecossistema é mais maduro, com melhor integração ao pipeline de ML da Hugging Face. O overhead de inicialização é maior — 2-5 segundos no primeiro carregamento — mas a API é mais consistente e estável. Para aplicações que processam múltiplos áudios em sequência, esse custo inicial é amortizado.
whisper.wasm é explicitamente experimental. A documentação do whisper.cpp marca a implementação WASM como tal desde 2023. Performance é consistentemente 3-5x mais lenta que a versão nativa, mas o modelo de execução é mais previsível. Não há camadas de abstração adicionais entre o código WASM e a lógica do modelo.
Escolhendo Modelo e Quantização
A decisão mais crítica é o tamanho do modelo. Cinco opções existem: tiny (39M parâmetros), base (74M), small (244M), medium (769M) e large (1550M). Para arquitetura local, apenas tiny e base são viáveis em produção.
Modelo tiny processado em WASM pode operar próximo de tempo real em hardware moderno — digamos, um laptop com processador de última geração. “Próximo” aqui significa real-time factor (RTF) de 1.0 a 1.5. RTF mede quanto tempo leva para processar um segundo de áudio: RTF de 1.0 significa que leva exatamente 1 segundo para processar 1 segundo de áudio. RTF de 0.5 significa duas vezes mais rápido que tempo real. RTF de 2.0 significa duas vezes mais lento.
Base oferece melhor precisão, mas RTF em WASM frequentemente fica entre 1.5-2.5 em hardware médio. Para aplicações onde usuários enviam áudios curtos (10-30 segundos), isso ainda é aceitável — total de espera fica entre 15-75 segundos. Para transcrições longas ou em tempo real, isso se torna problemático.
Small, mesmo quantizado, produz bundles de 500MB+ e performance insuficiente para maioria dos casos. É uma opção para aplicações desktop usando Electron ou Tauri, não para web.
Estratégias de Otimização e Cache
Carregar modelos é caro. Download inicial de 75-250MB seguido de parse e inicialização do modelo adiciona 5-30 segundos antes de qualquer processamento começar. Aplicações robustas precisam cachear modelos localmente usando IndexedDB.
O padrão é simples: verificar se modelo existe no cache, baixar apenas se necessário, e manter versão em IndexedDB para carregamentos subsequentes. Cache elimina o download, mas inicialização do modelo ainda leva 1-3 segundos mesmo com arquivo já disponível localmente.
SharedArrayBuffer permite zero-copy data sharing entre workers, reduzindo overhead de memória e tempo de transferência. Mas isso requer configuração específica de headers CORS: Cross-Origin-Opener-Policy precisa ser “same-origin” e Cross-Origin-Embedder-Policy precisa ser “require-corp”. Sem esses headers, SharedArrayBuffer simplesmente não funciona, e você volta para transferências via postMessage que copiam dados inteiros.
SIMD (Single Instruction, Multiple Data) acelera operações vetoriais fundamentais para processamento de áudio. Chrome 91+, Firefox 89+ e Safari 15.2+ têm suporte completo. Garantir que WASM seja compilado com SIMD habilitado pode melhorar performance em 20-40%, dependendo da implementação específica do runtime WASM que você está usando.
Progressive Loading e Fallbacks
Para UX aceitável, aplicações precisam de progressive loading. Começar com modelo tiny permite transcription inicial rápida, enquanto modelo base carrega em background. Se usuário precisar de maior precisão, sistema pode retranscrever usando modelo melhor. Essa arquitetura duplica processamento, mas melhora percepção de performance.
Fallback para API externa quando device é insuficiente faz sentido tecnicamente, mas contradiz a premissa de privacidade. Se você oferece fallback, precisa ser explícito com usuário sobre quando dados saem do device. Consentimento informado não é opcional aqui.
Realidade de Performance e Custos
Números importam mais que arquitetura elegante. Em CPU Intel i5 comum, modelo base processa 30 segundos de áudio em aproximadamente 15-25 segundos — definitivamente não tempo real. Em Intel i7-12700K, esse tempo cai para 6-10 segundos. Apple M1 processa em 4-6 segundos. WASM adiciona overhead, então essas latências aumentam 3-5x.
Bateria é outro custo. Processamento local intensivo consome 3-5x mais energia comparado a enviar áudio para API e receber texto de volta. Para devices móveis, isso não é trivial — usuários perceberão drenagem mais rápida de bateria em aplicações que usam transcrição local frequentemente.
O trade-off fundamental: você troca latência e bateria por privacidade e custo zero de API. Em alguns contextos (aplicações médicas, jurídicas, dados financeiros), esse trade-off vale absolutamente a pena. Em outros (transcrições casuais, aplicações recreativas), custos podem não justificar benefícios.
Conformidade regulatória adiciona outra camada. HIPAA e GDPR têm requisitos específicos sobre processamento de dados. Processamento completamente local elimina muitos requisitos de compliance, mas implementação precisa ser auditável — você precisa provar que dados realmente nunca saem do device. Métricas de produção específicas sobre conformidade não são públicas, mas a arquitetura em si simplifica dramaticamente o processo de certificação.
Decisão Arquitetural
Arquitetura offline-first com Whisper não é universalmente apropriada. Funciona melhor para:
Aplicações onde privacidade é requisito não-negociável e usuários aceitam trade-offs de performance. Software de saúde mental, transcrição de sessões terapêuticas, documentação jurídica sensível.
Ambientes com conectividade limitada ou intermitente. Aplicações usadas em campo, regiões com infraestrutura de rede precária, contextos onde latência de rede é imprevisível.
Casos onde volume de transcrição tornaria APIs externas economicamente inviáveis. Se você processa centenas de horas de áudio mensalmente, custos de API crescem linearmente. Processamento local tem custo fixo de implementação, mas custo marginal zero por minuto transcrito.
O que a arquitetura não resolve: performance comparável a GPUs dedicadas, suporte a modelos large, transcrição em tempo real consistente em hardware mediano. Esses são constrangimentos técnicos fundamentais, não problemas de implementação.
Hardware moderno está melhorando. WebGPU eventualmente permitirá aceleração GPU real no navegador — mas “eventualmente” não é cronograma de produção. Decisões arquiteturais precisam ser baseadas em capacidades disponíveis hoje, não em promessas futuras.
Se você está projetando sistema de transcrição, comece com benchmarks reais no hardware que seus usuários efetivamente usam. Teste latência, use ferramentas de profiling para identificar bottlenecks, e meça impacto de bateria. Arquitetura local é viável, mas apenas com expectativas calibradas sobre o que “viável” significa quantitativamente.