Skip to content
Tech News
← Back to articles

How to Use Three.js's New Native Gaussian Splats

read original more articles
Why This Matters

Three.js r186 makes 3D Gaussian Splatting a first-class feature, with a built-in mesh type and loaders for major formats, so developers no longer need community add-ons to render photoreal captures on the web. That lowers the barrier for putting real-world scans of objects, rooms, and hard-to-model subjects like foliage or fur into browser-based 3D scenes.

Key Takeaways

The newly released Three.js r186 release adds native 3D Gaussian Splatting support, and it's a big deal: splats have been usable in Three.js for a while through community add-ons, but now they're a first-class citizen of the engine, with a built-in mesh type and loaders for the major formats.

I covered the underlying technical details in an earlier post, Adding Native Gaussian Splatting Support to Three.js. This one is the practical companion: what Gaussian Splats are good for, how to load and render one in a few lines of code, which file format to pick, and how to go from a real-world capture to a splat you can drop into a Three.js scene.

Gaussian Splats#

A Gaussian Splat is a point cloud where every point is a fuzzy, oriented, colored 3D ellipsoid (a "splat") instead of a hard vertex. Render thousands to millions of them, sorted back-to-front, and they blend into a photorealistic image, without any of the meshing, UV unwrapping, or material baking that traditional surface reconstruction needs.

That makes splats a great fit for capturing real-world objects and scenes and showing them in high fidelity, especially subjects that are hard to model by hand: foliage, fur, reflective or translucent surfaces, cluttered rooms, museum artifacts. Because a splat is built directly from photos rather than a hand-authored mesh, the result looks like the source material with a fraction of the traditional reconstruction work, and once it's loaded you treat it like any other object in your Three.js scene: sorted and shaded fresh each frame.

There is a scale limit worth knowing up front, though: GaussianSplat is built for a single captured object or a room-scale scene, not an entire city block. It has no level-of-detail (LOD) streaming and no spatial segmentation or culling, so a city-scale capture or a multi-gigabyte splat cloud needs tiling or reduction by hand before it will run smoothly. Large-scene tooling can sit on top of this foundation later, and I go into that groundwork in the implementation post.

Loading an SPZ file#

Here's the whole pipeline, start to finish: load a .spz file, wrap it in a mesh, and render it.

import * as THREE from 'three/webgpu' ; import { SPZLoader } from 'three/addons/loaders/SPZLoader.js' ; import { GaussianSplat } from 'three/addons/objects/GaussianSplat.js' ; const renderer = new THREE . WebGPURenderer ( ) ; await renderer . init ( ) ; const scene = new THREE . Scene ( ) ; const camera = new THREE . PerspectiveCamera ( 50 , window . innerWidth / window . innerHeight , 0.01 , 100 ) ; camera . position . set ( 0 , 0.3 , 2 ) ; // 1. Load the splat data const splatGeometry = await new SPZLoader ( ) . loadAsync ( 'model.spz' ) ; // 2. Wrap it in a mesh and add it to the scene const splats = new GaussianSplat ( splatGeometry ) ; scene . add ( splats ) ; // 3. Render as usual. The mesh sorts itself every frame by default. renderer . setAnimationLoop ( ( ) => { renderer . render ( scene , camera ) ; } ) ;

That's really all there is to it: one loader call, one new GaussianSplat( geometry ) , and a scene.add() . Because GaussianSplat extends THREE.Mesh , it composes with the rest of the scene graph just like any other object, so transforms, visible , and raycasting groups all work the way you'd expect.

... continue reading