DISPONÍVEL PARA VAGAS FULL-TIME ABERTO A CONTRATOS FREELANCE AI FULLSTACK ENGINEER · AGENTIC AI AGENT-READY · HUMANS WELCOME AI AGENTS · LLM TOOLING · EVALS NEXT.JS · TYPESCRIPT · REACT EM LISBOA · REMOTO PARA O MUNDO
← Todos os posts

Tupi: reconstruindo 1554 no navegador com 32 agentes de IA

Um ataque tupinambá de 1554 recriado em 3D no navegador com Claude Opus 5.5, three.js e Blender: o pipeline, o que quebrou e quanto custou.

Imagem de capa — Tupi: reconstruindo 1554 no navegador com 32 agentes de IA

Tupi é um filme de três minutos e meio que roda ao vivo no navegador. Uma frota de guerra tupinambá, em igaras de casca, sobe o canal da Bertioga ao amanhecer, por volta de 1554, rumo a uma aldeia tupiniquim. O filme é um loop: perto do fim começa uma garoa, um trovão ecoa na Serra do Mar, e o amanhecer seguinte abre o céu de novo.

Eu não escrevi o código. Dirigi, numa única conversa longa com o Claude Opus 5.5 no Claude Code. Ele escreveu o dossiê de pesquisa, o código em three.js, os scripts de Blender e as ferramentas de revisão, e comandou outros 31 agentes para o trabalho em paralelo. Este post conta como foi: o pipeline, o que resistiu e a conta.

Uma xilogravura do livro de Hans Staden, de 1557, ao lado de um quadro do filme

O prompt

Tudo começou com uma mensagem:

Clone o dgreenheck/tidewater. Ele fez um experimento visual muito bom com Opus 5.5 e eu quero fazer algo do mesmo nível, mas no Brasil pré-colonial: um tupinambá subindo um rio em território inimigo, ambientado em Pindorama. A ideia desta pasta é fazer várias demos retratando momentos históricos da humanidade com three.js e 3D. Acha que somos capazes não só de produzir esse nível de visual 3D no navegador, mas também de fazer a pesquisa histórica correta?

Tudo o que veio depois foi direção em rodadas, como quem revisa um corte de filme:

  • “as árvores da margem direita estão iluminadas demais”;
  • “a neblina está parecendo fumaça e tirando o vigor da mata”;
  • “tudo que tem pedra está bem low poly”;
  • “as montanhas da Serra do Mar eram pontudas assim mesmo?”;
  • “coloque caranguejos e animais do mangue”.

A sequência completa de pedidos está na página de demos: o botão ✦ prompt no card da Tupi.

Pesquisa antes da geometria

A primeira pergunta que fiz foi se conseguiríamos chegar no nível visual do tidewater do dgreenheck e da Sakura River Valley, do Meng To, e, ao mesmo tempo, acertar a história. Por isso a primeira entrega não foi uma cena, foi um dossiê com fontes: Hans Staden (1557), Jean de Léry (1578), Gabriel Soares de Sousa (1587), o dicionário de tupi antigo de Eduardo Navarro, Florestan Fernandes, Carneiro da Cunha e Viveiros de Castro.

Cada escolha visual remete a ele:

  • Canoas. As igaras são de casca, não troncos escavados. Staden conta que tiravam a casca da árvore numa peça só, aqueciam no fogo, dobravam as duas pontas e mantinham a canoa aberta com duas travessas. Cabiam trinta homens.
  • Pintura. Jenipapo (preto) e urucum (vermelho).
  • Cabelo. A tonsura: a frente da cabeça raspada, o cabelo comprido atrás.
  • Adornos. O enduape, uma roseta de penas de ema amarrada na base das costas, e o tembetá no lábio.
  • Fauna. Guarás cruzando o canal e tainhas saltando, porque Staden diz que os ataques vinham em agosto, “porque nesse tempo vão à caça de uma espécie de peixe”.

O dossiê também corrigiu o meu briefing, duas vezes. Eu tinha falado em “Pindorama”, que é uma invenção do século XIX. E a Serra do Mar dos primeiros renders era uma fileira de picos alpinos. Quando perguntei “as montanhas eram pontudas assim mesmo?”, a resposta foi não: é uma escarpa antiga e erodida, arredondada e coberta de mata. A serra do round 4 ao lado da final:

O round 4, com picos pontudos e um reflexo âmbar na água, ao lado da Serra do Mar arredondada da versão final

O dossiê completo é público, com um nível de confiança em cada afirmação: research.md. Para ler as fontes você mesmo:

A stack

  • Motor. three.js r186 em WebGPU com materiais de nós TSL, com fallback para WebGL2.
  • Módulos. São 14: céu, água, terreno, mangue, mata, frota, aves, fauna, fumaça, áudio, câmera, ambiência, overlay e pós-processamento. Cada um tem um dono e o mesmo contrato: create(ctx) devolve { update(t, dt) }. Um módulo que quebra é registrado e pulado, então uma parte com defeito nunca apaga o filme inteiro.
  • Pós-processamento. TRAA, god rays próprios, bloom, exposição automática, color grade e grão.
  • Câmera. Gira quando você arrasta e volta sozinha para o trilho cinematográfico quando você solta, como na Sakura River Valley do Meng To. A chuva perto do fim do loop foi a outra ideia que peguei de lá.
  • Assets.
    • Scans CC0 do Poly Haven para pedras, falésias, lama e raízes.
    • Espécies da Mata Atlântica que não têm scan (juçara, jerivá, embaúba, ipê, figueira, aninga, caeté), geradas no Mint e reconstruídas no Blender com LODs e impostores.
    • Gravações de campo, só CC0 e CC-BY. Os remadores ficam em silêncio.

As pessoas foram o mais difícil

Se alguém pergunta qual detalhe histórico deu mais trabalho, a resposta honesta é: as pessoas. As fontes são precisas, e é no ser humano que o vale da estranheza pega.

A primeira tentativa foi um gerador de modelos 3D por IA, e deu dois problemas:

  • Ele recusava. Qualquer prompt que descrevesse as pessoas com precisão, e elas usavam muito pouca roupa, esbarrava na moderação.
  • Detalhe. O que ele gerava servia para uma canoa a cinquenta metros e ficava ruim de perto: uma malha com cara de boneco.

Então os corpos vêm do MakeHuman, gerados por script no Blender, sem interface, pelo add-on MPFB2. Tudo o que é histórico é construído em código por cima deles:

  • um shader de pele;
  • máscaras procedurais de jenipapo e urucum;
  • a tonsura feita com cartões de cabelo;
  • o enduape, os cordões, as braçadeiras e o tembetá;
  • um rig de remada.

Um rosto gerado por IA ao lado de um rosto do MakeHuman, e o rosto do MakeHuman com jenipapo, urucum e tonsura

Um guerreiro do MakeHuman no lookdev, com pintura corporal, cordões, adornos de penas e o tembetá

Ainda é a parte menos convincente do filme. Na distância da remada, os remadores passam por gente; de perto, parecem manequins bem pintados.

O Gauntlet

Um agente sozinho produz um resultado razoável e para, porque é ele mesmo quem julga e conhece a razão de cada escolha que fez. Então reaproveitei o loop do meu FPS de navegador: o Gauntlet.

  • Construtores. Rodam em paralelo, cada um dono dos seus arquivos.
  • Críticos. Começam com contexto limpo e só veem os pixels renderizados, nunca a explicação de quem construiu.
  • Classificador. Um “isso parece real?” dá nota a 42 quadros do filme a cada rodada.
  • Capturas A/B. Conferem cada correção que alguém diz ter feito.
  • Caçador de regressões. A única missão dele é achar o que piorou.

Rodei o loop sozinho, durante uma madrugada. A média do “parece real” subiu de 4,96 para 5,46 de 10 em 13 rodadas. É um número pequeno, e honesto. A maior parte do ganho foram defeitos que deixaram de aparecer, não notas que subiram:

O round 4, com brilho na água e névoa chapada, ao lado do quadro final

Três lições do loop:

  1. Um construtor dizer “corrigido” não é prova. Várias correções estavam erradas, e só a captura A/B mostrou.
  2. Bug de captura se disfarça de bug de cena. Por um tempo os remadores pareciam transparentes. Não era bug de renderização: a exposição automática, a névoa e o contraste juntos estavam apagando os corpos. Outra vez, um detector de “quadro ladrilhado” estava lendo um canvas WebGPU vazio em vez do screenshot. Meça primeiro a ferramenta de medir.
  3. A causa raiz raramente está onde parece.
    • As “pedras low poly” eram scans de verdade descartados em silêncio por um bug de temporal dead zone. O que aparecia na tela eram malhas de falésia esticadas na vertical.
    • As “golas de espuma” nas pedras eram a névoa rasteira.
    • Uma cruz amarela no céu era uma libélula a um centímetro da lente.

”Travou na tela de carregamento”

O primeiro relato do mundo real veio de um amigo, num PC de casa mais simples: o navegador travou na tela de carregamento. Medi:

  • Onde ia o tempo. O primeiro quadro montava quase todos os shaders da cena numa tarefa só, 5,2 s de thread principal bloqueada até num Mac M-series.
  • Por que travava. Num PC com Windows, onde o driver compila shaders bem mais devagar, isso dura o bastante para parecer travado.
  • Onde estava a barra de progresso. Já em 100%.

