(85) 99645-7140 nelclassico@gmail.com Praça Coronel Melquiades, 124
Articles case study performance React Three Fiber shopify Tecnologia Three.js WebGL

Projetando a experiência da Web por trás da edição Spring ’26 do Shopify: Everywhere | Codrops

Nota do editor: Cada lançamento do Shopify Editions é acompanhado por uma experiência interativa na web que explora novas ideias na web. Para a edição Spring ’26, Everywhere, a equipe se propôs a construir uma experiência que parecesse cinematográfica e ao mesmo tempo permanecesse fiel à web: responsiva, acessível, de alto desempenho e utilizável em uma ampla variedade de dispositivos. Antes do lançamento, conversamos com a Diretora de Criação Maggie Fost e o Designer de Produto Principal Andy Thelander sobre a criação da experiência, e estamos entusiasmados que Andy expandiu muitos dos tópicos que discutimos e os transformou neste passo a passo técnico aprofundado para Codrops. O que se segue é uma rara visão das decisões de engenharia, arquitetura de renderização, pipeline de ativos personalizados e fluxo de trabalho criativo por trás do Spring ’26 Edition: Everywhere.

Building Spring 2026 Editions

Cada edição tem grandes ambições de injetar técnicas novas e impressionantes na renderização de conteúdo na web. As edições da primavera de 2026 acabaram parecendo mais próximas de uma peça de movimento do que de uma página de produto normal, mas ainda precisava se ajustar às restrições rígidas de uma página de carregamento rápido e de alto desempenho com informações facilmente legíveis.

A camada visual principal como um pequeno sistema de renderização que é atualizado em segundo plano com rolagem por uma tela unificada e muitos buffers de quadros aninhados. O DOM cuidou da camada de conteúdo e interação e o WebGL cuidou da atmosfera, nuvens de pontos, luz volumétrica e transições de rolagem.

Existem muitas decisões técnicas inteligentes que foram tomadas na construção, mas vamos nos concentrar no fluxo de trabalho criativo de como duas técnicas WebGL foram desenvolvidas internamente

Efeito 1: Criando luz volumétrica a partir do vídeo

Algumas cenas precisavam de luz suave em movimento em vez de vídeo plano. Para eles, o vídeo foi pré-processado em texturas de array KTX2 e renderizado como caixas raymarched em Three.js.

O shader trata a textura como uma pilha de quadros ou fatias:

  • sampler2DArray para texturas de volume KTX2
  • sampler2D mais um mapa de profundidade para fotos com extrusão de profundidade
  • um loop raymarch fixo com um grampo de passo de tempo de execução
  • limite, suavidade, desbotamento de borda, brilho, ajuste HSL e controles de ruído

Os arquivos KTX2 carregam os dados de imagem compactados, mas não todos os metadados de tempo que precisamos, então o aplicativo mantém um pequeno mapa de origem para volume com duração, dimensões e contagem de camadas. URLs KTX2 criados também podem ser transmitidos diretamente da predefinição de cena.

Este não é um vídeo volumétrico verdadeiro. É uma versão pragmática do navegador: um volume texturizado, raymarched apenas o suficiente para ser lido como luz e profundidade, com um limite rígido na contagem da grade e etapas raymarch para que uma predefinição direcionada por arte não possa acidentalmente se transformar em um penhasco de desempenho.

Vídeo transparente sem depender de alfa nativo

Para movimento de autoria, usamos Rive onde a interatividade era necessária e exportamos não interativo movimento para vídeo quando a animação em tempo de execução não valia o custo.

O suporte a vídeo transparente ainda é irregular entre os navegadores. O Chrome e o Firefox podem reproduzir WebM VP9 alfa nativo no caminho que precisamos, mas o Safari e navegadores desconhecidos precisam de outro caminho. Para eles, usamos uma técnica de vídeo empilhado:

┌──────────────┐
│ RGB frame    │
├──────────────┤
│ alpha frame  │
└──────────────┘

O vídeo tem o dobro da altura do quadro visível. A metade superior armazena cores. A metade inferior armazena alfa. Um pequeno shader WebGL2 reconstrói o quadro transparente em uma tela:

