Caso 06Site interativo em Three.js

Portfólio 3D

Meu site pessoal é uma nave que se pilota no navegador. Foi escrito com agentes de código, e cada mudança visual ficou guardada numa captura.

Ver o voo
Meu papel
Conceito, direção de arte e aprovação de cada tela. O código é do agente.
Período
Desde 8 de julho de 2026, 165 commits antes destas páginas
Situação
No ar, é este site
Stack
Next.js 16, React Three Fiber, Three.js e CSS Modules

Em 30 segundos

A home é uma cena 3D em WebGL. Quem visita pilota uma nave, atira em meteoros e se teletransporta entre as seções. Os projetos ficam num cenário com telões, piso refletor e um planeta ao fundo.

Trabalho visual é onde "ficou bom" não se prova com teste. A resposta foi um processo: mockup aprovado antes do código e captura guardada depois.

978
capturas no histórico visual, em 11 dias de trabalho
13
rodadas de mockup, com as telas recusadas guardadas
0,2 s
do clique ao primeiro telão da galeria. Eram 2,3 s
100
em acessibilidade, boas práticas e SEO. Desempenho no celular: 42

Um portfólio que se pilota

O primeiro objetivo do SPEC é "Atrair oportunidades de emprego". O segundo, "Impressionar recrutadores, contratantes e pessoas técnicas". Você é quem decide se funcionou.

Trinta segundos no siteO site rodando com GPU real, filmado sem som.

WASD ou as setas pilotam, espaço ou clique atira, e 1, 2 e 3 levam às seções. No celular, viram um joystick e um botão de tiro. Tudo existe em português e em inglês.

Dá para ver tudo isso na home. O custo de tudo isso no celular está no último capítulo.

978 capturas

Toda mudança visual grava uma captura, e nenhuma é apagada. Até 4 de novembro eram 978. Estas são 30 delas, do primeiro dia até hoje.

O cenário fechado da galeria de Cinema.

21 de agosto, 14:14O cenário fechado da galeria de Cinema.

28 / 30

A régua mostra o que o texto de um commit não mostra. Em três dias de julho, um fundo preto virou uma cena com nave, planeta, teletransporte e placar. Em agosto, a galeria foi de uma imagem plana a um cenário 3D, com dois dias de piso, luz e moldura.

Capturas por dia, somando computador, tablet e celular. O total é 978.
DiaCapturasEm obra
9 de julho26A cena e a nave
10 de julho97A cena e a nave
11 de julho158Placar e painéis
18 de agosto54Galeria plana
20 de agosto190Galeria em 3D
21 de agosto221Galeria em 3D
30 de outubro12A loja
31 de outubro12A loja
1 de novembro12A loja
2 de novembro172A loja
4 de novembro24A loja

Do mockup à cena em WebGL

Antes do código, a tela é uma imagem que eu aprovo. Depois, o agente persegue essa imagem numa cena real, com materiais, luz e reflexo.

Mockup da galeria, com três telões e um planeta ao fundo
O mockup aprovado da galeria de Cinema.
A galeria real, com o filme O Salvador no telão central
A cena em WebGL, em 21 de agosto.
Mockup da cena principal, com a nave entre asteroides
O mockup aprovado da cena principal.
A cena principal real, com a nave, o planeta e o placar
A cena principal, em 2 de novembro.

O que foi recusado também fica

Foram 13 rodadas de mockup. Cada tela recusada continua no repositório, com o motivo no nome do arquivo, para o agente não propor a mesma coisa de novo.

Mockup recusado, com o nome no centro do topo
Nome centralizado
Mockup recusado, com os atalhos de teclado em tamanho grande
Atalhos grandes demais
Mockup recusado, com as abas de navegação deslocadas
Abas fora do centro
Mockup recusado, com as abas de navegação muito largas
Abas largas demais

O clique que travava a página

A galeria ficou bonita e lenta. Ao escolher uma área, a página congelava por segundos. A medição mostrou três causas: texturas pesadas baixadas só depois do clique, 18 programas de shader compilados de uma vez no primeiro quadro, e variantes compiladas para a tela quando a cena desenha em render targets.

