O pipeline de renderização
Do HTML e CSS até os pixels na tela: seis etapas sequenciais, cada uma com custo diferente. Entender isso é a chave para animações a 60fps.
Como pixels chegam na tela
O browser não coloca pixels na tela de uma vez — ele executa um pipeline de 6 etapas: parseia HTML em DOM, parseia CSS em CSSOM, combina os dois em uma Render Tree, calcula posições (Layout), preenche pixels (Paint) e envia as camadas à GPU (Composite).
O que torna isso crítico para performance é que algumas mudanças de CSS iniciam o pipeline
do começo (reflow), enquanto outras pulam direto para o final (composite-only).
Uma animação de left causa reflow a cada frame — 60 reflows por segundo
travam a página. A mesma animação com transform vai direto para a GPU.
Explore as etapas do pipeline
Clique em cada etapa para entender o que acontece, o que dispara, qual o custo e a dica de performance.
Parse HTML
O parser HTML lê o HTML byte a byte e constrói a árvore DOM (Document Object Model). Cada tag vira um Node. O processo é incremental — o browser começa a processar sem esperar o HTML inteiro.
Custo de cada operação
// O custo de cada tipo de mudança de estilo // ✓ COMPOSITE ONLY — só GPU, 60fps tranquilo el.style.transform = "translateX(100px)"; el.style.opacity = "0.5"; // ~ REPAINT — recalcula pixels, sem mover outros elementos el.style.color = "red"; el.style.backgroundColor = "blue"; el.style.boxShadow = "0 2px 8px rgba(0,0,0,0.5)"; // ✗ REFLOW — recalcula o layout inteiro, caro! el.style.width = "200px"; el.style.margin = "20px"; el.style.fontSize = "18px"; el.style.display = "none";
Animações de alto desempenho
/* Animações: o jeito certo vs. o jeito errado */ /* ✗ Errado: anima left/top — causa reflow a cada frame */ @keyframes mover-mal { from { left: 0; } to { left: 200px; } } /* ✓ Certo: anima transform — só composite, GPU puro */ @keyframes mover-bem { from { transform: translateX(0); } to { transform: translateX(200px); } } /* Dica: requestAnimationFrame para animações em JS */ function animar(timestamp) { el.style.transform = `translateX(${progress}px)`; requestAnimationFrame(animar); // executa antes do próximo frame }
transform, opacity,
will-change: transform ou position: fixed ganham sua própria camada
(layer) no compositor. Isso isola repaint — uma mudança nesse elemento não invalida outros.
Mas camadas custam memória: não use will-change em tudo.
Meça o custo de reflow
Mini projeto: crie uma página com 100 elementos <div>. Escreva dois loops: um que lê offsetWidth e depois escreve style.width (ruim), e outro que lê tudo antes de escrever (bom). Use performance.now() para medir a diferença.
Projeto principal: construa uma animação de partículas (100 bolinhas voando) usando requestAnimationFrame + transform: translate(). Garanta 60fps usando o DevTools Performance tab para confirmar.
Desafio extra: use a Layout Instability API (new PerformanceObserver com layout-shift) para medir o CLS (Cumulative Layout Shift) de uma página. Identifique qual elemento causa o maior shift e corrija-o.
Teste sua intuição
will-change: transform faz?
Onde você encontra isso
Web Games
Games em JavaScript como jogos de plataforma usam canvas 2D ou WebGL — que vão direto para a GPU, pulando o pipeline DOM inteiramente. Framer Motion e GSAP usam transform para animações a 60fps.
Lighthouse
O Lighthouse (DevTools → Lighthouse) mede Core Web Vitals e identifica automaticamente elementos que causam layout shift (CLS), paint lento (LCP) e interatividade ruim (INP).
Scroll virtual
Listas com 10.000 itens (react-window, TanStack Virtual) renderizam apenas os visíveis. Sem isso, o browser faria layout de 10.000 elementos e o scroll travaria.
CSS Houdini
A API Houdini permite escrever código C++ que roda no pipeline de renderização do browser. Você pode criar custom layout engines e custom paint worklets que executam antes do compositor.