24.6 C
New York
Tuesday, May 19, 2026

80s Enterprise Tech and Seamless Scene Transitions: Inside Shader.se’s Scroll-Pushed WebGPU Pipeline



Shader is a artistic growth studio primarily based in Sweden, based a couple of years in the past by me and my colleague Simon Hedlund. Beginning a enterprise threw us headfirst into the company ecosystem of acronyms and LinkedIn rhetoric. We coped the one affordable manner: by making enjoyable of all of it, to the purpose the place we named our newly based firm Excessive Tech Enterprise Options, staged a enterprise photoshoot, and degraded the outcomes till they regarded right: watermarked, a beneficiant horizontal stretch and saved as a JPEG with the standard slider dragged to the ground. Sensible, we thought. Much less good in conferences with potential purchasers, the place there wasn’t all the time room to make clear that the identify and firm materials was ironic.

The disgrace finally drove us to rebrand as Shader Improvement Studio, not practically as humorous, but it surely not less than describes what we really do: pc graphics, visualizations, and net. Nonetheless, we preferred the ironic enterprise angle from Excessive Tech Enterprise Options an excessive amount of to let it disappear utterly.

When it got here time to construct our personal web site, leaning into that company satire felt like the one sincere selection. We’d spent years poking enjoyable at enterprise tradition from the within, so we went all the way in which. We stored coming again to 80s enterprise tech: beige computer systems, shiny workplace advertisements, gross sales brochures, golden ties, and stiff company handshakes.

Tech Stack & Instruments

Subsequent.js: not the apparent selection for a Three.js-heavy website, but it surely earns its place right here for metadata dealing with and server-side knowledge fetching from our CMS. Many of the web page is canvas, however the bits that aren’t nonetheless profit from a correct framework beneath.

Three.js + React Three Fiber + TSL: Three.js does the heavy lifting, React Three Fiber provides us a declarative strategy to compose scenes in React, and TSL (Three.js Shading Language) lets us write node-based supplies that compile to each WebGL and WebGPU. That final half issues: we are able to goal the newer WebGPU renderer with out utterly abandoning WebGL compatibility.

Lenis: clean scroll library. We modified it barely to assist the form of web page snapping we needed between the Hero and Our work scenes.

@pmndrs/uikit: lets us construct DOM-like layouts and UI straight contained in the Three.js canvas. Since we would like every part to dwell within the WebGPU pipeline and go by way of post-processing, rendering UI as precise DOM parts on prime wasn’t an choice. Forked to get it to work with WebGPU.

Selective Rendering

The muse of the entire system is a straightforward page-section config: an array the place every web page part has a kind and a size. Web page lengths may be dynamic, adapting to content material or display measurement; the About web page for instance adjusts its size primarily based on how a lot UI content material wants to suit on totally different viewports.

We use Lenis for clean scrolling, which additionally provides us a dependable scroll place worth every body. That worth is what we use to find out which pages are at the moment in view. Every web page’s begin place is simply the cumulative sum of the lengths earlier than it, so changing a uncooked scroll worth right into a progress for any given web page is a straightforward calculation.

Every body, we examine the present scroll place towards every web page’s place in that config. If a web page isn’t in view, its total render go is skipped, no draw calls, no GPU work. Meaning every part: not simply the primary scene render, however each sub-pass it owns too. The diagram above illustrates this: solely the About scene is energetic, so the material sim go, the UI go, and the ultimate About composite all run. Every little thing above and under is solely not touched that body. Lastly the web page passes are despatched to the compose go the place submit results similar to movie grain, chromatic aberration, bloom and such are added.

Scenes which can be energetic render in reverse order: final web page first, first web page final. That ordering issues as a result of every scene can obtain the subsequent scene’s output as a texture. The final scene renders into its FBO and passes that texture to the scene earlier than it, which may use it nonetheless it desires earlier than rendering its personal FBO. The chain continues till the primary scene, whose output lands in a remaining compose go the place post-processing is utilized earlier than hitting the display.

Pages also can outline a render offset: a window that extends their energetic vary barely earlier than or after their scroll place. That is used to make sure a scene’s FBO is prepared earlier than it really must be seen, which is crucial for transitions the place the present scene must pattern the subsequent one.

That is illustrated, for instance, when on the finish of the About scene: the subsequent scene is rendered concurrently and visual beneath the paper shreds.

The best way this works in follow is thru React’s useImperativeHandle. In a typical R3F setup you’d attain for useFrame to run code each body, however useFrame is parent-blind. The callback will get subscribed to the render loop and runs on the loop’s schedule, not the guardian’s. That’s high quality for self-contained animations, however the guardian can’t invoke it on demand, can’t sequence it relative to its personal framework, and may’t get a return worth again.

useImperativeHandle solves this by letting a part expose a perform on a ref. The guardian holds that ref and decides when to name it, or whether or not to name it in any respect. Every scene exposes a render perform this manner: it takes the present renderer state and optionally the subsequent scene’s texture, renders into its FBO, and returns the ensuing texture.

