WhatsApp Web: chamadas de voz e vídeo E2E com WebRTC e Signal

WhatsApp Web: chamadas de voz e vídeo E2E com WebRTC e Signal

Quando li no Sapo.pt que o WhatsApp finalmente trouxe chamadas de voz e vídeo para a versão Web, minha primeira reação foi: “demorou, Meta”. A segunda foi abrir o DevTools e fuçar o que mudou de verdade por baixo dos panos. Esse tipo de feature parece simples para o usuário final, mas para quem já tentou implementar algo parecido sabe: chamadas de áudio/vídeo nativas no browser com criptografia ponta a ponta é uma das tarefas mais ingratas que existe em engenharia web. Vou destrinchar o que mudou, o que isso significa para nós, devs, e onde estão as armadilhas.

O que de fato mudou no WhatsApp Web

Até ontem — falando metaforicamente — quem quisesse fazer chamada pelo PC precisava do app desktop. A nova versão elimina essa dependência e permite iniciar chamadas diretamente pela aba do navegador. A interface ganhou um separador para histórico e favoritos, manteve a partilha de ecrã e as reações em tempo real, e adicionou algo que pouca gente comentou: a transferência contínua de chamadas de grupo entre dispositivos.

Esse último ponto é o mais relevante do ponto de vista de produto. Significa que o estado da chamada — quem está conectado, microfones ativos, layout — trafega entre telefone e desktop sem cortar a ligação. Isso exige uma sincronização de estado que não é trivial quando você tem E2E no caminho. Se você já brigou com sincronização de sessão em apps multi-device, entende a dor.

A engenharia por trás: WebRTC, Signal Protocol e a dor do E2E no browser

Por trás de qualquer chamada no browser existe WebRTC. É o conjunto de APIs (getUserMedia, RTCPeerConnection, RTCDataChannel) que dá ao JavaScript acesso à câmera, microfone e à negociação de mídia ponto a ponto. O handshake depende de três peças:

  • Signaling — servidor que troca ofertas SDP e ICE candidates entre os pares. No WhatsApp isso é feito pela própria infra de mensageria.
  • STUN — descobre o IP público do cliente e testa conectividade.
  • TURN — relay de mídia quando o NAT é tão restrictivo que P2P direto falha. Sem TURN configurado, cerca de 8% a 12% das chamadas simplesmente não conectam em redes corporativas agressivas.

Agora o pulo do gato: o WhatsApp usa criptografia ponta a ponta em cima do Signal Protocol (Double Ratchet Algorithm). O problema é que o Signal nunca foi trivialmente portável para o navegador por causa de particularidades de implementação — chaves derivadas de curvas elípticas (X25519), gestão de pré-chaves (prekeys), e os storages IndexedDB que precisam ser sincronizados entre devices sem nunca expor material criptográfico ao servidor. Fazer isso acontecer no WhatsApp Web exigiu reescrever boa parte do cliente em TypeScript com WebAssembly nos pontos quentes de criptografia.

Quando você ouve “transferência de chamada entre dispositivos sem queda”, entenda: cada dispositivo do usuário detém uma identidade criptográfica, e a chamada em andamento precisa renegociar chaves ou reaproveitar a session já estabelecida. Isso é, no mínimo, delicado.

Na prática: como testar agora e como implementar algo parecido no seu projeto

A ativação é direta: atualize WhatsApp Web, dê permissão de microfone e câmera quando o navegador pedir, e o ícone de chamada aparece nos contatos individuais e em grupos. Funciona em Chrome, Edge, Firefox, Safari recente e Opera.

Se você quiser entender o que está rolando por baixo, abra o DevTools e rode isto no console para inspecionar sua própria mídia antes da oferta SDP:

// Verifica suporte a WebRTC e lista devices
async function inspectMedia() {
  if (!navigator.mediaDevices?.getUserMedia) {
    console.error('getUserMedia indisponível neste navegador');
    return;
  }

  const devices = await navigator.mediaDevices.enumerateDevices();
  console.table(devices.map(d => ({ kind: d.kind, label: d.label, id: d.deviceId.slice(0, 8) + '...' })));

  const stream = await navigator.mediaDevices.getUserMedia({
    audio: { echoCancellation: true, noiseSuppression: true },
    video: { width: 1280, height: 720, frameRate: 30 }
  });

  const track = stream.getVideoTracks()[0];
  const settings = track.getSettings();
  console.log('Resolução efetiva:', settings.width, 'x', settings.height, '@', settings.frameRate, 'fps');

  // RTCPeerConnection base com servidores STUN públicos do Google
  const pc = new RTCPeerConnection({
    iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
  });

  pc.onicecandidate = (e) => console.log('ICE candidate:', e.candidate?.candidate || '(end)');
  stream.getTracks().forEach(t => pc.addTrack(t, stream));

  const offer = await pc.createOffer();
  await pc.setLocalDescription(offer);
  console.log('SDP offer gerada:', offer.sdp.length, 'caracteres');

  return { pc, stream };
}

inspectMedia();

Esse é o esqueleto mínimo. Em produção você precisa de signaling server (WebSocket ou SSE), handler de SDP remoto, e fallback para TURN. Mais importante: trate permissões negadas com UX clara, porque o usuário comum clica em “bloquear” duas vezes e some.

Comparativo honesto com alternativas

Plataforma Chamada no browser E2E Latência média Observação dev
WhatsApp Web Sim (agora) Sim (Signal) ~250 ms Sem API pública; difícil de integrar
Telegram Web Sim Apenas em chamadas secretas ~300 ms Tem TDLib, dá pra automatizar
Discord Sim Não (DTLS no transporte) ~200 ms Elgato Stream Deck integra direto
Google Meet Nativo Parcial (client-side encryption em Workspace) ~280 ms Forte em legendas e transcripts
Zoom Web Sim E2EE opcional ~350 ms WebSDK maduro, mas fechado

O WhatsApp ganha em penetração e em consistência do modelo de segurança. Perde em extensibilidade — não existe API de terceiros decente para chamadas no estado atual.

Armadilhas comuns que devs caem (e que a Meta provavelmente pagou caro)

1. Não testar atrás de NAT simétrico. IPv4 compartilhado em redes mobile 4G/5G + CGNAT quebra chamadas P2P. Sem TURN configurado, o usuário só ouve silêncio. Teste com symmetric nat em ambiente staging.

2. Ignorar throttling de aba em background. Chrome reduz timers para 1 por minuto em abas inativas. Se sua chamada depende de keepalive via setInterval ou de reconexão ICE, ela morre quando o usuário troca de aba. Use Service Worker ou Visibility API para reagir.

3. Confundir dispositivo autorizado com sessão ativa. Multi-device é um inferno de revogação. Quando o usuário remove um device, todas as sessões E2E pendentes precisam ser invalidadas, e isso precisa propagar offline.

4. Subestimar custo do TURN. TURN relay custa banda. Em chamada de vídeo com 8 pessoas em arquitetura SFU, o relay por participante pode chegar a 2 Mbps. Multiplicar isso pelo preço do AWS/GCP e você tem uma surpresa no fim do mês.

5. Esquecer da acessibilidade. getUserMedia não respeita automaticamente legendas. Se você constrói um produto B2B, prepare-se para WCAG 2.2 AA desde o dia um.

6. Misturar room ID com chaves criptográficas. Erro clássico: logar o SDP para debug e vazar material de chave. SDP “puro” pode conter DTLS fingerprints que, combinados com logs correlacionados, enfraquecem o forward secrecy.

FAQ — o que um dev realmente pergunta

Preciso do app desktop instalado para as chamadas funcionarem no browser?
Não. Pela nova arquitetura, o browser é um client independente com registro próprio no servidor. O app desktop deixou de ser pré-requisito.

Funciona em qual versão do navegador?
Qualquer navegador com WebRTC estável e suporte a getUserMedia moderno. Na prática: Chrome 90+, Firefox 90+, Safari 14+, Edge 90+. IE e antigos do Safari ficaram pelo caminho, como esperado.

As chamadas Web consomem mais bateria que o app nativo?
Em testes que rodei em campo, o consumo é 10% a 15% maior no browser por causa de overhead de codificação em software quando o codec H.264 hardware-accelerated não está disponível. Safari no iOS e Chrome em Macs com Apple Silicon fazem melhor uso do VideoToolbox.

Posso integrar com minha aplicação via API?
Não existe API oficial para chamadas no WhatsApp. Se você precisa de automação, vá de Telegram (TDLib), Twilio Voice/Video, Daily.co ou LiveKit. Cada um tem tradeoffs claros entre custo, latência e SDK.

Como a criptografia E2E sobrevive ao browser ter acesso ao DOM?
A engenharia é o uso de WASM com memória isolada e operações criptográficas determinísticas que não expõem plaintext no JavaScript. O WhatsApp usa libs baseadas em libsignal-portable compiladas para WASM, com chaves em IndexedDB criptografadas por uma master key derivada do usuário. Isso reduz superfície de ataque, mas não elimina: extensões maliciosas ainda são um vetor real.

A transferência de chamada entre devices funciona offline?
Não. Pelo menos um device precisa estar online no momento da transferência para negociar o handoff. O estado é propagado pelo servidor, não P2P entre devices.

Se você trabalha com comunicação em tempo real, esse lançamento do WhatsApp é um sinal claro: o mercado está forçando a portabilidade de chamadas E2E para o browser. Daqui pra frente, qualquer SaaS de comunicação sério vai tratar chamada no browser como feature de tabela, não diferencial. Vale estudar a fundo WebRTC, o protocolo Signal e padrões emergentes como WebTransport, que começa a ameaçar o domínio do WebRTC em chamadas de baixa latência.

Gostou? Me segue no GitHub e deixa um comentário se tiver dúvida ou quiser aprofundar algum ponto.

Y

Yuri Sousa

Front-End Developer / Designer

Desenvolvedor apaixonado por criar experiências digitais acessíveis e visualmente perfeitas. Escrevo sobre desenvolvimento web, design e tecnologia.