Medido com scripts/measure-gallery-load.mjs, em build de produção, com GPU real.
Ao escolher uma áreaAntesDepois
Do clique ao primeiro telão~2,3 s~0,2 s
Página travada0,6 a 2 s~0,3 s
Texturas~5,5 MB~0,6 MB
  1. Texturas menores, e antes do clique

    WebP em potência de 2, baixadas assim que a tela de escolha aparece. No clique, nenhuma requisição de textura.

    ADR-002

  2. Montar a galeria durante a animação

    A galeria monta no clique, sob a tela de escolha que está saindo. A compilação acontece enquanto a animação cobre o trabalho.

    ADR-002

  3. Compilar fora da thread principal

    compileAsync, com um render target ligado para gerar as mesmas variantes que o bloom e o piso refletor usam.

    ADR-002

O som que não saía no celular

Em 11 de julho, o som da nave não tocava no celular. O histórico desse dia mostra o agente errando rápido, e o que mudou quando a causa apareceu.

  1. 1h39 a 1h58

    Quatro correções em 19 minutos

    Retomar o áudio no toque, ligar no joystick, criar o contexto antes do gesto, tocar um oscilador mudo. Cada uma partia de um palpite, e o som não vinha.

  2. 2h10

    Voltamos tudo

    Um único commit desfez 5 correções e levou o código ao último estado conhecido. Cinco minutos depois, uma tentativa de autoplay entrou e saiu.

  3. 10h15

    A causa

    Os ouvintes de desbloqueio se removiam no primeiro evento, mesmo com o áudio ainda suspenso. E segurar o joystick só dispara touchstart, que o Chrome Android não conta como ativação. Quem começava pilotando nunca ouvia nada.

  4. 10h24 a 10h30

    O conserto e a limpeza

    Os ouvintes passam a ficar até o áudio tocar de verdade, e o gesto em andamento tenta de novo. Um laço de 200 ms, que tinha acabado de entrar, sai por ser redundante.

Empilhar correção sobre palpite custou sete commits. O que resolveu foi voltar ao último estado conhecido e entender em que momento o navegador libera o som. A conclusão ficou num teste, o verify-audio-unlock.mjs, e num limite que ficou registrado: no Chrome Android, a ativação só chega ao soltar o dedo.

As regras que viraram template

O agente não lembra da semana passada. O que ele precisa saber fica em arquivo, e escrever esse arquivo também é trabalho meu.

  1. Entrevista

    A skill requirements-discovery me entrevista e transforma as respostas no SPEC.md.

  2. Desenho

    A skill design-discovery leva o SPEC ao DESIGN.md e às variações de tela que eu aprovo.

  3. Código

    O agente segue o AGENTS.md, marca o BACKLOG.md e registra o estado de cada tela no SCREENS.md.

  4. Captura

    Nenhuma tarefa de tela termina sem a captura nos três tamanhos.

AGENTS.md, trechos das regras do histórico visual
Cada screenshot deve ser salvo em docs/visual-history/ [...]

Proibido sobrescrever um arquivo já existente em docs/visual-history/.
O histórico é append-only.

Nenhuma tarefa de front-end é considerada concluída sem o screenshot registrado em docs/visual-history/.

Essas regras vêm de um template meu, o AGENTS - Default, hoje na versão 5. Ele manda o agente devolver à base o que aprendeu em cada projeto. As regras de robustez do Film Factory, por exemplo, voltaram para o template em 18 de julho. Cada projeto novo começa com o que os anteriores custaram para descobrir.

Como dividi o trabalho com a IA

O agente escreve shader e acha gargalo. Quem olha a tela e diz que ainda não está bom sou eu.

Ficou com a IA

  • Escrever a cena em React Three Fiber, os materiais, o HUD e os controles.
  • Medir o carregamento da galeria e corrigir as causas.
  • Gravar as capturas e manter os documentos do processo em dia.

Ficou comigo

  • Definir o conceito: um portfólio que se pilota.
  • Aprovar cada mockup e cada tela, e recusar o que não servia.
  • Escrever as regras do processo e levá-las para os outros projetos.

O que ainda falta

O que este site ainda não resolve.

  • No Lighthouse de celular, a home tira 42 em desempenho, com o maior elemento em 5,2 s. Acessibilidade, boas práticas e SEO tiram 100. O peso é o preço da cena 3D, e ainda não resolvi isso.
  • O som no celular foi verificado em emulação. O toque emulado libera o áudio antes do Android real, então a prova final ainda depende de um aparelho físico.
  • A nave é um modelo pronto, com licença CC0. Não modelei.
  • A home existe em português e em inglês. Estas páginas de caso, por enquanto, só em português.