Profile
Back to NewsBack
GitHub Trending 5 min
Reader Mode
WilliamLiu-1997/Gaussian-Splat-Lite: High performance 3D Gaussian Splatting (3DGS) renderer for Three.js. WebGPU and WebGL2. Supports PLY, SPZ, Streaming LOD including SOG and RAD.

WilliamLiu-1997/Gaussian-Splat-Lite: High performance 3D Gaussian Splatting (3DGS) renderer for Three.js. WebGPU and WebGL2. Supports PLY, SPZ, Streaming LOD including SOG and RAD.

11 hours ago

Features

| Focus | What you get | | --- | --- | | WebGPU / WebGL2 | Use the same Three.js scene API with either renderer | | Large-scene streaming | Load RAD and SOG detail as the camera moves, with smooth transitions | | Stochastic rendering | Optional transparency mode, with built-in temporal anti-aliasing to smooth it | | Offscreen capture | Render Splats to images, cube maps, and environment maps for reflections | | SDF edits | Recolor or hide parts of a model without moving Splats | | Data and precision | Load URLs, files, or bytes; place local models in large GIS/ECEF scenes |

Installation

npm install gaussian-splat-lite three

Requires Three.js >=0.186.0.

Quick start

SplatMesh is a scene object. Add one GaussianSplatRenderer to display all visible Splat models in the scene.

WebGPU

import * as THREE from "three";
import { WebGPURenderer } from "three/webgpu";
import { GaussianSplatRenderer, SplatMesh } from "gaussian-splat-lite";

const renderer = new WebGPURenderer({ antialias: false }); await renderer.init(); // Initialize before constructing GaussianSplatRenderer. renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement);

const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.1, 1000, ); camera.position.set(0, 0, 3);

const splatRenderer = new GaussianSplatRenderer({ renderer, }); scene.add(splatRenderer);

const splat = new SplatMesh({ url: "/assets/scene.spz" }); scene.add(splat); await splat.initialized;

renderer.setAnimationLoop(() => renderer.render(scene, camera)); window.addEventListener("resize", () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); });

WebGL2

WebGPURenderer automatically falls back to its WebGL2 backend when WebGPU is unavailable. To choose WebGL2 explicitly:

const renderer = new WebGPURenderer({ antialias: false, forceWebGL: true });
await renderer.init();

For the classic WebGL renderer, replace the WebGPU renderer creation and initialization with:

const renderer = new THREE.WebGLRenderer({ antialias: false });

Keep the rest of the example unchanged.

Stochastic rendering

Sorted alpha blending is the default. To enable stochastic rendering:

splatRenderer.stochastic = true;

This mode produces visible noise. Smooth it with the library's temporal anti-aliasing: TAAPass for WebGLRenderer, and TAANode for WebGPURenderer (including its WebGL2 fallback). See Stochastic rendering for the setup.

Streaming large scenes

Use RadStreamScheduler for RAD scenes with levels of detail (LOD), or SogStreamScheduler for SOG lod-meta.json scenes. Both load detail as the camera moves and work on WebGPU and WebGL2.

For RAD, replace the SplatMesh loading and animation loop in the quick start with:

import { RadStreamScheduler } from "gaussian-splat-lite";

const streaming = new RadStreamScheduler({ url: "/assets/scene.rad", splatBudget: 3_000_000, fadeDurationMs: 200, // Smooth LOD transitions (default). }); scene.add(streaming.group); streaming.setCamera(camera); streaming.setResolutionFromRenderer(camera, renderer); // CSS pixels; again after resizing. await streaming.initialized;

renderer.setAnimationLoop(() => { streaming.update(); renderer.render(scene, camera); });

// The update loop must be running before awaiting the first visible data. await streaming.firstRenderable;

// When removing the model: // streaming.dispose(); // streaming.group.removeFromParent();

For streamed SOG, use this constructor. Register the camera with setCamera(camera) and call streaming.update() before rendering each frame; SOG detail follows distance, so it needs no resolution. The group and readiness lifecycle are the same:

import { SogStreamScheduler } from "gaussian-splat-lite";

const streaming = new SogStreamScheduler({ url: "/assets/scene/lod-meta.json", splatBudget: 3_000_000, fadeDurationMs: 200, // Smooth LOD transitions (default). });

Documentation

Rendering

Models Large scenes

Development

Requires Node.js 20.19+ or 22.12+, Rust via rustup, and the wasm32-unknown-unknown target. build:wasm installs wasm-pack through Cargo if needed.

npm ci
npm run build:wasm
npm run dev

Open the URL printed by Vite (normally http://localhost:8080/) and drop a .ply, .spz, .sog, or .rad file into the viewer, choose a local file, or load one from an HTTP(S) URL. For split SOG, select or drop meta.json together with its .webp images; for split RAD, include the header and its .radc pages. Files are decoded locally. Choose WebGPU / WebGL2 / WebGPU · WebGL2 in the viewer to compare backends.

See Contributing for validation commands, and Architecture for how the library is built internally. npm run build emits ESM, CommonJS, declarations, and source maps in dist/.

Acknowledgements

The overall architecture of Gaussian Splat Lite draws on Spark and SuperSplat.

Example model adapted from shehabmekky (CC BY 4.0).

License

Licensed under Apache 2.0. See NOTICE and third-party licenses for attribution.

Chat with me