The framework under deck.gl just shipped a Gaussian splat renderer

Screenshot: luma.gl Gaussian Splat Viewer, rendering Spark's Coit Tower RAD capture · Tools

Aug 26, 2026, 12:40 PM ETTools
vis.gl has published a Gaussian splat module for luma.gl, the WebGPU and WebGL framework that deck.gl is built on. The package went up as a beta on Friday and a third build followed on Monday; it is MIT-licensed, it is about four hundred kilobytes of TypeScript, and the part that matters is that it reads a file format someone else designed.
Why it matters: Spark, World Labs' three.js splat renderer, introduced the .RAD format with its 2.0 release: a precomputed level-of-detail tree stored so that a viewer can pull arbitrary chunks over HTTP range requests instead of downloading a scene whole. It is a good design, and until now it had one implementation — the one that shipped with it.
A format with one implementation is a file layout. A format with two is closer to a standard. luma.gl reading RAD means a capture authored for Spark can now be rendered by the stack that deck.gl sits on, which is a different audience entirely: geospatial dashboards, not three.js scenes.
What shipped: The module exports eighteen source files' worth of machinery, and the division of labour is legible from the names alone. `SplatRADHierarchyManager` walks the authored per-row child links and decides what to refine based on where the camera is. `SplatResidencyManager` decides what to evict. `GPUPagedSplatRenderer` projects sparse rows, evaluates spherical harmonics to degree three, does one radix ordering across pages, and writes its own indirect draw commands on the GPU.
There is also `splat-gltf.ts`, which reads splat primitives out of glTF — the Khronos extension route rather than the Spark one — plus GPU picking, semantic filtering and a covariance projection helper. The viewer offers four backends: WebGPU in three capability tiers, and a WebGL2 fallback.
The out-of-core work landed in a single pull request earlier in the month that added 6,140 lines across eighteen files. Its verification section reports 2,007 Node tests and 1,953 Chromium browser tests passing, with hardware WebGPU regressions covering interleaved cross-page transparency, exact global depth ordering and forced 12-KiB binding limits.
By the numbers:
- The Coit Tower scene in the shipped demo reports 50,937,127 rows. Loading it on a laptop this morning, the viewer's own readout said: 15,454 active rows against a 1,000,000-row budget, 983,040 resident rows across 15 pages, 2 pages pending, 205 fallback rows, 24.7 ms for the last refinement slice.
- 15 of 778 source batches had been fetched at that point. The picture on screen was a recognisable San Francisco.
- The asset file is named coit-40m. The hierarchy reports 50.9 million rows because Spark's tree stores merged, downsampled parent nodes above the original splats — the extra ten million are interior nodes, not captured Gaussians.
- A 128-MiB binding limit supports roughly 2.8 million rows in the contiguous renderer, and up to 33,554,432 simultaneously active four-byte global-sort references in the segmented one.
- The other scenes in the picker are conventional PLY captures from the public Hugging Face catalog — Train, Truck, Dr Johnson, Playroom — at 741,883 splats and 184 MB for the largest.
Yes, but: This is a beta, and the pull request that added it is unusually candid about what it does not claim. It says outright that traversing the Coit source through a bounded residency window "does not claim all authored rows fit in memory or that Spark visual/performance parity has been benchmarked." It notes that the published loaders.gl decodes RAD pages on the main thread and that applications wanting worker decoding must supply their own bridge. It notes that the segmented renderer has no GPU picker yet.
The bounded window is visible in the output. At a thousandth of the capture resident and a fraction of that drawn, the render is soft — the tower reads as a tower, the streets read as streets, and the detail that fifty million Gaussians presumably encode is somewhere else at that moment.
The independence of the implementation also has an asterisk. A refactor merged on Friday replaced what its author described as a "close Rust-derived foveation translation" with an independently structured version, and narrowed the Spark attribution in that file to a piece of MIT-licensed GLSL. Read plainly: the first cut was closer to a port than a reimplementation, and someone went back and did it properly.
The big picture: Splat formats have been proliferating faster than anyone can implement them — PLY, SPZ, SPLAT, KSPLAT, SOG, the Khronos glTF extension, and now RAD. Most will not survive. The ones that do will be the ones that get read by software their authors did not write.
That this particular reimplementation happened inside vis.gl is the part worth watching. luma.gl's users are not making three.js demos; they are drawing millions of data points on maps. A splat is just another primitive to that crowd, and out-of-core paging is a problem they solved for point clouds a decade ago.



