SUNDAY, SEPTEMBER 13, 2026|No. 14901
Technology · 3D Rendering

Three.js Integrates Native Gaussian Splatting for Advanced 3D Rendering

Three.js r186 now features native support for 3D Gaussian Splatting, simplifying the integration of photorealistic point cloud rendering into web applications.

A demonstration of Gaussian Splats rendering complex 3D models within the Three.js environment.
A demonstration of Gaussian Splats rendering complex 3D models within the Three.js environment. · Photo by Mo on Unsplash
1 sources
Pipeline ingest
3 reads
Positive / Neutral / Negative
0 countries
Related coverage

The recent release of Three.js r186 introduces native support for 3D Gaussian Splatting, a significant advancement. While community add-ons previously enabled splat usage in Three.js, they are now a core feature, complete with a built-in mesh type and loaders for major formats.

This post serves as a practical guide, complementing an earlier technical deep-dive on adding native Gaussian Splatting support to Three.js. Here, we'll explore the applications of Gaussian Splats, demonstrate how to load and render them with minimal code, discuss file format choices, and outline the process of converting real-world captures into Three.js-ready splats.

Gaussian Splats

A Gaussian Splat represents a point cloud where each point is an oriented, colored 3D ellipsoid, or "splat," rather than a simple vertex. Rendering millions of these splats, sorted from back to front, creates photorealistic images without the need for traditional meshing, UV unwrapping, or material baking.

This makes splats ideal for capturing real-world objects and scenes with high fidelity, particularly subjects that are challenging to model manually, such as foliage, fur, reflective or translucent surfaces, cluttered environments, and museum artifacts. Because splats are derived directly from photographs rather than hand-authored meshes, the resulting output closely resembles the source material with significantly less reconstruction effort. Once loaded, they can be treated like any other Three.js object, sorted and shaded dynamically each frame.

Tomatoes Gaussian Splat rendered in Three.js

It's important to note a scale limitation: GaussianSplat is designed for single captured objects or room-scale scenes, not entire city blocks. It lacks level-of-detail (LOD) streaming and spatial segmentation or culling. Consequently, city-scale captures or very large splat clouds require manual tiling or reduction for smooth performance. Future tooling can build upon this foundation for large-scene management, as discussed in the implementation post.

Loading an SPZ file

Here's the complete process for loading a .spz file, integrating it into a mesh, and rendering 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 );

} );

This process involves a single loader call, instantiating GaussianSplat with the geometry, and adding it to the scene. As GaussianSplat inherits from THREE.Mesh, it integrates seamlessly with the scene graph, supporting transformations, visibility toggling, and raycasting as expected.

A key requirement is the use of WebGPURenderer. This renderer utilizes TSL nodes and compute shaders for depth sorting, so ensure that both three/webgpu and three/tsl are correctly resolved in your import map.

Picking a file format

Gaussian Splats are available in various file formats, depending on their origin. Three.js provides five loaders to accommodate these:

  • SPZLoader (.spz): Recommended. Niantic's efficient format. Version 4 uses zstd compression and section-by-section streaming for minimal file sizes and rapid loading. It also supports legacy v1–v3 formats (gzip).
  • GaussianSplatPLYLoader (.ply): Highly interoperable. This is the native output format of the original 3D Gaussian Splatting research code and most training/cleanup tools, making it a common format. It's uncompressed and uses per-vertex text/binary, resulting in larger file sizes and slower loading compared to .spz.
  • KSPLATLoader (.ksplat): Used by the GaussianSplats3D viewer. Useful if your assets originate from this pipeline.
  • SPLATLoader (.splat): The original fixed 32-byte-per-splat format (from antimatter15/splat). It's uncompressed and easy to generate but results in large files.
  • GLTFGaussianSplatLoaderExtension (.gltf / .glb): Implements the KHR_gaussian_splatting glTF extension, allowing splats to be included within standard glTF assets alongside meshes, cameras, and animations.

All loaders produce an identical internal BufferGeometry structure with position, covariance, color, and optional sphericalHarmonics1..3 attributes. Therefore, GaussianSplat processes the output from any loader in the same manner.

If you have a choice of format, opt for SPZ version 4 for viewers, as it offers the smallest transfer size and fastest parsing.

Loading PLY splats

