O que você precisa saber antes de criar um jogo para dois jogadores no navegador
A maioria dos tutoriais que você encontra na internet começa falando sobre tecnologia, bibliotecas e frameworks. Ninguém menciona que o problema real é fazer duas pessoas em computadores diferentes jogarem ao mesmo tempo sem que uma delas fique olhando a tela travada enquanto o outro move as peças. Isso é o cerne de qualquer jogo de dois navegador. O resto é detalhe. No início, você decide se usa WebSockets ou server-sent events. WebSockets é o caminho mais direto para comunicação bidirecional em tempo real. O servidor mantém uma conexão aberta com cada cliente e encaminha os dados na hora. Server-sent events funciona melhor se só um lado precisar enviar informações o tempo todo, mas para um jogo onde ambos os jogadores precisam se ver movendo ao mesmo tempo, essa opção não serve.
Como configurar um jogo de dois navegador do zero
Você precisa de três partes funcionando junto: um servidor backend, um canvas ou DOM no frontend, e um sistema de sincronização que mantenha ambos os jogadores no mesmo estado do jogo. Vou explicar na ordem que eu realmente uso, não na ordem dos livros. O servidor é a parte mais crítica. Eu recomendo Node.js com a biblioteca Socket.io porque ela lida automaticamente com reconexões, fallback para HTTP long-polling quando WebSocket é bloqueado por proxy, e permite namespace de salas sem muita configuração. Um servidor mínimo que gerencia salas de jogo leva cerca de 80 a 120 linhas de código, dependendo da complexidade das regras. A lógica do jogo em si deve rodar no servidor, nunca no navegador do cliente. Se você confiar no cliente para validar movimentos, alguém vai burlar o jogo em cinco minutos.
Eu já perdi dois dias inteiros debugando um jogo de tabuleiro porque um jogador conseguia enviar mensagens de Movimento mais rápido do que a taxa de atualização do servidor. O servidor aceitava, atualizava, e o outro jogador via o peão atravessar três casas de uma vez. A solução foi implementar um sistema de rate limiting no lado do servidor que rejeita pacotes duplicados dentro de um intervalo de 200 milissegundos. Isso resolveu o problema imediatamente. Depois dessa experiência, eu nunca mais permito que o cliente seja a fonte única da verdade sobre o estado do jogo. No frontend, o canvas é mais performático do que manipular elementos DOM para jogos com muitos objetos em movimento. Se o seu jogo tem menos de dez elementos visuais se movendo na tela, DOM com CSS transitions funciona bem e é mais fácil de desenvolver. Canvas exige mais código inicial mas escala muito melhor. Eu comecei sempre com canvas, mesmo para jogos simples, porque depois de gastar tempo migrando, percebo que valeu a pena.
O sistema de sincronização é onde a maioria dos projetos trava. Você tem duas opções principais: lockstep e state synchronization. No lockstep, cada jogador envia seus comandos para o servidor, o servidor aplica todos os comandos recebidos naquela frame e transmite o novo estado para ambos. Funciona bem para jogos de ritmo rápido como RTS, mas exige que ambos os jogadores estejam na mesma taxa de frames. Se um tiver 30fps e o outro 60fps, o mais lento dita o ritmo e o mais rápido fica esperando, o que gera uma sensação de lentidão. No state synchronization, o servidor mantém o estado completo do jogo e envia atualizações apenas quando algo muda. É mais simples de implementar e tolera melhor variações de fps entre os jogadores. Para a maioria dos jogos casuais de dois jogadores, o state synchronization é a escolha mais segura. Eu uso ele praticamente em tudo agora.
👉 Clique no botão abaixo para saber mais sobre o assunto!
Para o lobby e criação de salas, você pode usar uma solução simples com código de sala de seis caracteres gerado aleatoriamente. O jogador A cria uma sala e recebe o código. O jogador B entra com o código. O servidor vincula ambos ao mesmo namespace e o jogo começa. Se um dos jogadores desconectar, o servidor envia um evento de disconnect e o outro jogador recebe uma notificação. É básico, mas funciona na prática. Uma coisa que quase ninguém menciona em tutoriais é o problema do timestamp. Quando o jogador A clica em um botão, o comando chega ao servidor com um delay de rede que varia entre 50 e 200 milissegundos dependendo da localização geográfica de cada jogador. Se você mostrar o movimento no momento em que o comando chega, o jogador B verá o movimento do A com um atraso perceptível. A solução é usar o timestamp do cliente no momento do clique e calcular o delay de rede com um ping periódico. O servidor então aplica o movimento no tempo correto, não no tempo de chegada. Isso melhora significativamente a sensação de responsividade, mesmo com latência alta.
Outro ponto que causa problemas é a gerência de sessões. Se o jogador fecha o navegador sem desconectar corretamente, o servidor continua achando que ele está conectado. Eventualmente o servidor detecta pelo heartbeat que a conexão caiu, mas isso leva alguns segundos. Nesse meio tempo, o jogo fica em estado inconsistente. Eu resolvi isso implementando um timeout de 10 segundos após o último heartbeat. Se o jogador não enviar nada nesse período, o servidor considera a desconexão e avisa o outro jogador. Nada pior do que ficar jogando contra alguém que já tinha fechado a aba há meia hora. Para deploy, hospedar o servidor em um VPS barato resolve. Você não precisa de infraestrutura complexa no começo. Um servidor com 1GB de RAM e 2 vCPUs aguenta dezenas de salas simultâneas para jogos simples. O custo mensal fica entre 5 e 10 dólares. Se o projeto crescer, aí sim você pensa em balanceamento de carga e Redis para compartilhar estado entre instâncias do servidor.
Armadilhas que eu já cometi
A primeira vez que fiz um jogo de dois navegador, eu tentei usar apenas o frontend com Firebase Realtime Database como bridge entre os jogadores. Funcionou até eu testar com dois jogadores em cidades diferentes. O Firebase tem latência variável e não oferece garantia de ordem de chegada dos eventos. O jogo ficou completamente inconsistente. Mover para o servidor próprio resolveu, mas custou uma semana de trabalho reverso. A segunda armadilha foi não considerar mobile. Navegadores de celular têm comportamentos diferentes com WebSocket, alguns bloqueiam conexões quando a aba sai do foco, e a experiência de toque exige controles diferentes do mouse. Eu adiei o suporte mobile para depois e depois precisei refazer boa parte da interface. Se você vai lançar um jogo de dois navegador, pense no mobile desde o início, mesmo que não seja prioridade imediata.
O problema mais chato que eu encontrei foi com CORS e cookies de sessão. Quando o frontend e o backend estão em domínios diferentes, o navegador faz uma pré-validação OPTIONS para cada requisição WebSocket, e se o servidor não responder corretamente com os headers de permissão, a conexão falha silenciosamente. O erro mais comum é esquecer de configurar o allowCredentials como true e o allowOrigin com o domínio exato do frontend. Um asterisco no allowOrigin com credenciais habilitadas quebra a conexão. Eu levei horas para identificar isso porque o erro não aparece no console de forma óbvia. Se o seu objetivo é lançar algo rápido sem escrever servidor, existem alternativas como PlayFab da Microsoft ou Nakama da Heroic Labs que oferecem infraestrutura multiplayer pronta. Elas reduzem o tempo de desenvolvimento em cerca de 40%, mas cobram conforme o número de conexões simultâneas e ficam caras rapidamente. Para um projeto pequeno e pessoal, fazer do zero ainda é mais barato e dá controle total. Para um projeto comercial com expectativa de muitos jogadores, as plataformas prontas valem o investimento.
O mercado de jogo de dois navegador é maior do que parece. Jogos como Agar.io e Slither.io provaram que experiências simples em tempo real funcionam bem nesse formato. A barreira de entrada é baixa porque não precisa de instalação, mas a concorrência também é alta. O diferencial geralmente está na qualidade da rede, na fluidez da sincronização e na simplicidade das regras. Três dessas coisas funcionando bem são mais importantes do que cinco funcionando razoavelmente.