Mais rápido para quem chega pelo celular
Nós, da AI Maker Lab, construímos nosso simulador de direção no navegador, Seat Time, com agentes de programação por IA. Na semana de 19 de outubro de 2026, abrir a garagem exigia baixar 2,10 MB de JavaScript com gzip. Depois, o navegador ainda precisava analisar 7,9 MB de JavaScript. Já tínhamos motores, chuva, pistas de kart e um volante conversando com o navegador. Mas o celular precisava passar pela entrada primeiro.
Queríamos abrir essa porta mais cedo. Quando pedimos aos agentes que examinassem os mapas de código-fonte, os textos de tradução dominaram o pacote do jogo. O mapa atribuía 7,4 MB de código-fonte bruto às mensagens, contra 1,4 MB à interface inteira. São quantidades do mapa, não tamanhos de download comprimido. Elas apontaram para um começo menos óbvio do que os carros.
Primeiro, precisávamos enxergar o carregamento
Adicionamos eventos de etapas no Google Analytics 4, da visualização da página até o primeiro comando do jogador. Isso tornou a sequência observável. Não produziu uma comparação de antes e depois sobre quanto tempo as pessoas ficavam. Essa comparação continua ausente dos registros que preservamos.
No build, medimos o módulo do jogo e tudo que seus imports estáticos traziam junto. Chamamos esse conjunto de fechamento estático. Olhar só o arquivo de entrada esconderia as dependências. Olhar só os bytes comprimidos esconderia outro custo: analisar e executar o código após o download. A explicação do web.dev sobre divisão de código descreve essa diferença.
Sete mil mensagens vieram de carona
Três referências mantinham cerca de 7.700 mensagens no pacote. Duas repassavam a coleção inteira; outra escolhia uma mensagem por uma chave calculada. Nossa interpretação dos mapas foi que essas referências impediam a remoção das mensagens não usadas. Uma conveniência pequena no código tinha virado um download grande.
Referências explícitas reduziram o total mantido para 2.508 mensagens. Só essa mudança levou o conjunto comprimido de 2.102 para 1.565 KB. Depois, passamos 29 painéis condicionais para carregamento sob demanda, ao abri-los. Com as duas mudanças, chegamos a 1.370 KB: cerca de um terço a menos em gzip.
Os painéis continuaram no jogo. Mudamos quando o código chegava, sem retirar suas funções. O Vite documenta os imports dinâmicos, mecanismo que permite esse carregamento adiado. Também adicionamos uma verificação contra o repasse da coleção inteira de mensagens. A correção precisava sobreviver à próxima função auxiliar conveniente.

Metade dos dados, sem mover as posições
Os arquivos dos carros ofereciam outro corte, mas nossa primeira tentativa passou do limite. Quantizar significa reduzir a precisão usada para representar valores. Na tentativa, falharam 15 verificações de contrato, envolvendo raios de rodas, planos de espelhos e faixas dos mostradores. Um modelo menor não compensava alterar silenciosamente a geometria protegida por elas.
Descartamos aquela quantização de posições. O resultado mantido, com meshoptimizer, preservou cada posição de vértice bit a bit. Já as normais, direções usadas no sombreamento, foram arredondadas para uma grade de 2⁻¹⁰. Os 26 arquivos passaram de 21.419.496 para 10.989.652 bytes. É aproximadamente metade do peso dos arquivos, não metade dos vértices dos carros.
A ressalva importa: as posições ficaram exatas; nem todos os atributos ficaram. A documentação do meshoptimizer trata quantização como uma escolha de precisão separada. Também registramos que a produção servia esses modelos binários sem gzip ou Brotli. Portanto, reduzir os arquivos atacava bytes realmente transferidos, em vez de contar com uma compressão de transporte presumida.
Cada som passou a chegar conforme sua função
O áudio também estava embarcando cedo demais. O conjunto buscado pelo carregador antigo ao começar a dirigir pela primeira vez somava 13,96 MB: 12,16 MB de efeitos e 1,80 MB de motor. Mudamos o carregamento por função, para não buscar tudo de uma vez. Também derivamos arquivos Opus comprimidos a partir de fontes WAV aprovadas.
A nova medição no navegador foi de 0,97 MB ao começar a dirigir pela primeira vez. O comparativo junta o conjunto antigo calculado com o novo total medido; não são duas capturas equivalentes no navegador. Nos 155 arquivos convertidos, a coleção passou de 25,3 para 3,1 MB. Oito loops contínuos permaneceram em WAV.

