How it works
One light path per pixel, per frame. Each frame a WebGPU compute shader starts a ray at the camera for every pixel and follows it through the scene. At each surface it picks one way to continue, by reflecting, refracting or scattering, according to the material. The path ends when it escapes to the sky or fades out. At every bounce the shader also aims a shadow ray at a light it chose on purpose, and weighs that against stumbling onto the light by chance (multiple importance sampling), so small, bright lights don’t turn into confetti. Each pixel shows the running average of its paths. Noise falls with the square root of the sample count: four times the samples, half the grain.
Finding what a ray hits is most of the work. Testing a ray against all 870,616 triangles of the dragon would mean 870,616 tests. Instead the triangles are sorted into a bounding volume hierarchy, a tree of nested boxes that your browser builds when the scene loads, in about two seconds. A ray opens only the boxes it passes through, so a typical ray here visits a few dozen boxes and tests a handful of triangles. Each node stores the boxes of both its children, so one 64-byte read tells the shader which side to try first. Tick X-ray to see this work pixel by pixel: silhouettes and grazing angles are where it piles up.
Materials. Metals and glazes use GGX microfacets, sampled by the facets the eye can actually see. Glass chooses between reflecting and refracting with the Fresnel equations, and amber absorbs more the further light travels inside it. Jade and wax are glass filled with a cloudy medium: light inside takes a random walk, bouncing off particles until it finds its way out.
Places. Each place is a high-dynamic-range photograph that wraps all the way around the scene and lights it, so a gold dragon in Venice reflects the sunset and the promenade. The ground is a trick borrowed from visual effects. The floor takes its colour from the photo itself, looked up from the height the camera stood at, so where it’s lit it matches the photograph exactly, and the dragon still casts real shadows into it.
Why a hand-written shader?
Browsers can’t reach the ray-tracing hardware in today’s GPUs, because WebGPU doesn’t expose it. So every path tracer on the web, library or not, finds what its rays hit in ordinary shader code, and the question is how well that code fits the job.
The best-known library, three-gpu-pathtracer, also runs on WebGPU compute shaders, generated from three.js’s node system. Given the dragon on a plain studio backdrop at the same size, bounce limit, lighting and materials, on the same laptop:
| Clay | Gold | |
|---|---|---|
| This page: one hand-written WGSL kernel | 52 | 50 |
| three-gpu-pathtracer 0.0.26, wavefront backend | 17.6 | 18.0 |
| three-gpu-pathtracer 0.0.26, megakernel backend | 2.4 | 2.4 |
About three times as fast, but the comparison flatters the custom shader. The library handles textures, the full glTF material model, any number of lights, animated meshes and denoising. This page handles what its four scenes need and nothing more, and every feature left out is registers and branches the GPU doesn’t have to carry. The library also spends its samples more cleverly. It draws them from blue-noise-dithered Sobol sequences, where this page uses plain random numbers, so it reaches a given level of noise in fewer samples than raw speed suggests.
So the lesson is less that custom beats libraries than that specialised beats general: one fixed material model, a BVH laid out for exactly one traversal loop, and that loop written by hand. If you need the features, the library is the better deal.
Credits
Dragon, Happy Buddha, Lucy, Armadillo and bunny: Stanford 3D Scanning Repository. Spot: Keenan Crane (CC0). Marble bust, horse statue and rubber duck: Poly Haven (CC0). The teapot is tessellated from Martin Newell’s patches by three.js’s TeapotGeometry. Places, from Poly Haven (CC0): Venice Sunset and Shanghai Bund by Greg Zaal, Brown Photostudio 02 and Studio Small 09 by Sergej Majboroda, and Kloofendal 48d Partly Cloudy by Greg Zaal and Jarod Guest. Cornell box dimensions and reflectances: Cornell Program of Computer Graphics. The spheres recreate the cover of Peter Shirley’s Ray Tracing in One Weekend. Interface colours sampled from a GretagMacbeth ColorChecker.