vec3 rgb = texture(u_tex, vec2(v_uv.x, v_uv.y * 0.5)).rgb;
float alpha = texture(u_tex, vec2(v_uv.x, 0.5 + v_uv.y * 0.5)).r;
outColor = vec4(rgb * alpha, alpha);

Alguns detalhes importaram aqui:

  • Use NEAREST filtragem para que as linhas RGB e alfa não ultrapassem o ponto médio.
  • Dimensione a tela a partir das dimensões do vídeo decodificado, não do tamanho CSS.
  • Use requestVideoFrameCallback quando disponível; volte para requestAnimationFrame.
  • Recuperar de webglcontextlost, especialmente no Safari móvel.
  • Mantenha um pôster visível até que o primeiro quadro reconstruído seja pintado.
  • Use crossOrigin="anonymous" ou texImage2D(video) falhará no vídeo CDN.

Este caminho nos permite manter a liberdade de autoria do movimento transparente sem apostar toda a experiência na implementação de vídeo alfa de um único navegador.

Efeito 2: Nuvens de pontos

As cenas atmosféricas de heróis usam fortemente nuvens de pontos. O envio de posição bruta e buffers de cores teria sido muito caro, então usamos um pequeno formato de nuvem de pontos específico do projeto: .mdpc.

Os metadados armazenam a contagem de pontos, caixa delimitadora, quantização de posição, subamostragem de croma, comprimentos de fluxo e codec de compactação. As posições são quantizadas dentro da caixa delimitadora e decodificadas novamente em Float32Array dados em um trabalhador. A cor é armazenada como fluxos de luma/croma em vez de RGB completo para cada ponto e, em seguida, reconstruída no trabalhador como RGB normalizado.

Ativos mais recentes usam deflate, que pode ser decodificado com DecompressionStream no navegador. Ativos mais antigos ainda podem retornar a Brotli por meio de um pequeno caminho wasm. O carregador também mantém um legado .bin/Draco fallback, mas .mdpc é o caminho preferido porque nos permite controlar o layout exato dos dados e evitar fazer mais trabalho do que a cena precisa.

O carregador de nuvem de pontos retorna matrizes digitadas:

type PointCloudData = {
  positions: Float32Array;
  colors: Float32Array;
  count: number;
  bounds?: {
    min: [number, number, number];
    max: [number, number, number];
  };
};

A partir daí, o renderizador constrói um Three.js BufferGeometry, amostra um número apropriado de pontos para a camada atual e orienta o comportamento visível por meio de uniformes de sombreador: tamanho, opacidade, fluxo, cáusticos, correção de cor, desbotamento da câmera, progresso da transição, colisão SDF e deslocamento de fluido.

Um pequeno, mas importante detalhe de desempenho: o objeto de nuvem de pontos carregado não é passado como estado React. Esses objetos podem conter geometrias e materiais de vários megabytes. Mantê-los em referências evita que o React percorra grandes gráficos de objetos Three.js durante os commits.

Efeitos de fluido

Vários efeitos necessários para reagir ao movimento do cursor e velocidade de rolagem: deslocamento de nuvem de pontos, distorção de volume de luz e algum pós-processamento. Em vez de cada sistema possuir sua própria simulação, a página usa um campo de fluido compartilhado por renderizador.

Um componente FluidField não renderiza nada. Ele possui a simulação de fluido, insere-a em useFrame e grava a textura de velocidade em uma referência. Os consumidores experimentam essa textura em seus próprios shaders.

Somente a seção ativa pode conduzir a simulação. As seções passivas podem ler a textura atual, mas não redimensionam nem alteram o FBO compartilhado. Isso evita que diferentes seções lutem por recursos no nível do renderizador durante as transições de rolagem.

O movimento reduzido é tratado dentro do mesmo sistema. A simulação pode continuar em execução para que a interação do ponteiro ainda pareça responsiva, mas os splats acionados pela rolagem são removidos e a força do splat é reduzida antes que os efeitos downstream experimentem o campo de velocidade.