Mantivemos uma ligação explícita de aprovação entre cada arquivo derivado e sua fonte. Um Opus sem registro de derivação correspondente não podia tocar. Essa era nossa regra de projeto, não um resultado de escuta. O padrão Opus define o codec; ele não prova que nossos arquivos soem idênticos. Não há um novo veredito de escuta registrado para essa mudança de entrega.
O jogo mal lia o manifesto
O manifesto dos ambientes carregava mais informação do que o jogo precisava em execução. Nós o substituímos por uma projeção de runtime: um documento menor, com os campos efetivamente usados. O tamanho bruto caiu de 3.337 para 962 KB. Nessa comparação, o conjunto comprimido passou de 1.374,0 para 1.223,4 KB, economizando 150,6 KB.
Essa base difere um pouco do retrato anterior de 1.370 KB. Mantivemos as comparações separadas, sem forçar uma soma artificial. Para nós, o padrão estava ficando claro: o primeiro carregamento levava dados úteis em algum lugar, mas dispensáveis naquele momento. Auxiliares de tradução, painéis fechados e metadados de autoria chegavam juntos à garagem.
Começar no clique rendeu menos do que esperávamos
Depois, iniciamos o download dos pacotes do jogo no clique, junto com a solicitação dos dados do servidor. Os pacotes começavam por volta de 370 ms, antes da resposta dos dados, em cerca de 480 ms. Isso tirou uma espera entre tarefas, sem mudar tudo que ainda precisava chegar.
O ganho foi modesto com Slow 4G emulado e processador desacelerado em quatro vezes. A mediana do carregamento completo passou de 15,85 para 15,23 segundos. Na navegação pelo cliente, passou de 13,75 para 13,47 segundos. Arredondando, os ganhos foram de 0,6 e 0,3 segundo, respectivamente. A documentação de limitação de rede do Chrome descreve a emulação; não são medições nos celulares dos visitantes.
Mantivemos a mudança, mas ela não fez desaparecer o download restante. Cerca de 1,9 MB ainda precisava chegar antes de a garagem ficar utilizável. Começar antes ajudava. Enviar menos continuava sendo a tarefa maior, ainda incompleta.
O problema dos imports voltou no servidor
O servidor tinha sua própria versão do trabalho desnecessário chegando cedo demais. Entre 22 e 25 de outubro, três incidentes repetiram esse formato. As consequências foram diferentes: um deploy recusado, uma publicação bloqueada e uma falha que chegou aos jogadores. Chamar os três de quedas apagaria as proteções que barraram dois deles.
Em 22 de outubro, a Cloudflare recusou um deploy com o erro de inicialização 10021. A produção permaneceu na versão anterior. A validação de itens cosméticos atravessava um manifesto do renderizador até a física dos carros, só para obter seus identificadores. Importar essa cadeia construía as especificações dos carros na inicialização. A documentação de startup da Cloudflare relaciona esse erro ao excesso de trabalho no escopo global.
Em 24 de outubro, uma verificação de inicialização pegou o catálogo de pistas entrando novamente no servidor. Dessa vez, a validação de voltas queria metadados das pistas. Uma tabela simples de comprimentos dos circuitos substituiu a dependência e retirou o caminho até o catálogo. A publicação foi bloqueada antes de a versão chegar aos jogadores.
Em 25 de outubro, a falha chegou aos jogadores. Instâncias novas do servidor gastavam de 4 a 6 segundos de processamento nas consultas de competição autenticadas. O tempo decorrido ficava entre 7 e 17 segundos. Requisições em instâncias já aquecidas levavam cerca de 20 ms. A tela de desafios não conseguia carregar a disponibilidade das rodadas. O perfil local apontou a construção dos dez circuitos ao carregar módulos; a tarefa agendada da competição também excedeu a memória.

