← Alle Einträge

11. Juni 2026 · webgpu · tsl · three.js · astro · gaussian-splatting

001: The Captured World, foundations

I’m rebuilding this site in the open. The old version was a heavyweight platform with a database, auth, and payment rails, all wrapped around a portfolio that never used any of it. The new one inverts the priorities. The experience is the product. Everything else stays as small as I can make it.

The premise

My work is about capturing reality and rebuilding it with machines. Photogrammetry, Gaussian splatting, neural rendering, generative systems. The site takes that literally. The homepage is a captured space you fly through by scrolling, and at chapter boundaries the world will fall apart into particles and reassemble. Reality turns into data and back into reality.

The final hero will be a Gaussian splat of a real Frankfurt space, trained against an exact splat budget and shipped as a compressed SOG file. That capture doesn’t exist yet, so a stand-in plays the role for now: a LiDAR-style point cloud of a room. Eighty thousand sprites sit on the shell of an implied space while an orange scan plane sweeps through. It reads as exactly what it is, a space being scanned.

Renderer decisions

Everything renders through three.js’s WebGPURenderer from the three/webgpu entry point. Where WebGPU isn’t available, the same renderer transparently falls back to WebGL2. One scene graph, two backends. The rule that makes this work: every shader is written in TSL (Three Shading Language), which compiles to WGSL or GLSL from a single source. No ShaderMaterial, no onBeforeCompile, nothing that ties the code to one backend.

The page stays honest about cost. The document ships with about two kilobytes of boot script. It checks prefers-reduced-motion first (those users get a designed poster, not a broken page), probes for WebGPU and WebGL2, and guesses a device tier from memory, cores, and pointer type. Only then, on requestIdleCallback, does it pull the three-quarter-megabyte world bundle as an async chunk. Particle counts scale 18k/42k/80k by tier. Device pixel ratio is capped between 1.0 and 1.5. The frame loop suspends entirely when the canvas leaves the viewport or the tab hides.

Scroll as the camera operator

The story and the world share exactly one number: a scroll progress value written by GSAP ScrollTrigger and read by the scene. The camera rides a Catmull-Rom spline through the room, and a second spline drives where it looks. A small catch-up interpolation keeps the dolly cinematic even when the scrollbar jumps. Lenis smooths the scroll itself, but only inside the motion-OK media query branch. Reduced-motion users get native scrolling and fully visible content. Nobody is handed a degraded version of someone else’s preferences.

What’s next

The splat pipeline. Capture, training against a fixed splat budget, cleanup, SOG compression, and a loader that streams the result into the existing world while the point cloud hands over. After that comes the dissolve, a TSL compute pass that takes the room apart particle by particle. That one is the reason this whole architecture exists.