|

Construindo um estúdio de animação 2.5D com paralaxe em uma única sessão do Claude Code: 3 trechos de código essenciais para exportar MP4 quadro a quadro de forma determinística

Construindo um estúdio de animação 2.5D com paralaxe em uma única sessão do Claude Code: 3 trechos de código essenciais para exportar MP4 quadro a quadro de forma determinística

Nota do autor: do comando à fórmula de compensação de profundidade de campo, passando pela exportação quadro a quadro com Playwright + ffmpeg, aqui vai o passo a passo completo de como montar com o Claude Code um estúdio de animação de paralaxe 2.5D que exporta sem perder frames.

Muita gente que usa um assistente de programação com IA para fazer animação passa pela mesma situação: no navegador, a prévia roda lisinha, mas na hora de exportar o vídeo aparecem travadas, frames pulados e uma duração que não bate com o esperado. O problema quase sempre está na arquitetura: desde o começo, a exportação não foi tratada como parte do design. Este artigo gira em torno de um eixo central, função pura renderAt(t) + captura de tela com seek quadro a quadro, e mostra como montar, numa única sessão do Claude Code, um estúdio de animação de paralaxe 2.5D e exportar MP4 de forma estável.

Este não é mais um daqueles artigos de demonstração do tipo “peça para a IA escrever uma animação”. Aqui a gente desmonta a dedução da fórmula de compensação de profundidade de campo, um bug de deslocamento bem típico e a diferença essencial entre reprodução em tempo real e exportação quadro a quadro.

Valor central: ao terminar a leitura, você vai ter uma estrutura de comando reutilizável, um pipeline de exportação reproduzível e uma checklist de validação que dá para levar para outros projetos de vídeo programático.

claude-code-2-5d-parallax-motion-studio-deterministic-mp4-export-pt-pt-image-0


Pontos-chave do estúdio de animação de paralaxe 2.5D com Claude Code

A prática descrita nas referências vem de um artigo publicado em 2026-11-09. O autor usou o Claude Code com o Opus 5.5 e, em uma única sessão, criou um estúdio em HTML de arquivo único, mais um script de exportação em Playwright com cerca de 25 linhas. No notebook do autor, um vídeo de 6 segundos em 720p (180 quadros) foi exportado em cerca de 20 segundos. Ele não informou a configuração do hardware, então esse tempo serve só como ordem de grandeza.

Ponto-chave Descrição Valor
Grafo de cena (Scene Graph) Cada camada tem profundidade z, tamanho, posição e conteúdo HTML Cria sensação de profundidade sem modelos 3D
Linha do tempo (Timeline) Keyframes da câmera e das camadas, cada trecho com uma curva de easing Os movimentos de câmera ficam editáveis e reutilizáveis
Renderizador determinístico renderAt(t) gera um único quadro para qualquer instante t O resultado exportado não depende da velocidade da máquina
Escala com compensação de profundidade Tamanho e deslocamento são multiplicados por 1 - z/perspective Parado, o que você vê é o que escreveu; em movimento, a paralaxe vem de graça
Pipeline de exportação quadro a quadro O Playwright faz seek em i/fps, tira o screenshot e entrega ao ffmpeg para codificar A saída é igual em qualquer máquina, sem quadros perdidos

Como funciona a paralaxe 2.5D

A ideia do 2.5D é colocar materiais 2D planos em diferentes profundidades de um espaço 3D e depois fazer uma câmera virtual passar por eles. As camadas mais próximas da câmera se movem rápido e as mais distantes se movem devagar. O olho humano interpreta essa diferença de velocidade como profundidade, e isso é a paralaxe (parallax). O processo não envolve modelagem, iluminação nem engine 3D: o perspective e o preserve-3d do CSS no navegador já dão conta.

A diferença entre um “estúdio” e uma “animação avulsa” está em ter as três peças completas: grafo de cena, linha do tempo e renderizador determinístico. Muitos tutoriais pulam a terceira, mas é ela que decide se o MP4 exportado vai travar ou não. Por isso, ao escrever a primeira linha do comando, vale já definir isso como requisito obrigatório.

claude-code-2-5d-parallax-motion-studio-deterministic-mp4-export-pt-pt-image-1


Configuração de ambiente e modelo para o estúdio 2.5D no Claude Code

O material de referência recomenda usar o Opus 5.5 para fazer a primeira construção. A visão geral de modelos oficial da Anthropic posiciona esse modelo como voltado para “programação com agentes de longa duração e trabalho de conhecimento”, com janela de contexto de 1M tokens e saída máxima de 128K. O preço oficial é de US$ 4 / US$ 20 por milhão de tokens de entrada / saída, mas vale conferir os valores atuais na página oficial.

Recomendo fixar o ID exato do modelo na hora de iniciar, para evitar que uma troca de alias no meio do projeto mude o comportamento:

claude --model claude-opus-5-5 --effort high

O Opus 5.5 usa effort medium por padrão. Na primeira construção, o agente precisa acertar de uma vez a matemática de projeção, o motor de keyframes e o pipeline de exportação, então o ideal é subir para high. Depois, para ajustes pequenos, como mudar valores de keyframes ou cores, dá para voltar para medium e economizar tokens.

Etapa Effort recomendado Tarefas típicas Motivo
Primeira construção high Matemática de projeção, motor de keyframes, scripts de exportação Vários subsistemas precisam ficar alinhados de uma só vez
Iteração e ajustes finos medium Ajustar keyframes, mudar paleta de cores, adicionar camadas Escopo de mudança pequeno, lógica já estável
Interface “descrever câmera” low (para começar) Retornar JSON de keyframes com schema fixo Tarefa curta, estrutura de saída fixa

O material de referência não traz dados comparativos sobre o resultado de uma construção equivalente com outros modelos, nem sobre a diferença de custo. Se você quiser trocar de modelo, sugiro rodar o mesmo comando em cada um e depois comparar item por item usando o método de validação por quadro estático, que veremos mais adiante.


Estrutura de comando para construir um estúdio 2.5D com o Claude Code

Um comando bem específico rende mais do que cinco comandos vagos. A especificação de construção do material de referência é um único parágrafo, e abaixo está a versão que preserva o sentido original:

Build a 2.5D motion studio as one file, studio.html, with no dependencies.
- Scene: 6 layers (sky, moon, hills, city skyline, a title card "Ship it.",
  foreground foliage), each at its own z depth, using CSS perspective and preserve-3d.
- Camera: keyframes for x, y, z (dolly) and yaw (orbit around the card).
- Per-layer tracks: opacity, scale, y. Easings: linear, easeInOutCubic,
  easeInOutSine, easeOutBack.
- A timeline bar with keyframe markers, a playhead, click to scrub, space to play or pause.
- renderAt(t) must be pure in t. Expose window.__seek(t) that resolves after
  the frame paints, and a ?capture mode that hides the UI.
Then write export.mjs: Playwright opens ?capture, seeks every frame at 30 fps,
screenshots it and pipes PNGs to ffmpeg as H.264.

Esse comando pode ser dividido em quatro camadas: cena, câmera, trilhas e curvas de suavização (easings) e interface interativa, fechando com o contrato de exportação. O que realmente faz diferença são as duas últimas linhas. Elas exigem que renderAt(t) seja uma função pura em relação a t, que exista um __seek que só retorna depois que o quadro foi desenhado e que haja um modo ?capture que esconde a interface. Com isso, a capacidade de exportar vira uma restrição de arquitetura, e o agent projeta o código seguindo essa restrição.

Se você for levar isso para o seu próprio projeto, use essa estrutura como modelo: no começo, deixe claro “o que desenhar” e, no final, use duas ou três linhas para travar “como os quadros serão capturados de forma determinística”.


Análise do código central 2.5D gerado pelo Claude Code

O estúdio inteiro tem cerca de 150 linhas, e o essencial está nos três trechos abaixo.

Amostragem de keyframes e easing

Cada trilha é um array de keyframes ordenado por tempo. A função sample() primeiro encontra os dois keyframes que cercam o instante t, usa a função de easing do keyframe anterior para calcular o progresso e, em seguida, interpola todos os campos numéricos:

function sample(keys, t) {
  if (t <= keys[0].t) return keys[0];
  for (let i = 0; i < keys.length - 1; i++) {
    const a = keys[i], b = keys[i + 1];
    if (t <= b.t) {
      const p = (easings[a.ease] || easings.linear)((t - a.t) / (b.t - a.t));
      const out = {};
      for (const k in b)
        if (typeof b[k] === 'number' && k !== 't') out[k] = a[k] + (b[k] - a[k]) * p;
      return out;
    }
  }
  return keys[keys.length - 1];
}

Essa função não lê o tempo atual, não depende do estado do frame anterior e também não usa números aleatórios. Para o mesmo t, ela sempre devolve exatamente os mesmos valores, e é daí que vem o determinismo do renderAt(t).

Compensação de profundidade: a fórmula central do paralaxe 2.5D

A ideia aqui é que a câmera fica parada e o mundo inteiro se move no sentido contrário. Além de se deslocar no eixo z, cada camada ainda é multiplicada por uma escala de compensação:

const PERSPECTIVE = 1200; // deve ser igual à perspective do #viewport
world.style.transform =
  `translate3d(${-cam.x}px, ${-cam.y}px, ${cam.z}px) rotateY(${-cam.yaw}deg)`;
const comp = 1 - L.z / PERSPECTIVE;
el.style.transform = `translate(-50%,-50%) ` +
  `translate3d(${x * comp}px, ${y * comp}px, ${L.z}px) scale(${comp * s})`;

Veja como chegamos nisso. Com uma distância de perspectiva CSS igual a p, um ponto na profundidade z (z negativo significa que ele se afasta do observador) é projetado na tela com escala de p / (p - z). Tomando p = 1200 e z = -900, temos 1200 / 2100 ≈ 0.571, ou seja, a camada fica com apenas cerca de 57% do tamanho original.

Para cancelar essa redução, basta multiplicar pelo inverso: (p - z) / p = 1 - z/p = 1 + 900/1200 = 1.75. É exatamente esse o valor de comp no código. Como 0.571 × 1.75 = 1, com a câmera parada cada camada aparece no tamanho e na posição que você definiu.

E de onde vem o paralaxe? Quando a câmera se move, o world inteiro se desloca pelo mesmo dx, mas o deslocamento de cada camada na tela ainda é multiplicado pelo seu próprio p/(p - z). Camadas distantes têm um coeficiente menor, então se movem menos; camadas próximas têm um coeficiente maior, então se movem mais. A compensação só corrige a composição em repouso e não afeta a diferença de velocidade durante o movimento. É justamente isso que cria o efeito 2.5D.

z da camada Escala de projeção p/(p−z) Fator de compensação comp Velocidade relativa no deslocamento da câmera
0 (plano do cartão de título) 1.000 1.000 Referência
-300 0.800 1.250 Mais lenta
-900 0.571 1.750 Bem mais lenta
-1800 0.400 2.500 Quase um fundo estático

(Os valores da tabela foram calculados com p = 1200; as linhas de -300 e -1800 são derivações de exemplo.)

Um bug que vale a pena lembrar: compensar só o tamanho, mas não o deslocamento

Na primeira versão do código, as camadas só recebiam scale(comp), e os deslocamentos x/y não eram multiplicados por comp. O resultado foi que o deslocamento das colinas distantes ficou comprimido pela perspectiva, a composição inteira saiu do lugar e as colinas foram subindo até cobrir cerca de dois terços do céu. A correção é simples: multiplicar x e y por comp também, porque a perspectiva escala posição e tamanho pelo mesmo fator.

O mais interessante é como esse bug foi descoberto. O autor percebeu o problema olhando um único frame renderizado; lendo o código, ele não tinha notado nada. Por isso, antes de confiar em qualquer movimento de câmera, peça ao agente para renderizar três imagens estáticas: o início, o meio e o fim.

claude-code-2-5d-parallax-motion-studio-deterministic-mp4-export-pt-pt-image-2


Exportação determinística quadro a quadro de animação de paralaxe 2.5D para MP4

A diferença essencial entre reprodução em tempo real e exportação quadro a quadro

A prévia em tempo real usa requestAnimationFrame, e quando a máquina fica ocupada ela perde quadros. Na fase de prévia isso é aceitável, mas na exportação não pode acontecer. Se você usar gravação de tela, o que vai ser capturado é “o que estava na tela naquele instante de tempo real”, então qualquer atraso de um quadro vira quadro repetido ou quadro faltando.