Every scene part exposes its render perform on a ref utilizing useImperativeHandle. It receives the renderer state and the subsequent scene’s texture, updates no matter it must, renders into its personal FBO, and returns the feel. Beneath is a simplified instance that reveals how that is achieved.

const fbo = useFBO();

useImperativeHandle(renderHandle, () => ({ state, nextSceneTexture }) => {
  const { gl } = state;

  nextSceneTextureUniform.worth = nextSceneTexture

  gl.setRenderTarget(fbo);
  gl.render(scene, digicam);
  gl.setRenderTarget(null);

  return fbo.texture;
});

The guardian holds refs to all scene render features and calls them from a single useFrame. Scenes render in reverse order so each can go its texture ahead to the scene earlier than it. Any scene whose scroll progress is exterior vary is skipped totally.

useFrame((state) => {
  let aboutTexture = null;
  let heroTexture = null;

  const aboutProgress = getPageScrollProgress({ pageType: "about", clamp: false });
  if (aboutProgress >= 0 && aboutProgress <= 1) {
    aboutTexture = renderAboutScene({ state, nextSceneTexture: null });
  }

  const heroProgress = getPageScrollProgress({ pageType: "hero", clamp: false });
  if (heroProgress >= 0 && heroProgress <= 1) {
    heroTexture = renderHeroScene({ state, nextSceneTexture: aboutTexture });
  }
});

The distinction from useFrame is the inversion of management. The guardian orchestrates the order of rendering, passes textures down the chain, and skips any scene whose progress is exterior vary, all from one place, with none of the scenes needing to learn about one another.

Transitions

There are two essentially other ways to transition between scenes. Which one to make use of is dependent upon whether or not the transition must really feel bodily grounded in 3D house or whether or not it’s purely a 2D reveal dressed up with geometry.

Display screen-space sampling. When the transition doesn’t have to be anchored to a selected 3D floor, screen-space coordinates are the best choice. As an alternative of UV coordinates, the fabric samples the subsequent scene texture on the fragment’s display place. The mesh may be any form and sit wherever in 3D house, it is going to all the time reveal the proper a part of the underlying scene.

import { Fn, screenCoordinate, texture, uniformTexture, vec2 } from "three/tsl";

const nextSceneTexture = uniformTexture(new Texture());
const decision = uniform(new Vector2(width, top));

const materials = new MeshBasicNodeMaterial();
materials.colorNode = Fn(() => {
  const screenUv = screenCoordinate.div(decision);
  return texture(nextSceneTexture, screenUv);
})();

There are a number of scene transitions utilizing this system on our website, for instance the company handshake transition to our Contact web page.

Frustum-matched airplane. The hero transition must really feel bodily grounded within the 3D scene. The digicam doesn’t simply transfer towards a set z worth; it strikes towards the precise monitor display mesh. We learn the display’s world place, measurement, and rotation, then place the digicam alongside the display’s ahead course on the distance the place the digicam frustum covers the display.

perform fitCameraToScreen(
  planePosition: Vector3,
  planeScale: Vector3,
  planeQuaternion: Quaternion,
  viewportAspect: quantity,
  fov: quantity,
) {
  // The monitor could also be rotated, so use its world-space regular as a substitute of digicam z.
  const regular = new Vector3(0, 0, 1).applyQuaternion(planeQuaternion).normalize();

  // Choose the dimension that fills the viewport first.
  const planeAspect = planeScale.x / planeScale.y;
  const distance =
    viewportAspect < planeAspect
      ? planeScale.y / 2 / Math.tan(fov / 2)
      : planeScale.x / 2 / (Math.tan(fov / 2) * viewportAspect);

  // Transfer barely previous the precise match so no monitor border leaks by way of.
  return planePosition.clone().add(regular.multiplyScalar(distance * 0.9));
}

The subsequent scene’s FBO must exist by the point it seems on the monitor display, which is the place the render offset pays off, the upcoming scene begins rendering a couple of scroll items early, so its texture is all the time prepared when wanted.

Wrapping Up

The technical facet of a website like that is solely a part of the work. You could know your manner round render targets, scroll state, shaders, and efficiency, however that isn’t the place more often than not goes. More often than not goes into discovering an concept that feels particular, then tuning animations and transitions till they’ve the appropriate weight, timing, and character. A transition may be technically right for days earlier than it really feels good.

One space we’re enthusiastic about is the work taking place round HTML in canvas. Rendering UI in a canvas at the moment typically means rebuilding issues the browser already does effectively: structure, textual content, inputs, interplay states, and accessibility. For this website we used @pmndrs/uikit so the UI might dwell in the identical WebGPU pipeline as every part else, however having extra native methods to deliver actual HTML into canvas-based experiences would make this a lot simpler. It will additionally make accessibility much better, as a result of the UI might maintain extra of the semantics and conduct folks already count on from the net.

We’ll be releasing an entire open supply instance using all of the strategies coated right here quickly. Comply with us on LinkedIn, X, and Instagram to know when it drops.





Supply hyperlink

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles