Technical note · Published on 25 September 2026
How I built this site's 3D studio
One data source, 3D devices matched to the DOM simulators, a ground that falls away to make a horizon, and above all what I had to remove so it runs everywhere.
The studio is the site's default view on a computer. The projects sit on their devices, in a three-dimensional scene you hover over and open. Here is how it's built, and above all what I had to change so it holds up.
One data source. Every project is a typed TypeScript file, and the site's three views, the list, the desk and the studio, are just three ways of rendering the same data. The prerendered HTML is always the list. The studio is a separate chunk of code, mounted on top once the browser has confirmed it has a GPU, a wide enough screen and no preference for reduced motion.
React Three Fiber and drei make the scene. The devices are rounded boxes with a screen cut into the corners, textured with the apps' real screenshots. The detail that makes the focus possible is that the 3D devices' proportions are exactly those of the DOM simulator frames. On every frame, the camera projects the four corners of the 3D screen into CSS pixels, and the HTML simulator lands right on top. You're no longer looking at a texture, you're handling the real simulator.
The ground is a small planet. A height field is computed at load, with craters and their rims, and from it I derive a colour map and a normal map so the light genuinely models the relief. The plane stays flat under the devices and bends away beyond an ellipse, which gives a curved horizon. I did the maths before coding. Seen from a low viewpoint, the horizon of a real sphere projects almost straight, whatever its size. Visible roundness can only come from a ground that falls away.
The stars cost me an evening. The off-the-shelf component I was using projects its points with a homogeneous component of 0.5, which shifts their depth and, with a far plane at 60 units, clips every star beyond 30. The sky stayed almost empty without anything crashing. I now draw them myself, nine thousand points on a computer, deterministically placed, in three whites, each with its own twinkle.
What I removed matters as much as what I added. The post-processing composer, for bloom and vignette, allocated a multisampled half-float buffer at full Retina resolution, close to 300 MB on a large screen, and made the scene stutter. The vignette became a CSS gradient, the bloom went away, and a budget of seven million pixels per frame caps the pixel ratio on very large screens.
No real GPU, no studio. Browsers without a graphics card, virtual machines and Lighthouse get a software WebGL that brings the scene to its knees. The probe asks for the context with failIfMajorPerformanceCaveat, which refuses a software renderer straight away, and in that case the site recommends the desk. Reduced motion doesn't remove the studio, it calms it, with no choreographed entrance, no floating, no parallax.
On a phone, you walk. The camera frames one group of projects at a time and moves along the ground under your finger, with a stop per project. A tap opens the device in a sheet at the bottom of the screen. And if nothing is touched for twenty seconds on a computer, the studio tours itself, one group after another, until the first gesture.