Built by World Labs.
- Integrates with THREE.js rendering pipeline to fuse splat and mesh-based objects
- Portable: Works across almost all devices, targeting 98%+ WebGL2 support
- Renders fast even on low-powered mobile devices
- Render multiple splat objects together with correct sorting
- Most major splat file formats supported including: .PLY (also compressed), .SPZ, .SPLAT, .KSPLAT, .SOG
- Render multiple viewpoints simultaneously
- Fully dynamic: each splat can be transformed and edited for animation
- Real-time splat color editing, displacement, and skeletal animation
- Shader graph system to dynamically create/edit splats on the GPU
Check out all the examples
Copy the following code into an index.html file.
<style> body {margin: 0;} </style>
<script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/",
"@sparkjsdev/spark": "https://sparkjs.dev/releases/spark/2.1.0/spark.module.js"
}
}
</script>
<script type="module">
import * as THREE from "three";
import { SparkRenderer, SplatMesh } from "@sparkjsdev/spark";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.01, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement)
const spark = new SparkRenderer({ renderer });
scene.add(spark);
const splatURL = "https://sparkjs.dev/assets/splats/butterfly.spz";
const butterfly = new SplatMesh({ url: splatURL });
butterfly.quaternion.set(1, 0, 0, 0);
butterfly.position.set(0, 0, -3);
scene.add(butterfly);
renderer.setAnimationLoop(function animate(time) {
renderer.render(scene, camera);
butterfly.rotation.y += 0.01;
});
</script><script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/",
"@sparkjsdev/spark": "https://sparkjs.dev/releases/spark/2.1.0/spark.module.js"
}
}
</script>npm install @sparkjsdev/sparkUse the asynchronous SDK entry point when one application should support both
rendering backends. auto tries WebGPU first and creates the original WebGL2
SparkRenderer implementation when WebGPU initialization fails.
import * as THREE from "three";
import { createSparkRenderer } from "@sparkjsdev/spark";
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.1, 1000);
const spark = await createSparkRenderer({
backend: "auto", // "webgpu", "webgl2", or "auto"
onFallback: (error) => console.warn("Using WebGL2", error),
});
spark.setSize(innerWidth, innerHeight);
spark.setPixelRatio(devicePixelRatio); // Clamped by the active device profile.
document.body.appendChild(spark.renderer.domElement);
const splat = await spark.createSplatMesh({
url: "https://sparkjs.dev/assets/splats/butterfly.spz",
webgpu: {
// Optional WebGPU-only overrides. Mobile/desktop defaults are automatic.
gpuSortMaxSplats: 2_000_000,
},
});
scene.add(splat);
spark.renderer.setAnimationLoop(() => spark.render(scene, camera));Setting backend: "webgpu" is strict and reports initialization errors instead
of falling back. Setting backend: "webgl2" skips WebGPU detection. Existing
new SparkRenderer(...) and new SplatMesh(...) code remains supported.
gpuSortMaxSplats is a sorting budget rather than a loading limit; larger models
remain renderable and automatically fall back to an asynchronous Worker radix
sort while preserving back-to-front blending order.
Counting sort never uses fewer than 65,536 depth buckets. If that precision is
too expensive, Spark switches sorting backends instead of reducing visual quality.
Install Rust if it's not already installed in your machine.
Next, build Spark by running:
npm install
npm run build:wasm
npm run build
This will first build the Rust Wasm component (npm run build:wasm), then Spark itself (npm run build).
The examples fetch assets from a remote URL. This step is optional, but offline development and faster loading times are possible if you download and cache the assets files locally with the following command:
npm run assets:download
Once you've built Spark and optionally downloaded the assets, you can now run the examples:
npm start
This will run a dev server by default at http://localhost:8080/. Check the console log output to see if yours is served on a different port.
First try cleaning all the build files and re-building everything:
npm run clean
npm install
npm run build:wasm
npm run build
There's no versioning system for assets. If you need to re-download a specific file you can delete that asset file individually or download all assets from scratch:
npm run assets:clean
npm run assets:download
To ignore the dist directory and prevent accidental commits and merge conflicts
git update-index --assume-unchanged dist/*
To revert and be able to commit into to the dist directory again:
git update-index --no-assume-unchanged dist/*
To list ignored files in case of need to troubleshoot
git ls-files -v | grep '^[a-z]' | cut -c3-
Install Mkdocs Material
pip install mkdocs-material
If you hit an externally managed environment error on macOS and if you installed python via brew try:
brew install mkdocs-material
Edit markdown in /docs directory
npm run docs
Build the static site and docs in a site directory.
npm run site:build
You can run any static server in the site directory but for convenience you can run
npm run site:serve
The following command will generate a static site from the docs directory and push it to the repo that hosts the site via gh-pages
npm run site:deploy
To compress a splat to spz run
npm run assets:compress <file or URL to ply>