---
title: "Mēxihco Tenochtitlan: sobrevoando 1507 no navegador"
description: "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."
language: "pt-BR"
published: "2026-10-06T00:00:00.000Z"
tags: ["threejs", "webgpu", "ai-agents", "blender", "history"]
source: "https://www.rubenmarcus.dev/pt/blog/sobrevoando-tenochtitlan-no-navegador"
translation: "https://www.rubenmarcus.dev/blog/flying-over-tenochtitlan-in-the-browser"
---

[Mēxihco Tenochtitlan](/demos/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](/pt/blog/reconstruindo-1554-no-navegador). 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.

<img src="/art/blog/tenochtitlan/nuremberg-vs-render.webp" alt="O mapa de Tenochtitlan impresso em Nuremberg em 1524 ao lado de um quadro da ilha na demo" loading="lazy" />

## 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](/pt/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)](https://www.biblioteca-antologica.org/es/wp-content/uploads/2018/05/SAHAG%C3%9AN-Historia-General-de-la-Nueva-Espa%C3%B1a-I.pdf), livros 2, 4, 7 e 12;
  - [Cortés, *Segunda carta de relación* (1520)](https://revistavoces.net/wp-content/uploads/2016/02/cortes_hernan-cartas_de_relacion.pdf);
  - [Bernal Díaz del Castillo no Project Gutenberg](https://www.gutenberg.org/ebooks/64945), capítulos LXXXVIII e XCII.
- **Arqueologia:**
  - [López Luján, "Tenochtitlán: Ceremonial Center" (2001)](https://www.mesoweb.com/es/articulos/sub/CeremonialCenter.pdf);
  - [López Luján et al., "Línea y color en Tenochtitlan"](https://www.mesoweb.com/es/articulos/sub/Linea-y-color.pdf);
  - [INAH sobre o templo de Ehécatl-Quetzalcóatl](https://lugares.inah.gob.mx/en/node/5908);
  - [Šprajc, "Astronomical alignments at the Templo Mayor" (2000)](https://journals.sagepub.com/doi/10.1177/002182860003102502).
- **Mapas e reconstruções:**
  - [o mapa de Nuremberg de 1524 no Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Map_of_Tenochtitlan,_1524.jpg);
  - [o mapa "Basin of Mexico 1519"](https://commons.wikimedia.org/wiki/File:Basin_of_Mexico_1519_map-en.svg);
  - [Thomas Kole, *A Portrait of Tenochtitlan*](https://tenochtitlan.thomaskole.nl/), 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.

<img src="/art/blog/tenochtitlan/dawn-to-new-fire.webp" alt="Quatro quadros do loop: o sol nascendo sobre o lago, o recinto sagrado de manhã, a cidade no entardecer com os fogos se apagando e o Fogo Novo no morro à noite" loading="lazy" />

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](/pt/blog/construindo-um-fps-de-navegador-com-agentes-de-ia) 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](/pt/blog/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.

<img src="/art/blog/tenochtitlan/serpent-inside-out.webp" alt="O mesmo quadro antes e depois da correção: à esquerda o corpo do avesso parece um tubo preto chapado, à direita o flanco com escamas aparece iluminado" loading="lazy" />

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 `frameMs` seguia 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](/demos/tenochtitlan) num computador com WebGPU, espere o entardecer e pegue os controles.