Dimensão Reprodução em tempo real (rAF / gravação de tela) Exportação com seek quadro a quadro
Fonte de tempo Relógio do sistema Índice do quadro i / fps
Quando a máquina está ocupada Perde quadros, repete quadros Só fica mais lento, a imagem não muda
Consistência entre máquinas Inconsistente Todas as máquinas geram a mesma saída
Número de quadros Não dá para controlar Exatamente duration × fps
Casos de uso Prévia de edição, arrastar a linha do tempo Vídeo final, geração automática em CI

Script de exportação minimalista

O tempo do exportador é conduzido pelo número do quadro. A cada quadro, primeiro fazemos o seek, esperamos a imagem terminar de ser desenhada, tiramos o screenshot e escrevemos o PNG na entrada padrão do ffmpeg:

import { chromium } from 'playwright';
import { spawn } from 'node:child_process';
import { pathToFileURL } from 'node:url';

const out = process.argv[2] || 'out.mp4';
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
await page.goto(pathToFileURL('studio.html').href + '?capture');

const { duration, fps } = await page.evaluate(() => window.__scene);
const ffmpeg = spawn('ffmpeg', ['-y', '-f', 'image2pipe', '-framerate', String(fps),
  '-i', '-', '-c:v', 'libx264', '-pix_fmt', 'yuv420p', '-crf', '18', out],
  { stdio: ['pipe', 'ignore', 'inherit'] });

const frames = Math.round(duration * fps);
for (let i = 0; i < frames; i++) {
  await page.evaluate(t => window.__seek(t), i / fps);
  ffmpeg.stdin.write(await page.screenshot({ type: 'png' }));
}
ffmpeg.stdin.end();
await new Promise(r => ffmpeg.on('close', r));
await browser.close();

Alguns detalhes que vale a pena observar. O window.__seek(t) só resolve depois de dois quadros de animação, assim o screenshot sempre pega o transform novo. O image2pipe faz o ffmpeg ler a sequência de PNGs da stdin. O yuv420p garante que o arquivo gerado toque normalmente no navegador. Se o Playwright ainda não tiver o navegador instalado, rode uma vez npx playwright install chromium.

Ver a implementação de referência do __seek e da validação por quadros estáticos (código ilustrativo)
// Dentro do studio.html: seek determinístico, só retorna depois de desenhar dois quadros
window.__seek = (t) => new Promise(resolve => {
  renderAt(t);                       // Função pura: depende apenas de t
  requestAnimationFrame(() =>
    requestAnimationFrame(resolve)); // Garante que os estilos foram aplicados e desenhados
});
// stills.mjs: renderiza três quadros estáticos (início / meio / fim); veja a imagem antes de confiar no enquadramento
import { chromium } from 'playwright';
import { pathToFileURL } from 'node:url';

const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
await page.goto(pathToFileURL('studio.html').href + '?capture');
const { duration } = await page.evaluate(() => window.__scene);

for (const [name, t] of [['start', 0], ['mid', duration / 2], ['end', duration - 1e-3]]) {
  await page.evaluate(t => window.__seek(t), t);
  await page.screenshot({ path: `still-${name}.png` });
}
await browser.close();

Os dois trechos acima são implementações ilustrativas, escritas a partir da ideia do material de referência; o que vale mesmo é o código que o agente gerar no seu projeto.

Resultado do teste feito pelo autor: 180 quadros, 1280×720, 30 fps, duração de 6,0 segundos, MP4 de saída com cerca de 420 KB e tempo total de aproximadamente 20 segundos. Na imagem, as torres da cidade se deslocam menos do que as colinas escuras em primeiro plano, e essas colinas acabam saindo completamente de quadro, que é justamente o efeito de paralaxe.

claude-code-2-5d-parallax-motion-studio-deterministic-mp4-export-pt-pt-image-3


Adicionando um recurso de “descrever plano” ao estúdio 2.5D

A próxima etapa é deixar o roteirista digitar uma frase descrevendo o plano e receber do modelo um JSON com os keyframes. O material de referência faz isso chamando direto a Anthropic Messages API:

curl https://api.anthropic.com/v1/messages \
  -H "x-api-key: $ANTHROPIC_API_KEY" \
  -H "anthropic-version: 2026-10-21" \
  -H "content-type: application/json" \
  -d '{
    "model": "claude-opus-5-5",
    "max_tokens": 4096,
    "output_config": { "effort": "low" },
    "system": "Return only JSON: {\"camera\": [{\"t\",\"x\",\"y\",\"z\",\"yaw\",\"ease\"}], \"tracks\": {}}. Times in seconds, 0 to 6.",
    "messages": [{ "role": "user", "content": "Slow push-in, slight orbit right, title fades up in the first second." }]
  }'

Aqui há duas armadilhas fáceis de cair. A primeira: o effort precisa ficar dentro de output_config.effort. A segunda: o Opus 5.5 sempre usa o raciocínio adaptativo (adaptive thinking), então passar thinking: {"type": "disabled"} retorna um erro 400, e os tokens de raciocínio também contam no max_tokens. Se você definir um max_tokens baixo demais, o JSON será cortado no meio do array e o stop_reason virá como "max_tokens".

Uma resposta truncada, ou com texto extra misturado, faz o sample() falhar na hora de renderizar. Por isso vale a pena ter um teste de contrato para essa chamada. O material de referência usa o Apidog CLI para que a mesma sessão do Claude Code crie e execute os testes, verificando que o status code é 200, que o stop_reason não é "max_tokens", que o texto retornado pode ser interpretado como JSON e que o t do array camera cresce de 0 até 6. Outras ferramentas de teste de API, ou um script de asserções escrito por você, chegam ao mesmo resultado. Sempre que mudar o system prompt ou o effort, rode os testes de novo, porque os dois alteram o conteúdo da resposta.


Perguntas frequentes (FAQ)

Q1: Qual é a diferença entre uma animação com paralaxe 2.5D e uma animação 3D de verdade?

No 2.5D, você coloca materiais 2D planos em profundidades diferentes e filma tudo com uma câmera em movimento. A sensação de profundidade vem da diferença de velocidade entre as camadas. Não precisa de modelos 3D, iluminação nem engine 3D: dá para fazer só com CSS no navegador. O preço é que a câmera não consegue dar uma volta em grande ângulo para trás das camadas.

Q2: Por que não gravar a tela direto, em vez de fazer seek frame a frame e tirar screenshots?

A gravação de tela segue o tempo real, então qualquer frame que demore vira frame perdido ou repetido. Quando você faz seek em `i / fps` e tira o screenshot, o conteúdo de cada frame depende só do número do frame. Por isso a saída é idêntica em qualquer máquina.

Q3: Como se chega ao coeficiente de compensação 1.75?

Com distância de perspectiva 1200 e profundidade -900, a escala projetada é 1200/2100 ≈ 0.571. A compensação é o inverso: `1 – z/p = 1 + 900/1200 = 1.75`. Multiplicando os dois, o resultado é 1, e a camada volta ao tamanho e à posição do design quando está parada.

Q4: Por que a primeira construção usa o Opus 5.5 com high effort?

Na primeira construção, é preciso escrever, numa única sessão, a matemática da projeção, o motor de keyframes e o pipeline de exportação. Na referência, um único comando mais uma rodada de correção já resultaram em um estúdio funcional. Para ajustes pequenos depois, medium ou menos é suficiente. Ainda não há comparações públicas com outros modelos, então você precisa testar por conta própria.

Q5: Dá para usar o estúdio sem uma chave API?

Dá. O estúdio e a exportação em MP4 rodam localmente, sem precisar de chave. Só a função “descrever a cena” chama a Messages API e exige a sua própria chave API da Anthropic. Os tokens gastos ao construir com o Claude Code são cobrados conforme o plano da conta que você usa.

Q6: Quais são as limitações conhecidas desse método?

A velocidade dos screenshots em PNG frame a frame depende da resolução e da complexidade da página. Os 20 segundos da referência são o resultado de uma única execução em um único notebook. O CSS 3D pode renderizar de forma diferente conforme a versão do navegador, principalmente com yaw em ângulos grandes e camadas semitransparentes sobrepostas. O ID do modelo, o header de versão da API e o parâmetro effort mudam com o tempo, então confira a documentação oficial antes de usar.

