Introduction
Per-format reference docs — deep-dive format specs and design logs for every source format this project reads.
Formats
See 3D Tiles format for the full writeup of 3D Tiles 1.1
itself (implicit tiling, KHR_gaussian_splatting, REPLACE vs ADD refinement, QUADTREE vs OCTREE).
COPC (Cloud Optimized Point Cloud) — A LAZ 1.4 file with a specific VLR/EVLR layout that embeds an octree hierarchy. Each octree node is a contiguous chunk at a known offset, enabling HTTP range requests without a separate index. Internally organized in the same way as 3D Tiles implicit tiling — a direct 1:1 mapping. CRS is stored as WKT in a VLR (LASF_Projection record 2112). Not all files include it (e.g. the Autzen test file does not), in which case the output is in local dataset coordinates.
RAD (SparkJS Gaussian Splat LoD) — SparkJS binary streaming format for BhattLod Gaussian
splat scenes. See structure in the rad-to-3dtiles section
of CLI usage, and rad-format-notes.md for the full format
writeup (Spark 2.0 LoD system, PackedSplats/ExtSplats byte layouts, geometric error tuning).
References
Local notes (in docs-references/)
| File | Description |
|---|---|
3d-tiles-format.md | 3D Tiles 1.1 itself — implicit tiling, REPLACE vs ADD refinement, QUADTREE vs OCTREE |
rad-format-notes.md | RAD (SparkJS Gaussian Splat LoD) — Spark 2.0 LoD system, PackedSplats/ExtSplats byte layouts, geometric error/bounding-volume tuning |
KHR_gaussian_splatting.md | KHR_gaussian_splatting glTF extension spec (RC), plus the KHR_gaussian_splatting_compression_spz_2 SPZ-compression child extension (Draft) as a subsection |
spz-format.md | SPZ v2/v3 binary format — per-field encoding, pack/unpack formulas (source: github.com/nianticlabs/spz) |
sog.md | SOG (Spatially Ordered Gaussians) chunk format — WebP textures + dequant (positions/quats/scales/SH); consumed by sog-to-3dtiles (source: developer.playcanvas.com) |
streamed-sog.md | PlayCanvas Streamed SOG LOD format — lod-meta.json spatial tree + chunk references; the input to sog-to-3dtiles (source: developer.playcanvas.com) |
lcc-format.md | XGRIDS LCC (Lixel CyberColor) — meta.lcc/Index.bin/Data.bin (32 B/splat, 10-10-10-2 rot, packed-11 SH); input to lcc-to-3dtiles (source: github.com/xgrids/LCCWhitepaper). Attribution required. |
potree-format.md | Potree 1.x (cloud.js + .hrc/.bin/.laz) and 2.0 (binary octree) — input to potree-to-3dtiles. Key: 1.x decode is POSITION·scale + the NODE's box.min (node-relative), with the createChildAABB octant convention bit0→Z/bit1→Y/bit2→X. cloud.js can carry a proj4 CRS. Sources: potree repo docs/ (pinned commits). |
nexus-format.md | 🟠 CNR-ISTI Nexus multiresolution mesh (DAG of patches) — doc-referenced, no converter (mesh, not splats; niche). Refs: github.com/cnr-isti-vclab/nexus, FastDec paper |
i3s-spec.md | 🟡 Esri I3S (Indexed 3D Scene Layer / SLPK) — draft converter i3s-to-3dtiles/ (validated on Buildings_NewYork_v18.slpk → 5882 tiles, see below): explicit nodepages + uncompressed DefaultGeometrySchema mesh / point cloud only. Ref: github.com/esri/i3s-spec |
The Design log (live-streaming harmonization — lazy hierarchy, the
cache = none/hierarchy/fullcontinuum, push/pull, per-format wiring + parallel hierarchy build, plus a running historical record of completed migrations) lives in the Guide, a companion narrative to "Architecture: the materialization continuum" rather than a per-format reference doc.
Upstream specs & reference implementations
glTF extensions (Khronos)
KHR_gaussian_splatting— PR #2490 — the base extension; 555-comment thread documenting the design (attribute set, namespacing, shape/rendering hints, SH handling, the split from the original single extension). Merged Jan 2026.KHR_gaussian_splatting_compression_spz_2— PR #2531 — the SPZ-compression child extension, pinned to SPZ v2.
CesiumJS implementation
- Issue #12837 — "Updating to latest 3D Gaussian Extensions and deprecating initial experimental version" (deprecation/removal timeline: deprecated Sep 2025, removed Nov 2025 / v1.135).
- PR #12843 — adds support for
KHR_gaussian_splatting+KHR_gaussian_splatting_compression_spz_2, deprecatesKHR_spz_gaussian_splats_compression. Confirms CesiumJS decodes via the Nianticspzlibrary.
SPZ format & tooling
- nianticlabs/spz — reference C++ SPZ codec. The v2 vs v3 rotation split lives in
src/cc/load-spz.cc(packQuaternionFirstThree= v2 / 3-byte,packQuaternionSmallestThree= v3 / 4-byte). - @sparkjsdev/spark — SparkJS; writes SPZ v3 by default but version-dispatches on read (handles v2 and v3).
- javagl/JSplat — Java reference for writing splat glTFs that CesiumJS reads (
GltfSpzSplatWriter); a useful cross-check when updating an SPZ writer.
PlayCanvas SOG / Streamed SOG (used by sog-to-3dtiles)
- playcanvas/splat-transform — SplatTransform: PlayCanvas's splat conversion library/CLI.
sog-to-3dtilesuses itsreadSog(CPUwebp.wasmdecode) to turn each SOG chunk into aDataTable. Authors Streamed SOG datasets too. - SOG format spec — local notes:
sog.md. - Streamed SOG (LOD) spec — local notes:
streamed-sog.md.
3DTilesRendererJS Gaussian-splat ecosystem (WilliamLiu-1997) — closest reference implementations to this repo's RAD path; studied to improve tiling:
-
3DTilesRendererJS-3DGS-Plugin — the
3d-tiles-rendererjs-3dgs-pluginthis viewer uses to render splats in 3DTRJS (parsesKHR_gaussian_splatting+_spz_2, renders via SparkJS, one shared Spark renderer per scene/renderer, camera-relative rebasing,sparkRendererOptions, WebXR-aware). Itsdata/gaussianSplat1&gaussianSplat2are wired into this viewer's preset bar (◈ GSplat 1/2). Note its rendering tip: keep the globe in the opaque pass so it doesn't object-sort against transparent splats at the horizon. -
3DGS-PLY-3DTiles-Converter — PLY→3D Tiles converter (kd-tree tiling, AABB bounds, SPZ v3 + quantized SH). Its
src/tiling/geometric-error.jsis the model our uniform-per-depth geometric error mirrors:GE(depth) = leafReference × (1/samplingRate)^(maxDepth−depth), anchored to a deepest-leaf "sparsity + extent" estimate, all siblings at a level sharing one error. Each tileset also emits abuild_summary.jsonexposing the full GE-by-depth and sampling schedule — handy for cross-checking. (Issue #24: auto-depth can overload the root node on >8M-splat scenes — a tree-balancing concern worth noting for very large RAD inputs.) -
3DTiles-Inspector — standalone web inspector for 3D Tiles / Gaussian-splat tilesets (root-transform edit, geometric-error & layer-multiplier scaling
1/16x–16x, splat crop regions,build_summary.jsonround-trip). A good complement to this repo'sviewer.html🪲 Debug-tiles overlay.The inspector is pre-installed in
tools-inspector/— run via npm scripts (no global install needed):cd tools-inspector npm run inspect:tastier # tastier500k RAD tileset npm run inspect:coit # coit RAD tileset (large, 778 tiles) npm run inspect:coit-s050 # coit σ×0.5 scale variantOr run directly from the repo root with
npx:npx --prefix tools-inspector 3dtiles-inspector sample-data/output/rad/tastier500k-3dtiles-from-rad npx --prefix tools-inspector 3dtiles-inspector sample-data/output/rad/coit-3dtiles-from-radEach command starts a localhost HTTP server and opens the inspector in your default browser. Its
Geometric Error/Layer Multipliersliders are the runtime analogue of this converter's uniform-per-depth error model — handy for confirming LoD behaviour without re-tiling.
Splatter (splatter.app, Jakub Červený) — a hosted Gaussian-splat viewer/host (tested against a
103M-splat scene). Its splatter-three library's actual source lives in
splatter-app/splatter-three, a private repo —
nothing beyond its published, intentionally-public consumer demo is reverse-engineered or reproduced
here. From splatter-app/three-demo's main.js (the
only public source), the observable public API surface:
new Splatter(renderer, { splatId })— content addressed by an opaque ID, loaded/streamed independent of the Three.js scene graph;renderer.render(scene, camera)andsplatter.render(camera, controls.target)are called separately per frame.- Progressive loading via
'update'(redraw on each streamed chunk) and'loaded'(totalLoaded, numDisplayed)events — their own demo hides a loading spinner oncenumDisplayed > 1e6, well beforetotalLoadedfinishes, i.e. partial-scene display while still streaming (conceptually close to this repo's own SOG octree LOD, though the wire format itself isn't public). splatter.setTransform(matrix)positions the splat cloud independent of the rest of the scene.splatter.addUniform(type, name)+splatter.setShaderEffect(glsl)— a small GLSL-injection point operating on per-splatposition/scale(their demo uses it for a radial reveal-on-load animation) without needing to fork the splat renderer itself.splatter.hitTest(camera, [x, y], { alphaThreshold })— screen-space picking weighted by actual rendered alpha, not just geometry — used for a recenter-on-double-click interaction.- Requires an explicit WebGL2 context (
canvas.getContext('webgl2', options)), passed intonew THREE.WebGLRenderer({ canvas, context }).
Other specs (no dedicated converter row above)
Extension/format specs referenced by the tables above but not themselves a distinct converter target:
| Format | Canonical spec(s) | Local mirror |
|---|---|---|
| SPZ / glTF splat exts | SPZ · KHR_gaussian_splatting | spz-format.md · KHR_gaussian_splatting.md |
| USD splats (ParticleField) | OpenUSD v26.03 · ParticleField3DGaussianSplat | usd-gaussian-splats.md |
| glTF 2.1 (complex scenes) | Khronos blog · 64-bit GLB / indices #2594 | gltf-2.1-complex-scenes.md |