As correções, todas medidas antes e depois:

  • Aquecimento dos shaders. Os objetos entram poucos por vez atrás da tela de carregamento, e cada passo espera a GPU. Nenhuma tarefa dura o suficiente para travar o navegador.

  • Pré-cálculo no build. O ruído do terreno, o skinning dos adornos dos remadores e a busca de posições dos bichos eram determinísticos, mas rodavam no navegador de cada visitante. Agora são calculados uma vez no build e vão como arquivos binários.

    O quêNo carregamento, antesDepoisDados pré-calculados
    Terreno2,8 s0,18 s6,8 MB
    Skinning dos adornos1,67 s0,39 s0,6 MB
    Posicionamento dos bichosuma trava de 3 s0,09 s0,1 MB

    Pré-calculado contra calculado na hora: diferença média de 0,00 nos pixels de 12 quadros.

  • Níveis por aparelho.

    • GPU integrada, até 4 GB de RAM ou até 4 núcleos, e celulares recebem uma versão leve.
    • Renderização por software recebe o filme gravado no lugar da cena ao vivo.
    • Um carregamento que travou de verdade faz a visita seguinte cair um nível.
  • Abertura com a cena. A tela de carregamento mostra um loop curto da canoa atrás do título, então há o que ver enquanto os shaders compilam.

Uma ideia deu errado, e deixo aqui porque é a parte útil. Tentei carregar a mata e o mangue depois que o filme começava. O carregamento ficou mais rápido, mas no site no ar o filme passou a engasgar por uns 20 s enquanto os shaders compilavam no meio das cenas. Compilar de forma assíncrona acabou com o engasgo, mas aí as árvores apareciam 40 s atrasadas. A melhor troca foi a menos esperta: a mata compila antes, atrás da tela de carregamento, e só os bichos, os guarás e a fumaça, que aparecem depois do primeiro minuto, carregam em segundo plano.

Resultado num Mac M-series: carregar os módulos caiu de 4,9 s para 1,5 s, e o filme começa em ~7,7 s, sem engasgos depois. Antes eram 11–16 s, seguidos de 20 s engasgando.

Caranguejos e garças

Depois que a parte de desempenho entrou no ar, veio a revisão seguinte: “no close dos caranguejos eles estão totalmente low poly, e tem uma ave branca de cabeça preta que não dá para identificar”. As duas coisas eram provisórias: um caranguejo procedural com casco de oito lados e pernas de palito, e um savacu montado como uma bola branca com uma bola preta em cima. As espécies estavam certas; os modelos, não.

Foram trocados por gerações do Mint, recortadas no Blender:

  • Um script divide cada caranguejo em casco, garras e pernas, então eles andam com pernas articuladas.
  • A garra grande do chama-maré acena de verdade. As fêmeas, com duas garras pequenas, são novas.
  • O savacu vira a cabeça e muda o peso de perna.

Antes e depois: o caranguejo procedural e o novo uçá

Antes e depois: as garças procedurais e os novos socó-grande e savacu

Quanto custou

ModeloClaude Opus 5.5, 1 orquestrador + 31 subagentes
Chamadas~3.770
Tokens~1,29 bilhão processados, a maioria leitura de cache; ~0,7 milhão gerados
Preço equivalente na API≈ US$ 516 a preço de tabela
Outras IAs~US$ 6 em créditos do Mint para as espécies de árvores
Tempo de relógio~22 horas do primeiro prompt ao primeiro build público, com uma madrugada rodando sozinho

O trabalho de desempenho e dos bichos, depois, somou mais cinco agentes. O custo é dominado pela leitura de cache: 32 agentes relendo um projeto grande muitas vezes. Em tokens, gerar o código foi a parte barata. Conferir foi a parte cara, e foi também o que tornou o resultado confiável.

O que ainda está fraco

  • Os remadores. Corpos com cara de real e adornos históricos são o problema mais difícil daqui. Se eu for além, o próximo passo é um pipeline de MetaHuman ou fotogrametria.
  • Taxa de quadros. Não é 60 fps cravado. Num Mac M-series ocupado fica em uns 40–45 fps, com a resolução dinâmica trabalhando bastante.
  • Compilação dos shaders. Ainda é a maior parte do carregamento. O próximo passo é juntar em shaders compartilhados os materiais que só mudam nos parâmetros.

Abra a Tupi num computador com WebGPU e ligue o som. O prompt que a construiu está na página de demos.