Work-in-progress Gaussian Splat renderer built with Three.js, TypeScript, GLSL, and Web Workers.
This package is experimental and APIs may change between releases.
This repository now has two usage modes:
- Standalone page app (entry at
src/main.ts) - Embeddable viewer classes:
SplatViewer(headless core, no UI)SplatViewerUI(optional overlay UI wrapper)
Inspired by:
- Custom splat pipeline with packed
DataTexturestorage - GLSL splat shading and ellipse projection
- Worker-based depth sorting for transparent blending
- Dynamic instance-count control by percentage
- Camera controls:
- Orbit controls
- WASD translation
- right-drag panning (inverted screen direction)
- Runtime reset and consistency recovery hooks
- Embeddable class API for host applications
- Node.js 20+
npm installnpm run devnpm run buildnpm run previewsrc/main.ts is a deployable standalone entry.
It mounts SplatViewerUI into #app in index.html.
Important packaging/runtime notes:
- Experimental package: expect API and build changes while the renderer evolves.
- Worker limitation:
SplatWorkeris instantiated vianew URL("./SplatWorker.ts", import.meta.url), so bundler behavior is tied to Vite-compatible worker URL handling. - Styles:
SplatVieweris headless and has no UI styles.SplatViewerUIexpects styles to be provided by the consumer. You can either include your own stylesheet or import the package sheet fromthreejs-gs-framework/style.css.
Use this when your app already has its own controls/DOM.
import { SplatViewer } from "./SplatViewer";
const container = document.getElementById("viewer")!;
const viewer = new SplatViewer(container, {
url: "https://media.reshot.ai/models/nike_next/model.splat",
background: 0x111111,
moveSpeed: 3,
enableWASD: true,
enableRightDragPan: true,
});
await viewer.waitUntilReady();
viewer.setInstancePercent(100);
// Later
viewer.dispose();SplatViewer methods:
waitUntilReady(): Promise<void>setInstancePercent(percent: number): numberreset(reason?: string): voidgetStats(): { totalSplats, instanceCount, cameraPosition }dispose(): void
Use this when you want a quick ready-made panel and loading/fps overlays.
import { SplatViewerUI } from "./SplatViewerUI";
const container = document.getElementById("viewer")!;
const viewerUI = new SplatViewerUI(container, {
url: "https://media.reshot.ai/models/nike_next/model.splat",
initialPercent: 100,
});
// Later
viewerUI.dispose();- Orbit: mouse drag
- Translate:
W,A,S,D - Pan: right-click drag
- Reset view: UI reset button
src/classes/SplatLoader.ts: parse/load.splatsrc/classes/SplatPacker.ts: pack splat attributes for GPU texture usagesrc/classes/SplatGeometry.ts: create instanced quad geometry + packed texturesrc/classes/SplatMaterial.ts: shader material and uniformssrc/classes/SplatRenderer.ts: orchestration, async readiness, sort/update/reset APIssrc/classes/SplatWorker.ts: worker-side depth sort
src/SplatViewer.ts: headless embeddable viewer classsrc/SplatViewerUI.ts: opt-in overlay UI wrapper aroundSplatViewersrc/main.ts: standalone page bootstrap
src/shaders/splat.vertsrc/shaders/splat.frag
- Load binary splat buffer.
- Pack splat data into integer texture storage.
- Render instanced quads, one per splat.
- Project covariance to screen-space ellipse in vertex shader.
- Apply Gaussian falloff in fragment shader.
- Keep transparent blending stable using worker depth sorting.
- This is still a learning project and API surface may change.
- Remote model URLs are subject to CORS/network constraints.
- Performance depends heavily on GPU/browser and splat count.