Mēxihco Tenochtitlan: sobrevoando 1507 no navegador
Um dia de 12 minutos em loop sobre Tenochtitlan, do amanhecer ao Fogo Novo de 1507, ao vivo em three.js: os dossiês, as legendas em náhuatl e a serpente do avesso.
Mēxihco Tenochtitlan é um dia de 12 minutos que roda ao vivo no navegador. Você voa no dorso de uma serpente emplumada sobre a capital asteca em 1507, do sol nascendo atrás do Iztaccíhuatl até a noite do Fogo Novo, quando todos os fogos do vale eram apagados e reacendidos a partir de um único morro. Se pegar os controles, dá para voar até os vulcões. Se soltar por quatro segundos, a serpente volta sozinha para a rota.
É a segunda demo da série, depois da Tupi. Mesmo esquema: uma conversa longa com o Claude Opus 5.5 no Claude Code, que escreveu a pesquisa, o código em three.js e os scripts de Blender, e rodou o loop de revisão. A integração começou em 30 de setembro de 2026. Foi ao ar em 4 de outubro.
O prompt
O briefing foram duas mensagens:
Outra demo legal seria um guerreiro asteca sobrevoando a capital asteca montado em Quetzalcoatl.
Vamos fazer uma demo só de Tenochtitlan, mas com o nome na língua náhuatl, e todas as legendas em náhuatl também, pode ser? Faça pesquisas históricas e use o mesmo mecanismo com Blender MCP + mint.gg. Tem que ficar realista e fabuloso. O difícil é que tem que ser em loop, e aí já não imagino como vamos fazer esse loop.
Depois vieram dois acréscimos: o usuário poderia conduzir a serpente e chegar perto da cidade, da mata e dos vulcões, e o tempo passaria, “da lua cheia até o sol da manhã e o entardecer”. Todos os pedidos seguintes estão na página de demos, no botão ✦ prompt do card de Tenochtitlan.
Cinco dossiês antes de qualquer geometria
A pesquisa saiu em cinco dossiês: a cidade, os personagens, a língua náhuatl, a iconografia e os relatos escritos de quem viu a cidade. Cada afirmação tem um nível de confiança (alta, média, baixa ou contestada), e as citações foram conferidas no texto original. O que não deu para conferir está marcado como não verificado. Algumas coisas que saíram de lá:
- O ano. Sahagún diz com todas as letras: “La última solemnidad que hizieron de este fuego nuevo fue el año de mil y quinientos y siete.” O Fogo Novo fechava um ciclo de 52 anos. O de 1507 foi o último celebrado em público, no reinado de Motecuhzoma II.
- O morro. O fogo era tirado no Huixachtécatl, o atual Cerro de la Estrella: a 11,1 km do Templo Mayor, no azimute 157°, 224 m acima do vale. As “dos leguas de México” de Sahagún batem.
- O céu. Os sacerdotes esperavam as Plêiades cruzarem o meridiano à meia-noite. Com a precessão de 1507, elas culminam a 3° do zênite naquela latitude. O módulo do céu aplica a precessão de 1507 ao catálogo de estrelas, então é ali que elas estão.
- O templo. Em 1507 o Templo Mayor estava na sexta etapa, ou na sétima em obra. A última etapa ficava numa plataforma de 84 × 77,2 m e chegava a uns 45 m com os dois santuários (López Luján 2001). Os famosos 114 degraus de Bernal Díaz são do templo de Tlatelolco, não deste.
- O que estava enterrado. O disco de Coyolxauhqui que todo mundo conhece já estava coberto por ampliações em 1507. O que ficava no pé da escadaria era o monólito de Tlaltecuhtli, de 4,17 × 3,62 m. A orientação para o build: Tlaltecuhtli à vista, Coyolxauhqui não.
- Onde a serpente pousa. O templo de Ehécatl-Quetzalcóatl era redondo, com 18 m de diâmetro, virado para o leste, em frente ao Templo Mayor. É o deus da conta de 52 anos, então é dali que o voo sai e é ali que ele pousa.
- Escala. Vistos da cidade, o Popocatépetl e o Iztaccíhuatl sobem só 2,3° a 2,7° acima do horizonte. O dossiê manda não aumentar: a sensação de “vulcão enorme” vem da altitude, e o voo chega lá.
- A grade. As ruas e o eixo do templo são girados uns 7,5° no sentido horário em relação ao norte (Šprajc 2000). O gerador da cidade usa essa rotação.
O achado mais útil foi de gramática. Bernal, do alto do templo de Tlatelolco, escreveu que viam “cues é adoratorios á manera de torres é fortalezas, y todas blanqueando… y las casas de azuteas”. O “todas blanqueando” concorda com os templos, não com as casas. Das casas ele só diz que eram “de azuteas”, de terraço. A primeira versão tinha pintado a ilha inteira de branco por causa dessa linha. Nos quadros de casa e canal, 33% a 45% dos pixels ficavam acima de 200 de luminância. Na reconstrução de Thomas Kole, menos de 1%. A reconstrução deixou a cal nova para templos e palácios e misturou adobe e reboco gasto no resto.
A noite exigiu uma decisão, não um fato. A astronomia do dossiê põe as Plêiades no meridiano à meia-noite em 5 ou 6 de novembro de 1507 (calendário juliano), perto da lua nova. O texto náhuatl põe a cerimônia na vintena de Panquetzaliztli, da metade ao fim de novembro, quando a lua estaria cheia. O briefing pedia lua cheia. A demo usa a data de Panquetzaliztli, e a nota “sobre a demo” registra que a noite exata é discutida.
As fontes, se você quiser conferir:
- Relatos de época:
- Sahagún, Historia general de las cosas de Nueva España (PDF, vol. 1), livros 2, 4, 7 e 12;
- Cortés, Segunda carta de relación (1520);
- Bernal Díaz del Castillo no Project Gutenberg, capítulos LXXXVIII e XCII.
- Arqueologia:
- Mapas e reconstruções:
- o mapa de Nuremberg de 1524 no Wikimedia Commons;
- o mapa “Basin of Mexico 1519”;
- Thomas Kole, A Portrait of Tenochtitlan, usado para conferência.
Legendas só das fontes
O briefing pedia o título e todas as legendas em náhuatl. Foi o pedido que mais me preocupou. Um modelo escreve náhuatl com cara de fluente sem nenhum esforço, e eu não sei ler náhuatl.
Por isso o dossiê de língua tem uma regra: nenhuma legenda é inventada. São 19 legendas no roteiro principal. Cada uma é citada de uma fonte com a grafia normalizada ou recortada de uma frase atestada sem mudar nenhuma palavra. As únicas adaptações são duas variantes opcionais, e cada uma tem um recorte puro como alternativa. As fontes são o Códice Florentino, conferido na transcrição de Anderson e Dibble no Digital Florentine Codex do Getty, a Crónica Mexicáyotl e Chimalpahin. As legendas do Fogo Novo saem todas do livro 7 do Códice Florentino, o relato nahua da própria cerimônia. Na tela, a legenda vem com a tradução e a fonte, por exemplo In izqui ohtli ihuan in izqui acalohtli, “todos os caminhos e todos os canais”, Códice Florentino, livro 12, capítulo 21.
O dossiê também descartou coisas:
- “Tenōchtitlan”. A grafia popular com ō longo está errada. Carochi marca as três primeiras sílabas como breves. O título é “Mēxihco Tenochtitlan”: ē longo e saltillo (a oclusiva glotal) em Mēxihco, nenhum mácron em Tenochtitlan.
- “Ayac quimati in Tenochtitlan”. Parecia uma boa epígrafe. O dossiê não achou atestação nas fontes consultadas e suspeita de confusão com outro verso dos Cantares mexicanos, então ficou de fora.
- A interface. Não existe náhuatl atestado para “carregando” ou “voltar à rota”, então a UI ficou em português e inglês.
A stack
- Motor. O mesmo da Tupi: three.js r186 em WebGPU com materiais TSL, fallback para WebGL2, Vite, e módulos que exportam
create(ctx)e devolvem{ update(t, dt) }. A rodada 0 integrou 16 deles sem nenhum erro: environment, terrain, lake, city, precinct, vegetation, people, fire, flight, serpent, rider, camera, birds, audio, overlay e post. As cidades da margem do lago entraram depois, como o 17º. - Terreno. Elevação do Copernicus GLO-30 no quadrado de 122,88 km em volta da cidade e GLO-90 no anel do horizonte. A cidade moderna sai com uma abertura morfológica de 210 m, e os lagos de 1507 são refeitos com um limiar sobre o leito drenado mais polígonos desenhados à mão para o dique, a ilha e os istmos.
- Céu. Sol, lua e precessão das estrelas pelas fórmulas do Astronomical Algorithms de Meeus, estrelas brilhantes do Yale Bright Star Catalogue e coeficientes de atmosfera de Bruneton e Neyret (2008) e Hillaire (2020).
- Cavaleiro. Mint, depois Rodin Gen-2, depois Blender: 180 mil triângulos de perto e 45 mil de longe, texturas 4K e um rig de seis ossos.
- Serpente. Um corpo procedural que segue o rastro da cabeça. A cabeça é uma geração do TRELLIS.2, a uns US$ 0,74 por execução.
- Superfícies. Scans CC0 do Poly Haven para reboco, adobe, basalto, palha e casca de árvore.
Fazendo um dia virar loop
O loop era a parte que eu não conseguia imaginar, e virou a regra mais simples do código: tudo é função pura de t. Céu, sol, lua, estrelas, fogo das casas e o cronograma do Fogo Novo leem só o relógio, nunca a posição da câmera. O piloto automático segue uma spline fechada cuja posição também é função de t. No voo livre, a serpente tem posição e rumo próprios, mas o relógio continua correndo. Quatro segundos depois que você solta, uma perseguição suave a leva de volta para onde o piloto automático estaria agora. Segurando Shift ela vai a 560 m/s, que é como se chega a um vulcão a 70 km.
A primeira versão fechava o loop em 360 segundos. Minha revisão foi “a passagem de tempo está rápida demais”. Dobrar o loop parece mudar uma constante, mas mexe em todo sistema que lê o relógio, e alguns deles não podem ficar mais lentos. A solução dividiu o tempo em dois:
- Um relógio do dia na metade da velocidade (
τ = t/2), que comanda o céu, o cronograma do fogo, as lareiras das casas e a agenda das pessoas. - Um período de movimento que continua em 360 segundos, para que caminhada, remada, água, vento e penas mantenham a velocidade.
Isso foi medido, não suposto. 400 pedestres tinham média de 1,1533 m/s antes e depois. 2.915 canoas, 1,2932 m/s antes e depois. A rota dobrou de 76,5 km para 152 km para a serpente voar na mesma velocidade sobre lugares novos: Iztapalapa, Coyoacán, Xochimilco, o lago de Chalco rumo aos vulcões. A emenda em que 719,9 s vira 0 muda a imagem em 7,34 na métrica da captura, contra uma média de 8,6 num passo comum de 0,1 s. Não dá para ver onde o loop fecha.
O Gauntlet, de novo
Reaproveitei o loop do FPS de navegador e da Tupi: builders em paralelo, críticos de contexto limpo que só veem pixels, um classificador “parece real?”, capturas A/B e um caçador de regressões. Escrevi sobre ele em Por dentro do Gauntlet loop.
O classificador deu 3,2 de 10 na primeira rodada. A Tupi tinha começado em 4,96 com o mesmo tipo de classificador. A rodada 1 melhorou quase tudo o que mediu: cal mais clara nos templos, hora azul de verdade, multidões subindo de L* 15 para 43, canoas indo de 2.700 para 9.000. A nota voltou 3,15. Empate.
O motivo estava nas notas dos críticos. A serpente aparecia em todos os quadros e era o defeito número um em todos eles. Enquanto o herói parecesse brinquedo, nenhum trabalho de céu ou de água mexia na média. Isso continuou verdade por um bom tempo.
A serpente estava do avesso
Da rodada 8 em diante, a serpente seguiu como defeito número um, e os sintomas voltavam sempre: uma “faixa preta” ao longo do corpo, um “tubo preto”, “a barriga vista de cima”. As rodadas 9 e 10 atacaram isso com material: escamas assadas no Cycles, parallax, atlas de penas, luz de contorno do céu e uma correção para um bug de iluminação do three.js r186 que ignorava o specularIntensity. Cada uma ajudou um pouco.
A rodada 12 achou a causa de verdade. A ordem dos índices em body.js montava os triângulos com as normais para dentro. Com o descarte de faces traseiras, o renderer jogava fora a parede de fora do corpo, e a câmera via o lado de dentro da parede oposta. Uma troca na ordem dos vértices corrigiu o defeito número um desde a rodada 8. E também mostrou que as calibrações de material das rodadas 9 e 10 tinham sido feitas sobre uma malha do avesso, então foram refeitas. O flanco na sombra subiu de L* 2 a 5 para 13 a 15.
A lição que ficou: quando todo crítico aponta o mesmo defeito rodada após rodada e ajuste de parâmetro quase não mexe nele, pare de ajustar e procure algo estruturalmente errado.
Tela preta por causa de um cache de dependências
A rodada 10 reescreveu o antialiasing temporal depois de descobrir que ele não acumulava nada. Quarenta quadros parados com e sem ele diferiam em 0,76. Parte da causa: o nó de posição anterior ignorava posições de vértice customizadas, então 5,6% da tela ficavam sem histórico válido.
O TRAA novo, de vez em quando, gerava uma captura preta do começo ao fim. A causa não tinha nada de renderização. O fork do TRAA importava um helper de three/addons que mais nada usava. O servidor de dev descobria essa dependência em tempo de execução, re-otimizava as dependências e trocava o hash. A página de captura ignorava o reload, e duas cópias de three/webgpu acabavam na mesma página. Um If de TSL lia uma pilha nula da cópia errada, o material falhava ao resolver e o three.js trocava por um material vazio. Reproduziu 2 de 2 vezes editando o arquivo com o servidor rodando. A correção pôs os helpers inline, criou um fallback que só repassa a imagem se o TRAA falhar e fez o script de captura refazer qualquer sessão com erro ou quadro preto.
Meça o instrumento
A rodada 8 também achou bugs na medição:
- O cronômetro de quadro era quantizado. O
frameMsseguia o requestAnimationFrame a 120 Hz, então só lia 8,3, 16,7 ou 25 ms. O custo real exigiu uma sonda de throughput à parte. - A GPU era compartilhada. Um protetor de tela Flurry e o Chromium do Playwright de outro projeto disputavam a placa com as capturas.
- As capturas não eram determinísticas. O mesmo código dava diferença de 9 a 13 entre carregamentos. A causa eram os contadores do aquecimento deslocando o jitter subpixel do TRAA. Depois da correção, dois carregamentos diferem em 0,01 a 0,03.
Antes dessa correção, qualquer diferença de antes e depois menor que uns 10 estava dentro do ruído.
O orçamento
Em 1º de outubro defini um teto de US$ 20 para gerações pagas, e cada execução entrava num ledger: modelo, id da predição, custo. A regra era parar e perguntar antes de passar do teto. Três clipes do Seedance entraram como régua visual de luz, névoa e desgaste, não como imagem do filme. Erraram de um jeito útil: monte Fuji, palmeiras, pirâmides egípcias, telhado de duas águas, escadaria única. Em 2 de outubro tirei o teto: “Não se preocupe com custo, e sim sempre com qualidade.”
Não tenho um total limpo desta demo como tive na Tupi. Cada execução do TRELLIS custa uns US$ 0,74. Os preços do Seedance e do Rodin não apareciam na API, e o ledger diz “a confirmar no painel”. Prefiro deixar o número de fora a chutar.
O que ainda está fraco
- A serpente. Mesmo depois da correção das normais, ela ainda era o primeiro item do classificador na rodada 12: “guirlanda listrada” de longe, “pilha de penas sem corpo” de perto.
- A noite. Os telhados ao luar ainda parecem, para os críticos, um dia nublado.
- Casas de perto. De longe a arquitetura convence. Perto dos telhados, ainda parece maquete.
Quando a fonte é ambígua, ponha a ambiguidade na obra: a noite exata do Fogo Novo e o vermelho do santuário de Huitzilopochtli estão marcados como contestados na nota “sobre a demo”, e a serpente vem rotulada como mito.
Abra Mēxihco Tenochtitlan num computador com WebGPU, espere o entardecer e pegue os controles.