Q7: Como levar a “renderização determinística + validação por frames estáticos” para outros projetos de vídeo programático?

Use a checklist abaixo, item por item:

– A função de renderização recebe apenas `t`: não lê o relógio do sistema, não depende do estado do frame anterior e não usa números aleatórios sem seed fixa
– Ofereça uma interface de seek que só retorne depois que o desenho terminar
– Ofereça um modo capture que esconda a UI, com tamanho de viewport fixo
– O número de frames exportados deve ser exatamente `round(duration × fps)`
– Depois de mexer na câmera, renderize primeiro três frames estáticos (início, meio e fim), confirme que estão certos e só então exporte o vídeo inteiro
– Adicione testes de contrato aos keyframes em JSON gerados pelo modelo: devem ser parseáveis, ter tempo monotonicamente crescente e não estar truncados


Pontos-chave do Claude Code para animação de paralaxe 2.5D

  • A exportação faz parte do design: nas últimas linhas do comando, deixe fixados a função pura renderAt(t) e o contrato de seek
  • Fórmula de compensação de profundidade: comp = 1 − z/perspective, e ela multiplica tanto o tamanho quanto os deslocamentos de x/y
  • De onde vem a paralaxe: a compensação só corrige a composição estática; quando há movimento, cada camada continua aparecendo com uma velocidade diferente, conforme a profundidade
  • Veja o quadro estático antes de confiar na câmera: três imagens (início, meio e fim) revelam bugs que a leitura do código não mostra
  • Tempo controlado pelo número do quadro: Playwright faz seek e captura de tela, e o ffmpeg com image2pipe codifica tudo em H.264
  • Ajuste o effort por etapa: use high na primeira construção, medium nos ajustes finos e comece em low para interfaces JSON estruturadas
  • Adicione testes de contrato à saída do modelo: evita que o truncamento por max_tokens quebre a análise dos keyframes

Resumo

Para montar um estúdio de animação de paralaxe 2.5D com o Claude Code, existem três pontos centrais:

  1. Arquitetura: o trio grafo de cena, linha do tempo e renderizador determinístico, sendo que o terceiro é o que define a qualidade do vídeo final
  2. Matemática: o coeficiente de compensação de perspectiva 1 - z/p atua ao mesmo tempo no tamanho e no deslocamento; parado, a composição fica precisa, e em movimento a paralaxe surge naturalmente
  3. Pipeline: faça seek quadro a quadro com i/fps, tire a captura de tela e passe para o ffmpeg codificar, de modo que o resultado não dependa do desempenho da máquina

Minha sugestão é começar seguindo a estrutura de comando deste artigo e rodar uma vez um vídeo de teste de 6 segundos. Crie o hábito de “olhar três quadros estáticos antes de mexer na câmera”. Depois, é só adicionar camadas, plugar a interface de “descrever a câmera” ou levar a mesma ideia determinística para outras soluções de vídeo programático, como o Remotion.


📚 Referências

  1. How to Build a 2.5D Motion Studio With Opus 5.5: fonte prática deste artigo
  2. Link: apidog.com/blog/build-2-5d-motion-studio-opus-5-5
  3. Descrição: inclui o comando completo, o código do estúdio, o script de exportação e um exemplo da interface “descrever plano”

  4. Documentação de modelos e do parâmetro effort da Anthropic: posicionamento do modelo, contexto, preços e explicação do parâmetro effort

  5. Link: docs.anthropic.com
  6. Descrição: serve para conferir o ID do modelo claude-opus-5-5, o cabeçalho de versão da API e o uso do output_config

  7. Documentação de formatos do ffmpeg: explicação de formatos de entrada como o image2pipe

  8. Link: ffmpeg.org/ffmpeg-formats.html
  9. Descrição: ajuda a entender como ler uma sequência de PNGs via stdin e os parâmetros de codificação H.264

  10. Documentação do Playwright: automação de navegador e API de capturas de tela

  11. Link: playwright.dev
  12. Descrição: cobre page.evaluate, page.screenshot e o comando de instalação do navegador

Artigos relacionados

Similar Posts