Camadas de dispositivos em vez de uma cena universal

A página teve que ser executada em uma ampla variedade de GPUs, então o renderizador usa uma única camada de tempo de execução como botão de desempenho principal:

  • Camada 0 : sem WebGL ou GPU na lista de bloqueio; use substitutos estáticos.
  • Camada 1 : WebGL mínimo; menores ativos de nuvem de pontos e menor custo de quadro.
  • Camada 2 : tamanhos de textura reduzidos e efeitos de transição mais simples.
  • Camada 3 : qualidade de desktop completa.

A camada é resolvida com detecção de GPU, verificações de suporte WebGL e caminhos de fallback de tempo de execução. Os dispositivos móveis são limitados agressivamente porque a pressão da memória é mais importante do que a pontuação máxima do benchmark. Na prática, isso significa que dispositivos móveis obtêm variantes de nuvem de pontos menores e custos de renderização mais baixos, mesmo em dispositivos que podem tecnicamente executar cenas mais pesadas por um curto período de tempo.

O DPR também está fixado. Em vez de usar window.devicePixelRatio cegamente, o renderizador limita o DPR por nível de qualidade e tamanho da janela de visualização.

Desempenho e rolagem

Cada seção de conteúdo é mapeada para uma predefinição de cena. À medida que o usuário rola, o aplicativo resolve qual cena está ativa, monta a cena ativa e seus vizinhos imediatos e compõe entre eles em uma tela WebGL em tela cheia.

O fluxo aproximado é assim:

scroll position
  → section visibility
  → active scene state
  → Three.js / R3F scene
  → offscreen framebuffer
  → GLSL composite pass
  → fullscreen canvas behind the DOM

A parte importante é que a posição de rolagem gera uniformes, não renderizações do React. O movimento da câmera, as transições de seção, o deslocamento da tela, o deslocamento da nuvem de pontos e os parâmetros de pós-processamento são lidos nas referências dentro do loop de renderização. Isso manteve as atualizações de rolagem de alta frequência fora do React e ainda permitiu que a página DOM e a cena WebGL permanecessem visualmente interligadas.

Apenas uma pequena janela de cena permanece montada por vez. A seção ativa e as seções adjacentes são mantidas ativas; todo o resto é desmontado e descartado. Isso nos deu sobreposição suficiente para transições de rolagem sem manter cada nuvem de pontos, textura de vídeo e pipeline de shader na memória ao mesmo tempo

Playground para capacitar designers

O fluxo de trabalho criativo foi construído em torno de uma predefinição de cena compartilhada em vez de um formato de protótipo separado. Uma predefinição descreve a câmera, o ambiente, a nuvem de pontos, o pós-processamento, os volumes de luz do vídeo, as formas SDF, os deslocamentos móveis e o comportamento de transição.

O playground e o aplicativo de produção consomem a mesma forma predefinida. Designers e desenvolvedores criativos poderiam ajustar cenas, iluminação, layout e movimento WebGL em um navegador e, em seguida, mover os mesmos valores para a produção, em vez de pedir à engenharia para reinterpretar uma maquete.

Isso mudou o ciclo de colaboração. Em vez de “intervenção de projeto, reconstruções de engenharia”, o ciclo passou a ser: ajustar a cena real, testá-la no navegador, verificar os substitutos em camadas e, em seguida, enviar a mesma configuração subjacente.

O que aprendemos

A maior lição foi que o trabalho do navegador de ponta é principalmente trabalho de pipeline, construindo a fábrica para construir o item em escala. A página final parece um sistema visual, mas a estabilidade vem de um conjunto de restrições práticas:

  • manter valores controlados por rolagem fora das renderizações do React
  • pré-calcular movimentos pesados quando a animação em tempo de execução não adiciona valor
  • monte apenas as cenas próximas à janela de visualização
  • faça com que as ferramentas criativas usem o mesmo contrato de dados da produção

Essa combinação permite que o site mantenha a sensação de um lançamento cinematográfico enquanto ainda se comporta como uma página de produção.

Deixe um comentário

Seu email não será publicado. Campos obrigatórios marcados com *

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.