Splats are frequently provided as .ply files, being the native output of the original 3D Gaussian Splatting research code and various processing tools. The GaussianSplatPLYLoader handles these, following a similar pattern to SPZLoader and SPLATLoader:

import { GaussianSplatPLYLoader } from 'three/addons/loaders/GaussianSplatPLYLoader.js';
import { GaussianSplat } from 'three/addons/objects/GaussianSplat.js';

const splatGeometry = await new GaussianSplatPLYLoader().loadAsync( 'point_cloud.ply' );
scene.add( new GaussianSplat( splatGeometry ) );

This loader is the appropriate choice when your splat data is exclusively available as a raw .ply export.

Loading glTF splats

For splats embedded within glTF files, an additional setup step is necessary. Since GaussianSplat requires WebGPURenderer, GLTFLoader does not automatically register the glTF splat plugin. You must register it manually:

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { GLTFGaussianSplatLoaderExtension } from 'three/addons/loaders/GLTFGaussianSplatLoaderExtension.js';

const loader = new GLTFLoader();
loader.register( ( parser ) => new GLTFGaussianSplatLoaderExtension( parser ) );

const gltf = await loader.loadAsync( 'scene.gltf' );
scene.add( gltf.scene ); // splat primitives arrive as GaussianSplat instances

Once registered, mesh primitives utilizing KHR_gaussian_splatting will load as GaussianSplat instances (or a Group of them for multi-primitive meshes). They will appear in the returned scene graph alongside other glTF elements like regular meshes, cameras, and animations.

Loading SPLAT and KSPLAT files (legacy formats)

The SPLATLoader and KSPLATLoader are primarily for legacy compatibility, supporting assets and pipelines based on antimatter15/splat and the GaussianSplats3D viewer.

Their APIs closely mirror SPZLoader, allowing for easy switching between them by selecting the correct loader class and file extension:

import { SPLATLoader } from 'three/addons/loaders/SPLATLoader.js';
import { GaussianSplat } from 'three/addons/objects/GaussianSplat.js';

const splatGeometry = await new SPLATLoader().loadAsync( 'model.splat' );
scene.add( new GaussianSplat( splatGeometry ) );
import { KSPLATLoader } from 'three/addons/loaders/KSPLATLoader.js';
import { GaussianSplat } from 'three/addons/objects/GaussianSplat.js';

const splatGeometry = await new KSPLATLoader().loadAsync( 'model.ksplat' );
scene.add( new GaussianSplat( splatGeometry ) );

Both loaders generate the same BufferGeometry structure as SPZLoader, ensuring consistent behavior for GaussianSplat and subsequent rendering processes, regardless of the initial loader used.

Capture, clean up, convert, render

Transforming a real-world subject into a splat within a Three.js scene involves four key steps:

1. Capture

Begin by using a mobile scanning application to capture your subject. Overlapping photos or video footage are processed by the app (or its cloud service) to reconstruct a splat. Several applications are well-suited for this:

  • Polycam: Offers Gaussian Splat capture directly within its mobile app, with cloud-based processing.
  • Scaniverse: Niantic's mobile scanning app provides on-device Gaussian Splat capture and direct export to .spz format.
  • Luma AI: A consumer-focused app for splat capture, with cloud processing.

Any of these applications can generate a usable splat from your captured data.

2. Clean up

Raw reconstructions often contain stray splats, background noise, and rough edges that require trimming. It is advisable to refine these aspects before deployment.

  • Polycam includes built-in tools for cropping and cleanup, which can be convenient if you captured your data with the app and wish to remain within a single application.
  • SuperSplat, a free web-based editor from PlayCanvas, is specifically designed for splat editing. It allows for cropping, erasing stray splats, and re-exporting. It supports splats from various sources, making it a good choice when more control is needed than provided by capture apps, or when the splat originates from a different source.

3. Convert to SPZ

After obtaining a clean splat (typically in .ply or .splat format), convert it to .spz v4 before loading it into Three.js:

  • Niantic's online SPZ converter: Upload your .ply or .splat file and download the converted .spz file.
  • If you used Scaniverse for capture, this step can be bypassed as it directly exports .spz v4.

4. Render

Once you have the .spz file, integrate it into your project and load it using SPZLoader and GaussianSplat, following the example provided earlier in this post.

PAN's pipeline reviewed approximately 1 open sources for this article. No human editor reviewed this article before publication.

Related Reads

Show on timeline →