O timeout não podia interromper o trabalho
Nossos prazos não estavam em posição de salvar essa falha. O trabalho dos módulos era síncrono: ocupava o ciclo de eventos que também executaria os temporizadores. Nem o orçamento de abertura de 750 ms nem o prazo de leitura de oito segundos conseguiam interrompê-lo. Esperar pelo timeout ainda significava esperar a construção dos circuitos terminar.
Separamos identificadores leves da geometria, passamos a construir circuitos só quando necessários e armazenamos assinaturas pré-calculadas. Assim, não era preciso reconstruir pistas para obter essas assinaturas. Verificações passaram a proteger a fronteira dos imports e detectar mudanças nas assinaturas. A importação a frio do endpoint de competição caiu de 16,4 para 0,5 segundo no perfil local. As geometrias e 24 assinaturas da classificação permaneceram idênticas byte a byte. Não temos medição preservada do processamento em produção após a correção; o resultado local não comprova o desempenho hospedado.
Havia outras portas menores para destravar
Um erro de “armazenamento indisponível” era, na verdade, localStorage cheio. Movemos as voltas fantasma para IndexedDB e uma representação binária: cerca de 180 kB por volta, em vez de aproximadamente 1,3 MB de texto. O fantasma é a volta gravada contra a qual você pode correr. O guia de armazenamento da MDN diferencia Web Storage, restrito a texto, dos armazenamentos maiores gerenciados pelo navegador. IndexedDB também tem cotas; a mudança não tornou o espaço ilimitado.
No servidor, uma leitura em lote das regras de acesso substituiu duas transações interativas. Também ativamos Workers Logs depois de erros que a observação ao vivo não tinha capturado. Essas mudanças tratavam partes diferentes da espera. Não as somamos em um ganho universal de velocidade, nem afirmamos que a semana melhorou a retenção.
A garagem ainda tinha peso a perder
Terminamos esse trabalho com downloads menores e uma explicação mais clara para a falha do servidor. Restaram limites: cerca de 1,9 MB até a garagem utilizável, nenhuma comparação de retenção e nenhum resultado registrado do processamento em produção após a correção final dos imports. Os totais menores mostram o que mudou. Não mostram quanto tempo alguém decidiu ficar.
Abra a garagem pelo celular. Em seguida, queríamos que o volante contasse a verdade sobre a aderência.
Anterior: um volante em uma aba do navegador · Próximo episódio: aderência é a direção ficando leve
Notas e fontes
Medido — registros de builds do navegador, de 19 a 25 de outubro de 2026: 2,10 MB gzip e 7,9 MB para análise; atribuição de 7,4 MB às mensagens e 1,4 MB à interface no mapa de código-fonte; retratos do conjunto comprimido e contagens de mensagens. Os totais de fonte e de código emitido descrevem representações diferentes. A comparação posterior do manifesto tem base própria. São observações nossas preservadas, não novas medições.
Medido — registros de assets: 26 modelos e os totais de bytes acima; 15 verificações de geometria que falharam; posições exatas e normais arredondadas. O áudio anterior é calculado pelo conjunto de downloads; o posterior foi medido no navegador. Valor de projeto escolhido — áudio derivado precisa corresponder a uma fonte aprovada; oito loops permanecem WAV. Sem veredito de escuta registrado — essa mudança de entrega não é uma comparação de qualidade sonora.
Medido, emulado — os tempos do início no clique usam Slow 4G e desaceleração do processador em quatro vezes. Medido, produção — as observações de requisições frias e aquecidas são de 25 de outubro. Somente perfil local — a importação de 16,4 para 0,5 segundo e a comparação de geometrias e assinaturas não comprovam aceitação hospedada. Nossa interpretação — retenção de mensagens e trabalho síncrono explicam os problemas de dependência. Fato documentado em nossos registros de entrega — os incidentes anteriores barraram o deploy e a publicação, respectivamente. Não há resultado preservado do processamento em produção após a correção.
Medido — os tamanhos das voltas fantasma são aproximados. Fato documentado em nossas notas de implementação — instrumentação das etapas, leitura em lote e logs. Referências públicas explicam mecanismos, não nossos resultados: divisão de JavaScript, imports no Vite, meshoptimizer, Opus, emulação de rede, inicialização de Workers e armazenamento no navegador. Consultadas em 6 de novembro de 2026. Os nomes identificam ferramentas, não endossos. IA auxiliou na síntese da pesquisa, redação e localização deste artigo.