@gameable/splat
Classes
AnimatedSplat
A dynamic splat: SplatSink plus lifecycle.
Implements
Constructors
Constructor
ts
new AnimatedSplat(renderer, options): AnimatedSplat;Use createAnimatedSplat; the GPU buffers must be acquired before the object is usable, and that needs an initialised renderer.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | An initialised WebGPURenderer, on WebGPU or the WebGL2 fallback. |
options | CreateAnimatedSplatOptions | Capacity, bounds and draw order. |
Returns
Properties
capacity
ts
readonly capacity: number;Total gaussians this sink can hold. Fixed for its lifetime.
Implementation of
nodes
ts
readonly nodes: SplatStorageNodes;The four buffers as writable TSL storage nodes, on either backend.
Implementation of
shBuffers
ts
readonly shBuffers: readonly GPUBuffer[];One GPUBuffer per spherical-harmonics band asked for (shDegree): band 1 is 3 words a gaussian, band 2 is 4, band 3 is 6, in three's packing, storageCapacity gaussians each. Empty without bands, and on the WebGL fallback.
splat
ts
readonly splat: AnimatedGaussianSplat;The fork instance. Typed as the fork, not as Object3D, for object3D consumers.
storageCapacity
ts
readonly storageCapacity: number;Elements actually allocated per buffer: capacity padded up to the WebGL fallback's PBO texture shape. The slots past capacity are never handed out and stay invisible. A compute on the fallback dispatches exactly this many invocations.
Implementation of
Accessors
available
Get Signature
ts
get available(): number;Slots not handed out. Named available, because free is the method above.
Returns
number
The count.
buffers
Get Signature
ts
get buffers(): SplatGPUBuffers;The four GPU buffers. WebGPU only.
Throws
On the WebGL fallback, which has no GPUBuffers; use nodes.
Returns
The buffers, acquired at construction.
The four GPU buffers, in the layout documented in backendBuffers.ts. WebGPU only: on the WebGL fallback reading this throws; write through SplatSink.nodes instead.
Implementation of
largestFree
Get Signature
ts
get largestFree(): number;The longest run of free slots, the most one allocate can give.
Returns
number
The count.
object3D
Get Signature
ts
get object3D(): Object3D;The scene object.
Returns
Object3D
The fork instance, which is an ordinary Object3D.
The scene object. Add it to a scene, move it, parent it — it is an ordinary Object3D.
Implementation of
used
Get Signature
ts
get used(): number;Slots currently handed out.
Returns
number
The count.
Methods
allocate()
ts
allocate(count): SlotRange;Reserve a contiguous run of slots.
Parameters
| Parameter | Type | Description |
|---|---|---|
count | number | How many gaussians. |
Returns
The range.
Implementation of
clearSlots()
ts
clearSlots(range?): void;Zero a slot range, which makes it invisible: a colour word of 0 is alpha 0.
This is a queue.writeBuffer of zeros, which is allowed here precisely because it is not a per-frame operation — it runs when a branch is allocated or retired. A producer must never upload gaussian data this way; that is what the compute pass is for.
Parameters
| Parameter | Type | Description |
|---|---|---|
range? | SlotRange | The range to clear. Defaults to the whole capacity. |
Returns
void
Nothing.
dispose()
ts
dispose(): void;Detach from the scene and release the geometry, material and every storage buffer.
"Every storage buffer" is nine of them (more with bands, rest points or a place's projected squares): the four gaussian buffers, the spherical-harmonics contribution buffer if the pre-pass ever ran, and the four the sort holds. Three frees none of them on its own — BufferAttribute.dispose() dispatches an event nothing listens for on a storage attribute — so a despawned character used to leak about 2 MB of GPU memory.
Idempotent, and every mutating method throws afterwards.
Returns
void
Nothing.
free()
ts
free(range): void;Give a range back.
Parameters
| Parameter | Type | Description |
|---|---|---|
range | SlotRange | A range from AnimatedSplat.allocate. |
Returns
void
Nothing.
Implementation of
markGaussiansChanged()
ts
markGaussiansChanged(): void;Force a re-sort on the next frame.
On the WebGL fallback this asks for a readback of the centres instead (at most one every 50 ms); the sort runs when it lands. Calls while one is in flight coalesce into the next.
Returns
void
Nothing.
Implementation of
SplatSink.markGaussiansChanged
releaseCpuCopies()
ts
releaseCpuCopies(): object;Let go of the CPU copies three keeps of buffers the GPU already holds: the zero-filled arrays the capacity was uploaded from, the sort's, the colour pass's. A producer that writes the GPU buffers itself never needs them, and at millions of gaussians they are hundreds of megabytes. Buffers not yet on the GPU (the sort's before its first frame) stay: call again after a frame until waiting is 0. Nothing happens on the WebGL fallback, whose transform feedback and readback keep the arrays.
Returns
object
Bytes freed now, and how many buffers are still waiting for their first frame.
bytes
ts
bytes: number;waiting
ts
waiting: number;restPoints()
ts
restPoints(): GPUBuffer;The GPU buffer of each point's rest centre and group, for a point hook (AnimatedGaussianSplat.setPointHook): a vec4 a slot, xyz the rest centre in the splat's own space, w the group. Allocated on the first call (16 bytes a slot); the producer writes it with device.queue.writeBuffer. WebGPU only: on the WebGL fallback it throws (the hook reads center for the rest centre there).
Returns
GPUBuffer
The buffer.
Throws
On the WebGL fallback.
Example
ts
const rest = sink.restPoints();
device.queue.writeBuffer(rest, range.offset * 16, restData);Implementation of
setBoundingSphere()
ts
setBoundingSphere(center, radius): void;Declare where the gaussians are, in local space.
Parameters
| Parameter | Type | Description |
|---|---|---|
center | Vec3Tuple | Local-space centre. |
radius | number | Local-space radius. |
Returns
void
Nothing.
Implementation of
setColorScale()
ts
setColorScale(
r,
g,
b
): void;Multiply every gaussian's colour, in linear light after the colour-space decode: a tint and an exposure, to fit a character into a scene's light. (1, 1, 1) is the file's colour. Set it as often as you like (a uniform).
Parameters
| Parameter | Type | Description |
|---|---|---|
r | number | Red multiplier. |
g | number | Green multiplier. |
b | number | Blue multiplier. |
Returns
void
Nothing.
setFragmentAlpha()
ts
setFragmentAlpha(builder): void;Scale each fragment's opacity by a node built from the splat's view-space centre, or pass null to draw as before. See the fork's edit 15.
Parameters
| Parameter | Type | Description |
|---|---|---|
builder | ((view) => Node) | null | Returns a float node from the view-space centre (a vec3 node). |
Returns
void
Nothing.
SlotAllocationError
An allocation request that cannot be satisfied.
Extends
Error
Constructors
Constructor
ts
new SlotAllocationError(
message,
requested,
largestFree
): SlotAllocationError;Build a slot allocation error.
Parameters
| Parameter | Type | Description |
|---|---|---|
message | string | What went wrong. |
requested | number | Slots requested. |
largestFree | number | Largest free run at the time. |
Returns
Overrides
ts
Error.constructorProperties
largestFree
ts
readonly largestFree: number;The largest run available at the time.
requested
ts
readonly requested: number;Slots that were asked for.
SplatLoadError
A splat file that could not be decoded.
Extends
Error
Constructors
Constructor
ts
new SplatLoadError(
url,
message,
options?
): SplatLoadError;Build a splat load error.
Parameters
| Parameter | Type | Description |
|---|---|---|
url | string | The URL that failed. |
message | string | What went wrong. |
options? | ErrorOptions | Standard Error options, used to keep the cause. |
Returns
Overrides
ts
Error.constructorProperties
url
ts
readonly url: string;The URL that failed.
SplatPlace
A streaming place in the scene.
Constructors
Constructor
ts
new SplatPlace(
renderer,
index,
source,
options?,
ready?
): SplatPlace;Use loadSplatPlace.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | An initialised WebGPURenderer on the WebGPU backend. |
index | PlaceIndex | The place's index. |
source | PlaceSource | Where its files come from. |
options | SplatPlaceOptions | Budget and the rest. |
ready | { metas?: ReadonlyMap<number, SogMeta>; pool?: SogWorkerPool; } | Workers already started and metas already fetched (by loadSplatPlace, so they get going while the index is read and before this constructor makes the splat's buffers). |
ready.metas? | ReadonlyMap<number, SogMeta> | Files' metas by file index. |
ready.pool? | SogWorkerPool | The decode workers. |
Returns
Properties
index
ts
readonly index: PlaceIndex;The place's index (lod-meta.json).
sink
ts
readonly sink: AnimatedSplat;The splat every piece is drawn in.
stats
ts
readonly stats: SplatPlaceStats;Live numbers.
Accessors
budget
Get Signature
ts
get budget(): number;Gaussians shown at most.
Returns
number
The budget.
Set Signature
ts
set budget(value): void;Change the budget (capped by the capacity the place was made with).
Parameters
| Parameter | Type | Description |
|---|---|---|
value | number | Gaussians. |
Returns
void
firstPicture
Get Signature
ts
get firstPicture(): Promise<void>;Resolves once the coarsest level of every leaf is on screen (the first picture).
Returns
Promise<void>
The promise.
object3D
Get Signature
ts
get object3D(): Object3D;The scene object.
Returns
Object3D
The splat; add it to the scene, move it, turn it.
Methods
dispose()
ts
dispose(): void;Remove from the scene, stop the workers, free the splat's buffers.
Returns
void
Nothing.
WebglSkinnedSplat
A dynamic splat fed by a gaussian source and an owner's order, for the WebGL fallback.
Example
ts
import { createWebglSkinnedSplat } from 'gameable/splat';
import { vec3, vec4 } from 'three/tsl';
const splat = createWebglSkinnedSplat(renderer, { capacity: 144_492, colorSpace: 'srgb' });
splat.setGaussianSource(({ index }) => ({ center: vec3(0), covA: vec4(0.01, 0, 0, 0.01), covB: vec4(0, 0.01, 0, 0), color: vec4(1) }));
scene.add(splat.object3D);Constructors
Constructor
ts
new WebglSkinnedSplat(renderer, options): WebglSkinnedSplat;Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | An initialised renderer on the WebGL fallback. |
options | CreateWebglSkinnedSplatOptions | Capacity, bounds, draw order and colour space. |
Returns
Properties
capacity
ts
readonly capacity: number;splat
ts
readonly splat: AnimatedGaussianSplat;The fork instance.
storageCapacity
ts
readonly storageCapacity: number;Slots the fork really holds: capacity padded to the fallback's texture shape (fork edit 17, padStorageCapacity). The source is asked for the first capacity only; an order from the owner may cover capacity or all of these.
Accessors
object3D
Get Signature
ts
get object3D(): Object3D;The scene object.
Returns
Object3D
The fork instance, an ordinary Object3D.
Methods
dispose()
ts
dispose(): void;Detach from the scene and release the geometry, the material and every attribute (its WebGL buffer and the float texture the fallback reads it through). Idempotent.
Returns
void
Nothing.
setBoundingSphere()
ts
setBoundingSphere(center, radius): void;Declare where the gaussians are, in local space.
Parameters
| Parameter | Type | Description |
|---|---|---|
center | readonly [number, number, number] | Local-space centre. |
radius | number | Local-space radius. |
Returns
void
Nothing.
setFragmentAlpha()
ts
setFragmentAlpha(builder): void;Scale each fragment's opacity by a node built from the view-space centre (fork edit 15).
Parameters
| Parameter | Type | Description |
|---|---|---|
builder | ((view) => Node) | null | Returns a float node, or null to draw as before. |
Returns
void
Nothing.
setGaussianSource()
ts
setGaussianSource(builder): void;The gaussian source: where each gaussian's centre, covariance and colour come from.
Parameters
| Parameter | Type | Description |
|---|---|---|
builder | SplatGaussianSourceBuilder | null | The source, or null to draw nothing (the buffers are zero). |
Returns
void
Nothing.
setSortOrder()
ts
setSortOrder(order, camera): void;The draw order, from the owner's CPU sort (far first).
Parameters
| Parameter | Type | Description |
|---|---|---|
order | Uint32Array | capacity slot indices (or storageCapacity; the padding goes last by itself). |
camera | Camera | The camera the order was made for. |
Returns
void
Nothing.
sortViewMoved()
ts
sortViewMoved(camera): boolean;Whether the view turned past the sort's threshold since the last order.
Parameters
| Parameter | Type | Description |
|---|---|---|
camera | Camera | The camera. |
Returns
boolean
True when a new order is due for the camera alone.
Interfaces
CreateAnimatedSplatOptions
Options accepted by createAnimatedSplat.
Properties
autoSort?
ts
readonly optional autoSort?: boolean;Sort in onBeforeRender. Defaults to true, which is what a producer wants: paired with markGaussiansChanged it gives exactly one sort per frame.
boundingSphere?
ts
readonly optional boundingSphere?: object;Where the gaussians will be, in local space. Defaults to a unit sphere at the origin, which is almost certainly wrong; set it as soon as the producer knows.
center
ts
readonly center: Vec3Tuple;radius
ts
readonly radius: number;capacity
ts
readonly capacity: number;Gaussians to allocate. Fixed for the object's lifetime.
colorSpace?
ts
readonly optional colorSpace?: "linear" | "srgb";What the producer's packed colour bytes mean. 'srgb' (the default) is what nearly every splat holds, a character trained against photographs among them: drawn only by gameable/core's attachSrgbPass (which the engine and gameable/three attach), blending on its sRGB values as it was trained; without a pass it draws nothing and warns once. 'linear' is for a splat trained in linear light: three's convention, drawn in the app's own pass.
kernel?
ts
readonly optional kernel?: "three" | "studio";The gaussian kernel, the fork's edit 18: 'three' is three's (cut at 2 sigma, opacity reduced as the 2D blur grows) and 'studio' the Gameable studio's, which exported characters were made on (cut at 2.83 sigma with the tail subtracted, the trained opacity kept, a 0.075 px² blur): less washed-out thin details (lashes, hair) and no grain from the hard 2-sigma edge. The same switch as CreateAnimatedSplatOptions.pointShape: 'studio' is pointShape: 'studio', 'three' is 'engine'; a pointShape wins when both are given.
pointShape?
ts
readonly optional pointShape?: "studio" | "engine" | "full-opacity" | "trained" | "size-aware";How each point is drawn (the fork's edits 18, 21, 26 and 27). 'engine' (the default) keeps three's 2-sigma point with its small-point fade (the dilation's opacity compensation); 'studio' draws each point as the Gameable studio draws it (the studio kernel above); 'full-opacity' is the studio's first rule alone (a small point keeps its whole opacity) on the engine's point; 'size-aware' gives a point the engine's soft shape while it is small on screen and the studio's once it is large, blended across a band of sizes (setSizeAwareBand): the look Isaac chose for every character on the looks sheet of 30 Sep 2026, which the character bridge asks for; 'trained' draws it as the training renderer did (the whole opacity capped at 0.999, a square to the major axis reaching 3 sigma of it, fragments under 1/255 not drawn): the align lab's measuring switch only, rejected for characters by Isaac on 30 Sep 2026; no package, manifest or bridge may ask for it.
renderOrder?
ts
readonly optional renderOrder?: number;Draw order. Defaults to SPLAT_RENDER_ORDER.
shDegree?
ts
readonly optional shDegree?: number;Spherical-harmonics bands, 0 (the default) to 3: the splat then has shBuffers, one per band, storageCapacity gaussians of three's packed words each (3, 4 and 6 words a gaussian), for the producer to write with the gaussians. A streaming place uses it; a character does not. WebGPU only (the buffers are GPUBuffers).
CreateSplatObjectOptions
Options accepted by createSplatObject.
Properties
autoSort?
ts
readonly optional autoSort?: boolean;Re-sort in onBeforeRender when the camera turns far enough. Defaults to true. Turn it off only if you drive updateSort yourself.
colorSpace?
ts
readonly optional colorSpace?: "linear" | "srgb";What the file's colour bytes mean. 'srgb' (the default) is what nearly every splat holds: trained against photographs read as they are stored, blending sRGB values. It draws through the fork (AnimatedGaussianSplat) in the sRGB pass (gameable/core's attachSrgbPass, which the engine attaches; without one it draws nothing and warns once). 'linear' is for a splat trained in linear light: three's own GaussianSplat, in the app's pass. The choice marks the asset's geometry (userData.colorSpace), so every object made from it agrees; a SOG asset's geometry says 'srgb' when decoded. Inside the engine, where the splats add up is createEngine's renderer.splatBlend (the layer, the pass, or linear light).
environmentLighting?
ts
readonly optional environmentLighting?: SplatEnvironmentLighting;Opt into diffuse lighting; omitted keeps the native, unlit splat path.
receiveShadows?
ts
readonly optional receiveShadows?: boolean;Make it the maintained fork, which can receive shadows (setShadowReceiver); it draws exactly as three's own until one is switched on. Defaults to false here; the splat service's add passes true.
renderOrder?
ts
readonly optional renderOrder?: number;Draw order. Defaults to SPLAT_RENDER_ORDER.
CreateWebglSkinnedSplatOptions
Options accepted by createWebglSkinnedSplat.
Properties
boundingSphere?
ts
readonly optional boundingSphere?: object;Where the gaussians will be, in local space (the owner's promise; culling is off).
center
ts
readonly center: readonly [number, number, number];radius
ts
readonly radius: number;capacity
ts
readonly capacity: number;Gaussians to allocate. Fixed for the object's lifetime.
colorSpace?
ts
readonly optional colorSpace?: "linear" | "srgb";What the source's colour bytes mean; 'srgb' decodes each gaussian before blending (fork edit 14).
pointShape?
ts
readonly optional pointShape?: "studio" | "engine" | "full-opacity" | "trained" | "size-aware";How each point is drawn (fork edits 18, 21, 26 and 27), the same names as createAnimatedSplat takes: the look a character gets on the WebGPU path is the look it gets here, so the head reads the same on both.
renderOrder?
ts
readonly optional renderOrder?: number;Draw order. Defaults to SPLAT_RENDER_ORDER.
EnvironmentProbeOptions
Shared mesh environment and Gaussian probe settings. SH must already be in world space.
Properties
enabled?
ts
optional enabled?: boolean;Set false to temporarily attach no mesh environment. Default true.
intensity?
ts
optional intensity?: number;Shared brightness multiplier. Default 1.
radianceSH
ts
radianceSH: readonly readonly [number, number, number][];Nine RGB radiance coefficients, already rotated to match the scene.
yaw?
ts
optional yaw?: number;Panorama yaw in radians; does not rotate the supplied world-space SH.
GaussianCloud
Gaussians of a place, for panoramaFromGaussians.
Properties
colors
ts
readonly colors: ArrayLike<number>;RGBA per gaussian (alpha = opacity), multiplied by colorScale to reach 0..1.
colorScale?
ts
readonly optional colorScale?: number;Multiplier on colors. Defaults to 1/255.
colorSpace?
ts
readonly optional colorSpace?: "linear" | "srgb";'srgb' decodes the colours before any sum. Defaults to 'srgb' (a place trained on photographs).
matrixWorld?
ts
readonly optional matrixWorld?: ArrayLike<number>;Column-major 4x4 from the cloud's space to the world (three's Matrix4.elements). Defaults to identity.
positions
ts
readonly positions: ArrayLike<number>;Centres, three per gaussian, in the cloud's own space.
GaussianPanoramaOptions
Options for panoramaFromGaussians.
Properties
maxSamples?
ts
readonly optional maxSamples?: number;Upper bound on gaussians visited; the rest are skipped evenly. Defaults to 600 000.
minDistance?
ts
readonly optional minDistance?: number;Gaussians nearer than this to the probe are skipped (the character's own space). Defaults to 0.35 m.
minOpacity?
ts
readonly optional minOpacity?: number;Gaussians fainter than this opacity are skipped. Defaults to 0.3.
width?
ts
readonly optional width?: number;Pixels across; the height is half. Defaults to 64.
GltfLoaderLike
The subset of GLTFLoader this package needs, so the loader itself is not imported.
Methods
register()
ts
register(callback): unknown;three's plugin hook.
Parameters
| Parameter | Type | Description |
|---|---|---|
callback | (parser) => unknown | Builds the plugin from the parser. |
Returns
unknown
The loader, for chaining.
JoinedGaussians
A layer's gaussians in the joined object's own frame, in the layout of the object's buffers.
Extended by
Properties
center
ts
readonly center: Float32Array;Four floats each: the centre, then 1.
color
ts
readonly color: Uint32Array;One word each: red, green, blue, opacity, a byte apiece, red lowest.
count
ts
readonly count: number;How many.
covA
ts
readonly covA: Float32Array;Four floats each: the covariance's c00, c01, c02, c11.
covB
ts
readonly covB: Float32Array;Four floats each: c12, c22, 0, 0.
max
ts
readonly max: [number, number, number];The high corner of the centres.
min
ts
readonly min: [number, number, number];The low corner of the centres.
JoinedLayer
One layer in a joined splat.
Properties
count
ts
readonly count: number;How many gaussians it has.
done
ts
readonly done: Promise<void>;Resolves when all of it is drawn; rejects when it is removed first.
uploaded
ts
readonly uploaded: number;How many are in the object so far.
JoinedSplat
Several layers of points as one splat.
Properties
capacity
ts
readonly capacity: number;Gaussians the layers may hold at once (less than asked for when the card binds less).
incremental
ts
readonly incremental: boolean;True on WebGPU: layers are written into the one object; false on the WebGL fallback.
object
ts
readonly object: Object3D<Object3DEventMap> | null;The scene object now, or null before the WebGL fallback has its first layer.
parts
ts
readonly parts: SplatPartsIn | null;The moving parts, bound to the object, or null (none asked for, or the WebGL fallback).
uploadPerFrame
ts
uploadPerFrame: number;Gaussians written to the card in a frame at most, on WebGPU. Change it at any time.
used
ts
readonly used: number;Gaussians the layers in it hold now.
Methods
add()
ts
add(gaussians): JoinedLayer | null;Add a layer. Its gaussians are already in the object's frame.
Parameters
| Parameter | Type | Description |
|---|---|---|
gaussians | JoinedGaussians | The layer. |
Returns
JoinedLayer | null
The layer in the object, or null when there is not room for it.
dispose()
ts
dispose(): void;Take the object out of the scene and free what it holds.
Returns
void
Nothing.
remove()
ts
remove(layer): void;Take a layer out: it is drawn no more and its room is free.
Parameters
| Parameter | Type | Description |
|---|---|---|
layer | JoinedLayer | A layer from add. |
Returns
void
Nothing.
update()
ts
update(camera): void;One frame's work: write the next piece of a layer that is arriving, sort again when the view has moved, move the parts. Call it once a frame, before the render.
Parameters
| Parameter | Type | Description |
|---|---|---|
camera | Camera | The camera the frame is drawn with. |
Returns
void
Nothing.
JoinedSplatOptions
Options for createJoinedSplat.
Properties
bounds
ts
readonly bounds: object;A sphere, in the object's own frame, that holds every layer there will ever be: the sort's depth range is built from it, so it is told before anything arrives.
center
ts
readonly center: readonly [number, number, number];radius
ts
readonly radius: number;capacity
ts
readonly capacity: number;Gaussians the layers may hold at once, all together.
colorSpace?
ts
readonly optional colorSpace?: "linear" | "srgb";What the colour bytes mean. Defaults to 'srgb'.
onObject?
ts
readonly optional onObject?: (object) => void;Told each time the scene object is made anew (the WebGL fallback does at every change).
Parameters
| Parameter | Type |
|---|---|
object | Object3D |
Returns
void
parts?
ts
readonly optional parts?: readonly number[];Room for moving parts (loose things that come as points): gaussians each. WebGPU only.
renderOrder?
ts
readonly optional renderOrder?: number;Draw order. Defaults to one before SPLAT_RENDER_ORDER: a place draws first.
sortNearScale?
ts
readonly optional sortNearScale?: number;Depth, in the object's units, below which the sort's bins stay nearly even (the fork's setSortDepthCurve): one object spans a whole world, and even bins across hundreds of metres are too coarse for two surfaces a hand apart near the eye. Defaults to 1.
KeyLightEstimate
A key light's direction and balance.
Properties
azimuth
ts
readonly azimuth: number;Degrees around y, from +z toward +x.
confidence
ts
readonly confidence: number;0 when there is no single key to find (flat light), 1 for a clear one.
direction
ts
readonly direction: readonly [number, number, number];Unit vector from the scene toward the light, y up.
elevation
ts
readonly elevation: number;Degrees above the horizon.
keyToFill
ts
readonly keyToFill: number;Light on a surface facing the key over light on a surface facing away from it.
shadowStrength
ts
readonly shadowStrength: number;How dark the key's shadow is, 0 to 1: the key's share of the light on a surface facing it, 1 - 1 / keyToFill, held at 0.9 or below.
LightProbeGrid
Samples a LightProbeGridData anywhere inside it.
Methods
sample()
ts
sample(
x,
y,
z,
out
): Float32Array;The light at a point, blended from the nearest probes.
Parameters
| Parameter | Type | Description |
|---|---|---|
x | number | Place-frame x. |
y | number | Place-frame y. |
z | number | Place-frame z. |
out | Float32Array | 27 numbers, written in place. |
Returns
Float32Array
out.
LightProbeGridData
The baked grid, as its JSON file holds it.
Properties
bounds
ts
readonly bounds: object;The grid's box, in the place's frame.
max
ts
readonly max: readonly number[];min
ts
readonly min: readonly number[];dims
ts
readonly dims: object;Probes across (x) and deep (z) per layer.
nx
ts
readonly nx: number;nz
ts
readonly nz: number;layerYs
ts
readonly layerYs: readonly number[];Height of each layer, in the place's frame.
probes
ts
readonly probes: readonly object[];Layer by layer, row by row (z), then x: { p: position, sh: 27 numbers }.
spacing
ts
readonly spacing: number;Metres between neighbouring probes of a layer.
LoadedSplatLayer
A layer as it came from its file: the gaussians, and how large the file was.
Extends
Properties
bytes
ts
readonly bytes: number;The file's size, bytes.
center
ts
readonly center: Float32Array;Four floats each: the centre, then 1.
Inherited from
color
ts
readonly color: Uint32Array;One word each: red, green, blue, opacity, a byte apiece, red lowest.
Inherited from
count
ts
readonly count: number;How many.
Inherited from
covA
ts
readonly covA: Float32Array;Four floats each: the covariance's c00, c01, c02, c11.
Inherited from
covB
ts
readonly covB: Float32Array;Four floats each: c12, c22, 0, 0.
Inherited from
max
ts
readonly max: [number, number, number];The high corner of the centres.
Inherited from
min
ts
readonly min: [number, number, number];The low corner of the centres.
Inherited from
LoadSplatOptions
Options accepted by loadSplat.
Properties
fetch?
ts
readonly optional fetch?: {
(input, init?): Promise<Response>;
(input, init?): Promise<Response>;
};Replaces globalThis.fetch, for tests and for an asset-manager transport.
Call Signature
ts
(input, init?): Promise<Response>;Parameters
| Parameter | Type |
|---|---|
input | URL | RequestInfo |
init? | RequestInit |
Returns
Promise<Response>
Call Signature
ts
(input, init?): Promise<Response>;Parameters
| Parameter | Type |
|---|---|
input | string | URL | Request |
init? | RequestInit |
Returns
Promise<Response>
format?
ts
readonly optional format?: SplatFormatOption;Force a decoder instead of sniffing. Defaults to 'auto'.
maxShDegree?
ts
readonly optional maxShDegree?: number;Highest spherical-harmonics band a SOG keeps, 0 to 3 (default 3, all the file has).
signal?
ts
readonly optional signal?: AbortSignal;Aborts the fetch. The decode itself is synchronous and is not interruptible.
LodMetaJson
lod-meta.json.
Properties
count?
ts
readonly optional count?: number;Gaussians across every level.
counts?
ts
readonly optional counts?: readonly number[];Gaussians per level.
filenames
ts
readonly filenames: readonly string[];Each file's meta.json, relative to the bundle.
lodLevels
ts
readonly lodLevels: number;Levels of detail.
tree
ts
readonly tree: LodNodeJson;The octree.
version?
ts
readonly optional version?: number;Format version (1).
Panorama
An equirectangular picture in three's orientation: the top row looks up, u = 0.5 looks along +x.
Properties
channels?
ts
readonly optional channels?: 4 | 1 | 3;Numbers per pixel: 1 (luminance), 3 (RGB) or 4 (RGBA). Defaults to 4.
colorSpace?
ts
readonly optional colorSpace?: "linear" | "srgb";'srgb' decodes each number before any sum (a photograph); 'linear' uses it as is. Defaults to 'linear'.
data
ts
readonly data: ArrayLike<number>;Row-major from the top row, channels numbers per pixel.
height
ts
readonly height: number;Pixels down (180°).
scale?
ts
readonly optional scale?: number;Multiplier that brings a stored number to 0..1 (1/255 for bytes). Defaults to 1.
width
ts
readonly width: number;Pixels across (360°).
yaw?
ts
readonly optional yaw?: number;Degrees added to every azimuth read from the picture, for a picture whose middle column does not look along +x (three's orientation). A World Labs place's own panorama looks along -z in its middle column: pass 90, plus the place's own turn. Defaults to 0.
PanoramaKeyLightOptions
Options for estimateKeyLightFromPanorama.
Properties
clipBoost?
ts
readonly optional clipBoost?: number;How much brighter than white a clipped pixel is taken to be (an 8-bit picture stores a lamp or the sun as white): values above 0.8 are raised by up to 1 + clipBoost times, as the studio's place-light reader does. Defaults to 24 for an sRGB picture, 0 for a linear one.
holdAbove?
ts
readonly optional holdAbove?: number;The key found is then lifted to at least this elevation, keeping its azimuth (a lamp at eye height still throws a floor shadow a camera can see). Defaults to minElevation.
keyCone?
ts
readonly optional keyCone?: number;Half-width of the key's cone, in degrees, for the confidence. Defaults to 25.
minElevation?
ts
readonly optional minElevation?: number;The key is searched no lower than this, in degrees above the horizon. Defaults to 0.
ParseSplatOptions
Options accepted by parseSplat, beyond the format.
Properties
decodeImage?
ts
readonly optional decodeImage?: SogImageDecoder;Turns a SOG's image files into exact RGBA texels. Defaults to a pool of workers decoding through WebGL2; tests and hosts with their own image path pass one.
fetch?
ts
readonly optional fetch?: {
(input, init?): Promise<Response>;
(input, init?): Promise<Response>;
};Fetches a SOG folder's images when the buffer is its meta.json. Defaults to globalThis.fetch.
Call Signature
ts
(input, init?): Promise<Response>;Parameters
| Parameter | Type |
|---|---|
input | URL | RequestInfo |
init? | RequestInit |
Returns
Promise<Response>
Call Signature
ts
(input, init?): Promise<Response>;Parameters
| Parameter | Type |
|---|---|
input | string | URL | Request |
init? | RequestInit |
Returns
Promise<Response>
maxShDegree?
ts
readonly optional maxShDegree?: number;Highest spherical-harmonics band a SOG keeps, 0 to 3 (default 3).
signal?
ts
readonly optional signal?: AbortSignal;Aborts those fetches.
PlaceIndex
A streaming place's index.
Properties
bounds
ts
readonly bounds: Float32Array;Per leaf: min x, y, z, max x, y, z (the place's own space).
box
ts
readonly box: object;The leaves' union box.
max
ts
readonly max: readonly number[];min
ts
readonly min: readonly number[];files
ts
readonly files: readonly object[];Each file: its meta.json URL (resolved), its level, and the gaussians the leaves take from it.
leafCount
ts
readonly leafCount: number;Leaves in the octree.
levelCounts
ts
readonly levelCounts: readonly number[];Gaussians per level, summed over the leaves.
levels
ts
readonly levels: number;Levels of detail; 0 is the finest.
sliceCount
ts
readonly sliceCount: Int32Array;Per leaf and level: gaussians (0 when none).
sliceFile
ts
readonly sliceFile: Int32Array;Per leaf and level (leaf * levels + level): file index, or -1 when the leaf has none there.
sliceOffset
ts
readonly sliceOffset: Int32Array;Per leaf and level: first gaussian in the file.
ShadowCasterSpot
Where a character stands, as clusterShadowCasters needs it.
Properties
radius
ts
readonly radius: number;Metres around the origin its shadow covers.
x
ts
readonly x: number;World x of its origin.
z
ts
readonly z: number;World z of its origin.
ShadowCharacter
A character, as the shadows need it.
Properties
captured?
ts
readonly optional captured?: KeyLightEstimate | null;The light its colours were captured under, in the character's own space (see estimateKeyLightFromSurfels).
footBones?
ts
readonly optional footBones?: readonly string[];The foot joints, for the contact shadow. Defaults to l_foot/r_foot, then foot_l/foot_r.
headBone?
ts
readonly optional headBone?: string;The head joint: it and its children are left out of the character's own shadow. Defaults to c_head, then head.
holder
ts
readonly holder: Object3D;Its origin stands on the floor between the feet.
radius?
ts
readonly optional radius?: number;Metres from the holder's origin to cover around the character. Defaults to 1.
rig
ts
readonly rig: Object3D;Its rig: every skinned mesh under it casts.
splat?
ts
readonly optional splat?: AnimatedGaussianSplat | null;Its gaussians, which receive; null for a mesh-only character.
ShadowDeviceHints
What defaultShadowQuality looks at; each field defaults to the browser's own answer.
Properties
coarsePointer?
ts
readonly optional coarsePointer?: boolean;A touch screen is the main pointer.
deviceMemory?
ts
readonly optional deviceMemory?: number;navigator.deviceMemory in GB (Chrome's rounded answer; Safari has none).
shortSide?
ts
readonly optional shortSide?: number;The screen's shorter side in CSS pixels.
ShadowInfo
What the shadows are doing, for a debug readout.
Properties
azimuth
ts
readonly azimuth: number;Degrees around y from +z toward +x, where the light comes from.
characters
ts
readonly characters: number;Characters casting.
confidence
ts
readonly confidence: number;How sure the chosen source was, 0 to 1.
elevation
ts
readonly elevation: number;Degrees above the horizon.
ground
ts
readonly ground: boolean;Whether the see-through ground is drawing (no place).
keyToFill
ts
readonly keyToFill: number;Light facing the key over light facing away, as read.
places
ts
readonly places: number;Places receiving.
quality
ts
readonly quality: ShadowQuality;The level in use.
rendered
ts
readonly rendered: boolean;Whether the last update drew the depth maps again (false: nothing in them had moved).
renders
ts
readonly renders: number;How many times the depth maps have been drawn since the start.
route
ts
readonly route: KeyLightRoute;Which source decided the light.
sources
ts
readonly sources: readonly object[];Every source that answered, in the order they are preferred, with its answer.
strength
ts
readonly strength: number;How dark the place's shadow is, 0 to 1.
tiles
ts
readonly tiles: number;Light cameras (atlas tiles) in use: 1 when every character stands together, up to 4.
updateMs
ts
readonly updateMs: number;Milliseconds of CPU the last update took, depth passes included.
ShadowPlace
A place, as the shadows need it; every field is optional.
Properties
collider?
ts
readonly optional collider?: Object3D<Object3DEventMap> | null;Its collider mesh: casts onto characters only, never onto the place itself.
colliderReach?
ts
readonly optional colliderReach?: number;How far toward the light, in metres from the characters, the collider still casts. Only its faces that face the light cast, so a room's own shell never shades the room. Defaults to 8.
compareRoutes?
ts
readonly optional compareRoutes?: boolean;Read every source this place has, not only the first, and list them in info.sources (for a readout; the place's own gaussians cost a tenth of a second more at load). Defaults to false.
lightProbes?
ts
readonly optional lightProbes?: LightProbeGridData | null;A baked light-probe grid, positions and light in world space.
panorama?
ts
readonly optional panorama?: Panorama | null;Its own 360° picture, directions in world space (pass yaw to align it).
ShadowQualityPreset
What a level draws.
Properties
characterMap
ts
readonly characterMap: boolean;A second map for the characters themselves: their own shadow (an arm on the body) and the place's collider on them. A second depth pass, so only the top level has it.
contact
ts
readonly contact: boolean;A soft dark patch under each foot.
mapSize
ts
readonly mapSize: number;The key light's depth map, texels per side; 0 draws no cast shadow.
taps
ts
readonly taps: readonly number[];The edge filter: flat [dx, dy, weight, ...] in texels.
SlotAllocator
Hands out and reclaims slot ranges inside a fixed capacity.
Properties
available
ts
readonly available: number;Slots not handed out. Equals capacity - used.
Named available rather than free because free is the method next to it, and a property cannot be both.
capacity
ts
readonly capacity: number;Total slots.
freeRuns
ts
readonly freeRuns: readonly SlotRange[];The free runs, low offset first. Exposed for tests and for the debug overlay.
largestFree
ts
readonly largestFree: number;The largest range allocate could satisfy right now.
used
ts
readonly used: number;Slots currently handed out.
Methods
allocate()
ts
allocate(count): SlotRange;Take the first free run large enough.
Parameters
| Parameter | Type | Description |
|---|---|---|
count | number | How many slots. Must be a positive integer. |
Returns
The range.
Throws
When nothing is large enough, or RangeError when count is not a positive integer.
free()
ts
free(range): void;Give a range back, coalescing with either neighbour.
Parameters
| Parameter | Type | Description |
|---|---|---|
range | SlotRange | A range from allocate. |
Returns
void
Nothing.
Throws
When the range was not allocated, or was already freed.
reset()
ts
reset(): void;Drop every allocation and return to one free run.
Returns
void
Nothing.
SlotRange
A contiguous run of gaussian slots.
Properties
count
ts
readonly count: number;How many slots. Always at least 1.
offset
ts
readonly offset: number;Index of the first slot.
SplatAsset
A decoded splat file.
Properties
boundingSphere
ts
readonly boundingSphere: Sphere;Bounds of the centres, in the file's own coordinate system.
count
ts
readonly count: number;Gaussians in the file.
format
ts
readonly format: SplatFormat;Which decoder ran.
geometry
ts
readonly geometry: BufferGeometry;Geometry with position (f32x3), covariance (f32x6), color (u8x4) and optional SH.
shDegree
ts
readonly shDegree: number;Spherical-harmonics degree, 0 to 3. Degree 0 is flat colour.
url
ts
readonly url: string;Where it came from.
SplatGPUBuffers
The four storage buffers behind a GaussianSplat, as real GPUBuffers.
Properties
center
ts
readonly center: GPUBuffer;array<vec4<f32>>: xyz = centre in local space, w unused.
color
ts
readonly color: GPUBuffer;array<u32>: pack4x8unorm(vec4(r, g, b, a)).
covarianceA
ts
readonly covarianceA: GPUBuffer;array<vec4<f32>>: (c00, c01, c02, c11).
covarianceB
ts
readonly covarianceB: GPUBuffer;array<vec4<f32>>: (c12, c22, 0, 0).
SplatLayerLoader
Fetches layers and puts them in a joined object's frame, off the page's own thread.
Methods
dispose()
ts
dispose(): void;Stop the workers; loads still out never answer.
Returns
void
Nothing.
load()
ts
load(url, matrix): Promise<LoadedSplatLayer>;Fetch a layer's file and unpack it into the joined object's frame.
Parameters
| Parameter | Type | Description |
|---|---|---|
url | string | The file (.spz). |
matrix | ArrayLike<number> | null | Sixteen numbers, column by column, from the file's frame to the object's. |
Returns
Promise<LoadedSplatLayer>
The gaussians and the file's size.
SplatModuleOptions
Options accepted by splat.
Properties
format?
ts
readonly optional format?: SplatFormat;Force a decoder for every splat asset instead of sniffing each one. Leave unset unless your assets are served from URLs with no useful extension.
SplatParts
Parts that move inside one splat.
Extends
Properties
asset
ts
readonly asset: SplatAsset;The base asset grown by room for the parts: make the object from this one.
ranges
ts
readonly ranges: readonly SlotRange[];Where each part's gaussians are in the object, in the order the counts were given.
Inherited from
Methods
bind()
ts
bind(object): void;Name the object the parts are written into. Call once, with the object made from SplatParts.asset (or, for createSplatPartsIn, the object that has the room).
Parameters
| Parameter | Type | Description |
|---|---|---|
object | object | The splat object. |
Returns
void
Nothing.
Inherited from
dispose()
ts
dispose(): void;Let go of the rest buffers. The object is the caller's to dispose.
Returns
void
Nothing.
Inherited from
fill()
ts
fill(part, asset): number;Give a part its gaussians, at rest in the part's own frame. More than the part has room for are left out; the part stays hidden until SplatParts.show.
Parameters
| Parameter | Type | Description |
|---|---|---|
part | number | Which part. |
asset | SplatAsset | The part's gaussians (spherical-harmonics bands are not carried). |
Returns
number
How many gaussians were taken.
Inherited from
flush()
ts
flush(renderer): boolean;Write what changed since the last call. Call once a frame, before the render.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | The renderer the object is drawn with. |
Returns
boolean
True when a pass ran.
Inherited from
move()
ts
move(part, matrix): void;Put a part somewhere.
Parameters
| Parameter | Type | Description |
|---|---|---|
part | number | Which part. |
matrix | Matrix4 | From the part's own frame to the object's local frame. Copied. |
Returns
void
Nothing.
Inherited from
show()
ts
show(part, shown): void;Show or hide a part.
Parameters
| Parameter | Type | Description |
|---|---|---|
part | number | Which part. |
shown | boolean | True to draw it. |
Returns
void
Nothing.
Inherited from
SplatPartsIn
Parts that move inside a splat that already has room for them (createSplatPartsIn).
Extended by
Properties
ranges
ts
readonly ranges: readonly SlotRange[];Where each part's gaussians are in the object, in the order the counts were given.
Methods
bind()
ts
bind(object): void;Name the object the parts are written into. Call once, with the object made from SplatParts.asset (or, for createSplatPartsIn, the object that has the room).
Parameters
| Parameter | Type | Description |
|---|---|---|
object | object | The splat object. |
Returns
void
Nothing.
dispose()
ts
dispose(): void;Let go of the rest buffers. The object is the caller's to dispose.
Returns
void
Nothing.
fill()
ts
fill(part, asset): number;Give a part its gaussians, at rest in the part's own frame. More than the part has room for are left out; the part stays hidden until SplatParts.show.
Parameters
| Parameter | Type | Description |
|---|---|---|
part | number | Which part. |
asset | SplatAsset | The part's gaussians (spherical-harmonics bands are not carried). |
Returns
number
How many gaussians were taken.
flush()
ts
flush(renderer): boolean;Write what changed since the last call. Call once a frame, before the render.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | The renderer the object is drawn with. |
Returns
boolean
True when a pass ran.
move()
ts
move(part, matrix): void;Put a part somewhere.
Parameters
| Parameter | Type | Description |
|---|---|---|
part | number | Which part. |
matrix | Matrix4 | From the part's own frame to the object's local frame. Copied. |
Returns
void
Nothing.
show()
ts
show(part, shown): void;Show or hide a part.
Parameters
| Parameter | Type | Description |
|---|---|---|
part | number | Which part. |
shown | boolean | True to draw it. |
Returns
void
Nothing.
SplatPlaceOptions
Options for loadSplatPlace.
Properties
behindPenalty?
ts
readonly optional behindPenalty?: number;A leaf straight behind the camera counts this many times as far. Defaults to 5 (SuperSplat's).
budget?
ts
readonly optional budget?: number;Gaussians shown at most. Defaults to DESKTOP_PLACE_BUDGET, or PHONE_PLACE_BUDGET on a phone or tablet (a touch device whose user agent says so).
coarsestFirst?
ts
readonly optional coarsestFirst?: boolean;The first picture from the coarsest level only, then the full range (SuperSplat's way). Defaults to true.
colorSpace?
ts
readonly optional colorSpace?: "linear" | "srgb";What the colour bytes mean. Defaults to 'srgb' (a SOG's colours are its photographs').
concurrentLoads?
ts
readonly optional concurrentLoads?: number;Files fetched and decoded at once. Defaults to 4.
cooldownMs?
ts
readonly optional cooldownMs?: number;A file no piece needs is dropped after this cooldown (ms) once the files held pass holdBytes. Defaults to 3000.
fetch?
ts
readonly optional fetch?: {
(input, init?): Promise<Response>;
(input, init?): Promise<Response>;
};Replaces globalThis.fetch.
Call Signature
ts
(input, init?): Promise<Response>;Parameters
| Parameter | Type |
|---|---|
input | URL | RequestInfo |
init? | RequestInit |
Returns
Promise<Response>
Call Signature
ts
(input, init?): Promise<Response>;Parameters
| Parameter | Type |
|---|---|
input | string | URL | Request |
init? | RequestInit |
Returns
Promise<Response>
headroom?
ts
readonly optional headroom?: number;Capacity above the budget, as a fraction of it, for pieces in transition. Defaults to 0.15.
holdBytes?
ts
readonly optional holdBytes?: number;Decoded files the workers may keep (bytes of texels and tables), so a piece the camera comes back to need not be fetched again. Past it, files no piece needs go, the longest unneeded first. Defaults to 512 MB on a desktop, 128 MB on a phone.
lodUpdateAngle?
ts
readonly optional lodUpdateAngle?: number;...or turned this far (degrees). Defaults to 90 (SuperSplat's).
lodUpdateDistance?
ts
readonly optional lodUpdateDistance?: number;Re-choose the levels when the camera has moved this far (world units). Defaults to 1.
log?
ts
readonly optional log?: (message) => void;Told what the place does (loads, drops, first picture), for a debug overlay or the console.
Parameters
| Parameter | Type |
|---|---|
message | string |
Returns
void
minContribution?
ts
readonly optional minContribution?: number;With the projected draw, a point adding less than this many pixels' worth of full coverage (opacity x 2 pi x its footprint) is not drawn: PlayCanvas's minContribution, 1 in SuperSplat's viewer. Defaults to 0 (every point): at 1 the street drew 25 % faster standing still, but the room seen close lost its faint points (the rug's soft weave turned speckled; 10 % of the pixels moved by more than 16 grey levels).
projectedDraw?
ts
readonly optional projectedDraw?: boolean;Project each gaussian once a frame in a compute pass and draw from that (fork edit 36), instead of working out each corner of its square in the vertex stage. Defaults to true.
shDegree?
ts
readonly optional shDegree?: number;Highest spherical-harmonics band drawn, 0 to 3. Defaults to 3.
sortNearScale?
ts
readonly optional sortNearScale?: number;Depth (world units) below which the sort's bins stay nearly even (fork edit 33). Defaults to 1.
sortRadius?
ts
readonly optional sortRadius?: number;How far from the camera the sort orders finely (world units); beyond, one bin. Defaults to 200.
uploadPerFrame?
ts
readonly optional uploadPerFrame?: number;Gaussians uploaded per frame at most (one piece always goes, however big). Defaults to 300,000.
uploadPerSecond?
ts
readonly optional uploadPerSecond?: number;Gaussians uploaded per second at most, after the first picture (about 104 bytes each with three bands). The browser copies every upload into the graphics process; past what it can carry, a frame waits for it (measured on a desktop with the frame rate uncapped: 300,000 a frame stalled frames for up to 1.1 s). Defaults to 2,000,000.
workers?
ts
readonly optional workers?: number;Decode workers. Defaults to half the processors, 2 to 6.
SplatPlaceStats
What a place is doing, updated every frame (read it, do not keep it).
Properties
budget
ts
budget: number;The budget.
capacity
ts
capacity: number;Gaussians the splat can hold.
fetchedBytes
ts
fetchedBytes: number;Bytes fetched so far.
filesHeld
ts
filesHeld: number;Files decoded and held in the workers now.
filesLoading
ts
filesLoading: number;Files being fetched and decoded.
firstPictureMs
ts
firstPictureMs: number | null;When (ms on the page's clock) the coarsest level was all on screen.
gpuBytes
ts
gpuBytes: number;Graphics memory of the splat's buffers (bytes): gaussians, bands, colour pass, sort.
heldBytes
ts
heldBytes: number;Their bytes (texels and tables).
leavesEmpty
ts
leavesEmpty: number;Leaves showing nothing yet.
leavesPerLevel
ts
readonly leavesPerLevel: number[];Leaves showing each level.
loads
ts
loads: number;Files ever loaded, and ever dropped.
lodUpdates
ts
lodUpdates: number;Level choices made so far.
perLevel
ts
readonly perLevel: number[];Gaussians drawn per level (0 the finest).
queued
ts
queued: number;Pieces decoded and waiting for their upload.
releasedCpuBytes
ts
releasedCpuBytes: number;Page memory freed from three's copies of those buffers.
shown
ts
shown: number;Gaussians drawn now.
slowestTickMs
ts
slowestTickMs: number;The longest a frame's place work took (ms).
startedMs
ts
startedMs: number;When the place was asked for.
unloads
ts
unloads: number;uploadedLastFrame
ts
uploadedLastFrame: number;Gaussians uploaded in the last frame.
wanted
ts
wanted: number;Gaussians the chosen levels add up to.
SplatService
What engine.get('splat') returns.
Properties
added
ts
readonly added: readonly (AnimatedGaussianSplat | GaussianSplat)[];Every object add has put in the scene, in order (an object removed from the scene stays listed; its parent is null). The character bridge reads it to find the places that receive shadows.
loaded
ts
readonly loaded: ReadonlyMap<string, SplatAsset>;Every splat asset loaded through the registry, by asset id.
Assets are cached by the registry, so this is a view of it, not a second cache.
Methods
add()
ts
add(id, options?): AnimatedGaussianSplat | GaussianSplat;Add a loaded asset to the scene as a GaussianSplat, with the right draw order.
It receives shadows unless receiveShadows: false is passed: the object is then the maintained fork (AnimatedGaussianSplat), which draws exactly as three's own until a shadow is switched on.
Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | SplatAsset | Asset id, as declared in assets.json; or an asset made in code (a place grown by createSplatParts), which is in no manifest. |
options? | CreateSplatObjectOptions | - |
Returns
AnimatedGaussianSplat | GaussianSplat
The object that was added.
addBuilt()
ts
addBuilt(
id,
options?,
place?
): Promise<AnimatedGaussianSplat | GaussianSplat>;SplatService.add, with the drawing built before the first sight: the object goes in hidden, the card builds its programs off the frame (renderer.compileAsync), and it shows once they are built, so a place never freezes the picture when it first appears.
Parameters
| Parameter | Type | Description |
|---|---|---|
id | string | SplatAsset | Asset id, as declared in assets.json, or an asset made in code. |
options? | CreateSplatObjectOptions | As for add. |
place? | (object) => void | Called with the object before the build, to put it where it goes. |
Returns
Promise<AnimatedGaussianSplat | GaussianSplat>
The object, showing.
adopt()
ts
adopt(object): void;List a splat made elsewhere (a world's joined places, createJoinedSplat) among the objects add put in the scene, so it receives shadows as they do. The object is the caller's: in the scene already, and the caller's to take out.
Parameters
| Parameter | Type | Description |
|---|---|---|
object | object | The splat object. |
Returns
void
Nothing.
SplatShadows
The running shadows of one scene.
Properties
info
ts
readonly info: ShadowInfo;What the shadows are doing.
quality
ts
readonly quality: ShadowQuality;The level in use.
Methods
addCharacter()
ts
addCharacter(key, character): void;Add a character: its rig casts, its gaussians receive, its colours suggest the light.
Parameters
| Parameter | Type | Description |
|---|---|---|
key | object | Any object that names it (the caller's own record). |
character | ShadowCharacter | The character. |
Returns
void
Nothing.
addPlace()
ts
addPlace(splat, place?): void;Add a place: its gaussians receive, its data suggests the light.
Parameters
| Parameter | Type | Description |
|---|---|---|
splat | AnimatedGaussianSplat | The place's gaussians (the fork class). |
place? | ShadowPlace | Its collider, panorama or light probes. |
Returns
void
Nothing.
dispose()
ts
dispose(): void;Release every map, material and receiver.
Returns
void
removeCharacter()
ts
removeCharacter(key): void;Remove a character.
Parameters
| Parameter | Type | Description |
|---|---|---|
key | object | The key it was added with. |
Returns
void
Nothing.
removePlace()
ts
removePlace(splat): void;Remove a place.
Parameters
| Parameter | Type | Description |
|---|---|---|
splat | AnimatedGaussianSplat | The place's gaussians. |
Returns
void
Nothing.
setKeyLight()
ts
setKeyLight(light): void;Set the light yourself, or null to go back to reading it from the scene.
Parameters
| Parameter | Type | Description |
|---|---|---|
light | KeyLightEstimate | null | The light, world space. |
Returns
void
Nothing.
setQuality()
ts
setQuality(quality): void;Change the level; 'auto' picks by device. Rebuilds the receivers' shaders once.
Parameters
| Parameter | Type | Description |
|---|---|---|
quality | "auto" | ShadowQuality | The level. |
Returns
void
Nothing.
update()
ts
update(dt): void;Draw the depth maps. Call once a frame after the characters are posed, before the frame renders. When nothing in them moved since the last draw, the maps are kept as they are (info.rendered says which).
Parameters
| Parameter | Type | Description |
|---|---|---|
dt | number | Seconds since the last call. |
Returns
void
Nothing.
SplatShadowsOptions
Options for createSplatShadows.
Properties
characterBias?
ts
readonly optional characterBias?: number;Metres along the light a character's gaussian may sit behind its own rig mesh and still count as lit. Its gaussians sit on and a little inside the smooth rig mesh that casts (loose cloth, folds): at 0.02 a character lit from the front comes out blotched grey. An arm this far or more in front of the body still shades it. Defaults to 0.12.
ground?
ts
readonly optional ground?: boolean;A see-through ground under the characters when no place receives. Defaults to true.
placeBias?
ts
readonly optional placeBias?: number;The same for the place's gaussians and the see-through ground. Defaults to 0.03.
placeSpread?
ts
readonly optional placeSpread?: boolean;Read a place's shadow at each corner of its points' squares instead of once a point (fork edit 38): softer on a place whose points are centimetres across, where one value a point makes a shadow of blobs, but fainter too. In test since 3 Oct 2026; defaults to false, the shadow as shipped.
quality?
ts
readonly optional quality?: "auto" | ShadowQuality;A level, or 'auto' (the default) for defaultShadowQuality.
turnSeconds?
ts
readonly optional turnSeconds?: number;Seconds a later, better light takes to turn in. Defaults to 1.
SplatSink
What a producer writes into.
Deliberately small and three-free: a lift shader needs slots, buffers and a way to say the gaussians moved, and nothing else.
Properties
buffers
ts
readonly buffers: SplatGPUBuffers;The four GPU buffers, in the layout documented in backendBuffers.ts. WebGPU only: on the WebGL fallback reading this throws; write through SplatSink.nodes instead.
capacity
ts
readonly capacity: number;Total gaussians this sink can hold. Fixed for its lifetime.
nodes
ts
readonly nodes: SplatStorageNodes;The four buffers as writable TSL storage nodes, on either backend.
object3D
ts
readonly object3D: Object3D;The scene object. Add it to a scene, move it, parent it — it is an ordinary Object3D.
storageCapacity
ts
readonly storageCapacity: number;Elements actually allocated per buffer: capacity padded up to the WebGL fallback's PBO texture shape. The slots past capacity are never handed out and stay invisible. A compute on the fallback dispatches exactly this many invocations.
Methods
allocate()
ts
allocate(count): SlotRange;Reserve a contiguous run of slots.
Parameters
| Parameter | Type | Description |
|---|---|---|
count | number | How many gaussians. |
Returns
The range, as { offset, count }.
free()
ts
free(range): void;Give a range back. The slots are not cleared; call clearSlots first if the caller is not about to overwrite them.
Parameters
| Parameter | Type | Description |
|---|---|---|
range | SlotRange | A range from allocate. |
Returns
void
Nothing.
markGaussiansChanged()
ts
markGaussiansChanged(): void;Tell the splat its gaussians moved, so the next frame re-sorts.
Call it once per frame after the compute pass. Without it the sort only runs when the camera turns more than about 1.81 degrees, and a moving avatar seen from a still camera renders in a stale depth order.
Returns
void
Nothing.
restPoints()?
ts
optional restPoints(): GPUBuffer;The rest centre and group of every slot (a vec4 each), for a point hook; allocated on the first call. Optional: a sink that cannot keep them leaves it out and producers skip the rest points.
Returns
GPUBuffer
setBoundingSphere()
ts
setBoundingSphere(center, radius): void;Declare where the gaussians are, in the object's local space.
The sort quantises depth into 4096 bins across this sphere, so a sphere that does not contain the splats costs precision, and frustum culling is off precisely because this value is a promise rather than a measurement.
Parameters
| Parameter | Type | Description |
|---|---|---|
center | Vec3Tuple | Local-space centre. |
radius | number | Local-space radius. |
Returns
void
Nothing.
SplatStorageNodes
The four gaussian buffers as writable TSL storage nodes, storageCapacity elements each.
Layout as in backendBuffers.ts. A compute node that assigns to element(instanceIndex) of these writes the splat on WebGPU and on the WebGL fallback alike. On the fallback the compute runs as transform feedback, which has two consequences a producer must respect: invocation i writes element i (no offsets, no scattered writes), and each dispatch must cover the whole storageCapacity, once per frame — a partial dispatch leaves elements from two frames back, because three double-buffers the attribute and swaps after every pass.
Properties
center
ts
readonly center: StorageBufferNode<"vec4">;vec4: xyz = centre in local space, w written 1.
color
ts
readonly color: StorageBufferNode<"uint">;uint: pack4x8unorm(vec4(r, g, b, a)).
covarianceA
ts
readonly covarianceA: StorageBufferNode<"vec4">;vec4: (c00, c01, c02, c11).
covarianceB
ts
readonly covarianceB: StorageBufferNode<"vec4">;vec4: (c12, c22, 0, 0).
SurfelKeyLightOptions
Options for estimateKeyLightFromSurfels.
Properties
elevationRange?
ts
readonly optional elevationRange?: readonly [number, number];The elevation is kept inside this range, in degrees. Defaults to [25, 70].
minGroupLuminance?
ts
readonly optional minGroupLuminance?: number;Groups darker than this mean luminance are ignored (black carries no shading). Defaults to 0.01.
minGroupSize?
ts
readonly optional minGroupSize?: number;Groups with fewer samples are ignored. Defaults to 24.
SurfelSamples
The light a character's colours carry, one sample per gaussian.
Properties
count
ts
readonly count: number;Samples.
groups?
ts
readonly optional groups?: ArrayLike<number>;Which patch of the same material each sample belongs to (a body part and a colour), so a dark coat and a pale face are compared only with themselves. Defaults to one group.
luminance
ts
readonly luminance: ArrayLike<number>;Linear luminance per sample.
normals
ts
readonly normals: ArrayLike<number>;Outward unit normals, three per sample.
weights?
ts
readonly optional weights?: ArrayLike<number>;Weight per sample (opacity). Defaults to 1.
Type Aliases
KeyLightRoute
ts
type KeyLightRoute =
| "game"
| "light probe"
| "panorama"
| "place gaussians"
| "character"
| "default";Which source decided the key light's direction.
ShadowQuality
ts
type ShadowQuality = "off" | "contact" | "simple" | "soft";How much shadow to draw.
SplatFileFormat
ts
type SplatFileFormat = SplatFormat | "ssog";What sniffSplatFormat can recognise: every SplatFormat, plus 'ssog', a streaming place (lod-meta.json and SOG tiles), which is not one geometry and opens with loadSplatPlace.
SplatFormat
ts
type SplatFormat = "spz" | "ply" | "splat" | "ksplat" | "sog";Container formats loadSplat understands.
SplatFormatOption
ts
type SplatFormatOption = SplatFormat | "auto";SplatFormat, or 'auto' to sniff.
Vec3Tuple
ts
type Vec3Tuple = [number, number, number];A centre as a plain triple, so producers need no three import.
Variables
BYTES_PER_GAUSSIAN
ts
const BYTES_PER_GAUSSIAN: number;Bytes one gaussian occupies across the four storage buffers.
DEFAULT_KEY_LIGHT
ts
const DEFAULT_KEY_LIGHT: KeyLightEstimate;The light used when nothing can be read: above and to the front-right of a character facing +z.
DESKTOP_PLACE_BUDGET
ts
const DESKTOP_PLACE_BUDGET: 6000000 = 6_000_000;The budget on a desktop, in gaussians shown.
PACKAGE
ts
const PACKAGE: "@gameable/splat";Package identity marker.
Example
ts
import { PACKAGE } from 'gameable/splat';
console.log(PACKAGE); // 'gameable/splat'PHONE_PLACE_BUDGET
ts
const PHONE_PLACE_BUDGET: 1500000 = 1_500_000;The budget on a phone or tablet.
SHADOW_QUALITIES
ts
const SHADOW_QUALITIES: readonly ShadowQuality[];Every level, lowest first.
SPLAT_RENDER_ORDER
ts
const SPLAT_RENDER_ORDER: 1000 = 1000;Splats draw after opaque geometry.
The material is transparent with depthWrite: false, so three already puts it in the transparent pass; the render order pins splats after ordinary transparent meshes, because a splat cloud has no single depth to sort by and would otherwise be interleaved by its object centre.
SUPPORTED_THREE_VERSION
ts
const SUPPORTED_THREE_VERSION: "0.186.0" = '0.186.0';The three version this file's private-surface knowledge was written against.
Functions
acquireSplatGPUBuffers()
ts
function acquireSplatGPUBuffers(renderer, splat): SplatGPUBuffers;Materialise a splat's four storage attributes as real GPUBuffers and hand them back.
Idempotent in both directions: createStorageAttribute early-returns when the buffer already exists, and the same GPUBuffer objects come back on every call for the lifetime of the splat. The buffers are owned by three — do not destroy them; dispose the splat.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | An initialised WebGPURenderer. |
splat | object | A GaussianSplat or AnimatedGaussianSplat. |
Returns
The four buffers, in the layout documented at the top of this file.
Throws
When the backend is WebGL, the renderer is not initialised, or three's internals moved.
Example
ts
const buffers = acquireSplatGPUBuffers(renderer, splat);
console.log(buffers.center.size / 16); // gaussian capacityacquireStorageGPUBuffer()
ts
function acquireStorageGPUBuffer(renderer, attribute): GPUBuffer;Materialise one StorageBufferAttribute as a real GPUBuffer and hand it back.
For a producer that writes a buffer from its own compute pass which a three material then reads through storage(attribute, …) — a character's mouth writes its mesh this way. The same private surface as acquireSplatGPUBuffers, for any storage attribute. The buffer is owned by three; give it back with releaseStorageAttribute.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | An initialised WebGPURenderer. |
attribute | object | A StorageBufferAttribute. |
Returns
GPUBuffer
Its GPUBuffer, with STORAGE | COPY_DST usage.
Throws
When the backend is WebGL, the renderer is not initialised, or three's internals moved.
Example
ts
const attribute = new StorageBufferAttribute(new Float32Array(1024 * 4), 4);
const buffer = acquireStorageGPUBuffer(renderer, attribute);
device.queue.writeBuffer(buffer, 0, new Float32Array(1024 * 4));anglesFromDirection()
ts
function anglesFromDirection(direction): [number, number];Azimuth and elevation of a direction.
Parameters
| Parameter | Type | Description |
|---|---|---|
direction | ArrayLike<number> | Any non-zero vector, y up. |
Returns
[number, number]
[azimuth, elevation] in degrees.
backendKind()
ts
function backendKind(renderer): "webgpu" | "webgl";Which backend an initialised renderer ended up on.
Unlike isWebGPUBackend, this refuses to answer for a renderer that has not been initialised, because before init there is no backend to ask and "not WebGPU" would be a guess.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | An initialised WebGPURenderer. |
Returns
"webgpu" | "webgl"
'webgpu', or 'webgl' for the WebGL2 fallback.
Throws
When the renderer has not been initialised.
Example
ts
await renderer.init();
if (backendKind(renderer) === 'webgl') console.info('WebGL2 fallback: CPU sort from readback');clampElevation()
ts
function clampElevation(
direction,
min,
max
): [number, number, number];The same direction with its elevation held inside a range.
Parameters
| Parameter | Type | Description |
|---|---|---|
direction | ArrayLike<number> | Unit direction. |
min | number | Lowest elevation, degrees. |
max | number | Highest elevation, degrees. |
Returns
[number, number, number]
The adjusted unit direction.
clusterShadowCasters()
ts
function clusterShadowCasters(
spots,
previous?,
out?
): number[];Group characters for the shadow's light cameras: those standing together share one, those standing apart get one each, at most four groups. Two groups join when their circles overlap, or when joining leaves no texel coarser (one group has the whole map; two to four share it as a 2 x 2 atlas, half the texels per side each). Groups that were together last frame (previous) stay together a little longer, so a walk past a limit does not flicker. With more than four groups the two with the smallest circle around both join. Groups are numbered by their first member, so a lone character is always group 0.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
spots | readonly ShadowCasterSpot[] | undefined | Where each character stands. |
previous | readonly number[] | [] | Each spot's group last frame, -1 for a new one; empty for none. |
out | number[] | [] | An array to fill and return, to spare an allocation per frame. |
Returns
number[]
Each spot's group, 0 to 3.
Example
ts
import { clusterShadowCasters } from 'gameable/splat';
// Two people side by side, a third across the room.
clusterShadowCasters([
{ x: 0, z: 0, radius: 1 },
{ x: 1, z: 0, radius: 1 },
{ x: 8, z: 0, radius: 1 },
]); // [0, 0, 1]createAnimatedSplat()
ts
function createAnimatedSplat(renderer, options): Promise<AnimatedSplat>;Build a dynamic splat on an initialised renderer.
Asynchronous by contract rather than by need: acquiring the buffers is synchronous today, but the call sits on the boundary where a backend could have to be asked for something, and every caller already awaits.
Works on WebGPU and on the WebGL2 fallback. On the fallback there are no GPUBuffers: write through sink.nodes with a TSL compute node dispatched over sink.storageCapacity.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | An initialised WebGPURenderer. |
options | CreateAnimatedSplatOptions | Capacity, bounds and draw order. |
Returns
Promise<AnimatedSplat>
The sink, with its GPU buffers already acquired on WebGPU.
Throws
When the renderer is not initialised.
Example
ts
import { createAnimatedSplat } from 'gameable/splat';
const sink = await createAnimatedSplat(renderer, {
capacity: 250_000,
boundingSphere: { center: [0, 1, 0], radius: 1.4 },
});
scene.add(sink.object3D);
const head = sink.allocate(120_000);
// ... compute pass writes sink.buffers.* over [head.offset, head.offset + head.count)
sink.markGaussiansChanged();
// Either backend: a TSL compute node over the storage nodes, one invocation per slot.
import { Fn, instanceIndex, vec4 } from 'three/tsl';
const lift = Fn(() => {
sink.nodes.center.element(instanceIndex).assign(vec4(0, 1, 0, 1));
})().compute(sink.storageCapacity);
renderer.compute(lift);
sink.markGaussiansChanged();createEnvironmentProbe()
ts
function createEnvironmentProbe(
scene,
renderer,
panorama,
options
): object;Filter a caller-owned panorama for mesh IBL and scale its world-space Gaussian probe. Dispose restores the previous scene environment if this helper still owns it. The input texture remains caller-owned. No assets or network requests are created.
Parameters
| Parameter | Type | Description |
|---|---|---|
scene | Scene | Scene receiving mesh IBL. |
renderer | Renderer | Initialized engine renderer. |
panorama | Texture | Linear HDR equirectangular panorama. |
options | EnvironmentProbeOptions | Probe and matching mesh intensity/orientation. |
Returns
object
Shared SH coefficients and idempotent cleanup.
radianceSH
ts
radianceSH: [number, number, number][];dispose()
ts
dispose(): void;Returns
void
Example
ts
const lighting = createEnvironmentProbe(engine.scene, engine.renderer, panorama, {
radianceSH: probe.radianceSH, intensity: 0.95, yaw: probe.yaw,
});
splat.setEnvironmentLighting({ radianceSH: lighting.radianceSH });
// On shutdown: lighting.dispose(); panorama.dispose();createJoinedSplat()
ts
function createJoinedSplat(
renderer,
parent,
options
): JoinedSplat;Several layers of points as one splat: one object, one sort.
On WebGPU the object is there at once, empty, with room for capacity gaussians and the parts; each layer is written into it a piece a frame. On the WebGL fallback the object is built again from every layer whenever one comes or goes.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | The renderer, initialised. |
parent | Object3D | Where the object goes (the scene). |
options | JoinedSplatOptions | The room, the bounds, the colour space. |
Returns
The joined splat.
Example
ts
import { createJoinedSplat, createSplatLayerLoader } from 'gameable/splat';
const joined = createJoinedSplat(renderer, scene, {
capacity: 6_000_000,
bounds: { center: [60, 20, 90], radius: 180 },
});
const loader = createSplatLayerLoader();
const court = joined.add(await loader.load('/worlds/estate/places/court/place.spz', courtMatrix));
// each frame, before the render: joined.update(camera)
await court?.done;createLightProbeGrid()
ts
function createLightProbeGrid(data): LightProbeGrid;Check a baked grid and build its sampler.
Parameters
| Parameter | Type | Description |
|---|---|---|
data | LightProbeGridData | The parsed JSON. |
Returns
The sampler.
Throws
When the grid's sizes do not match its probes.
createSlotAllocator()
ts
function createSlotAllocator(capacity): SlotAllocator;Build a slot allocator over capacity slots.
Parameters
| Parameter | Type | Description |
|---|---|---|
capacity | number | Total slots. Must be a positive integer. |
Returns
An allocator with one free run covering everything.
Example
ts
import { createSlotAllocator } from 'gameable/splat';
const slots = createSlotAllocator(1000);
const head = slots.allocate(400);
console.log(head.offset, head.count); // 0 400
slots.free(head);
console.log(slots.available); // 1000createSplatLayerLoader()
ts
function createSplatLayerLoader(options?): SplatLayerLoader;A loader for the layers of a joined splat: each file is fetched, unpacked and put through its matrix in a worker, so the page's own thread does none of it. A file the worker cannot read (not an .spz of version 2 or 3, or no workers here) is read on the page's thread instead, by the package's own loader.
Parameters
| Parameter | Type | Description |
|---|---|---|
options | { workers?: number; } | workers: how many (default 2). |
options.workers? | number | - |
Returns
The loader.
Example
ts
import { createSplatLayerLoader } from 'gameable/splat';
const loader = createSplatLayerLoader();
const layer = await loader.load('/worlds/estate/places/court/place.spz', null);
console.log(layer.count, layer.bytes);
loader.dispose();createSplatObject()
ts
function createSplatObject(asset, options?): AnimatedGaussianSplat | GaussianSplat;Turn a decoded asset into a scene object.
Uses three's own GaussianSplat unless lighting or shadows are asked for; then the maintained fork, which on a real geometry takes upstream's own path (the same one-time repack) and only adds the switches.
Parameters
| Parameter | Type | Description |
|---|---|---|
asset | SplatAsset | A decoded asset. |
options | CreateSplatObjectOptions | Sort behaviour and draw order. |
Returns
AnimatedGaussianSplat | GaussianSplat
The object, with its bounds computed and its render order set.
Example
ts
attachSrgbPass(renderer, scene); // from gameable/core: sRGB splats draw in its pass
const splat = createSplatObject(asset, { autoSort: true });
splat.position.y = -1;
scene.add(splat);createSplatParts()
ts
function createSplatParts(base, counts): SplatParts;Grow a splat asset by room for parts that move.
The object made from the result draws the base as before; each part is a group of gaussians appended to it, filled from an asset of its own, moved by a matrix and sorted with the rest. WebGPU only (check isWebGPUBackend(renderer) first).
Parameters
| Parameter | Type | Description |
|---|---|---|
base | SplatAsset | The splat the parts stand in (a place). Its geometry is copied, not changed. |
counts | readonly number[] | How many gaussians each part has room for. |
Returns
The parts, with the grown asset to make the object from.
Example
ts
import { createSplatObject, createSplatParts } from 'gameable/splat';
const parts = createSplatParts(place, [trunk.count]);
const object = createSplatObject(parts.asset, { receiveShadows: true });
scene.add(object);
parts.bind(object);
parts.fill(0, trunk);
parts.show(0, true);
parts.move(0, trunkToPlace); // then, each frame: parts.flush(renderer)createSplatPartsIn()
ts
function createSplatPartsIn(first, counts): SplatPartsIn;Parts that move inside a splat that already has room for them: the same as createSplatParts, for an object whose own slots from first on are kept free for the parts (a dynamic splat made with the capacity, say: the joined places of a world). WebGPU only (check isWebGPUBackend(renderer) first).
Parameters
| Parameter | Type | Description |
|---|---|---|
first | number | The object's first slot kept for the parts; the parts follow one another from it. |
counts | readonly number[] | How many gaussians each part has room for. |
Returns
The parts; bind them to the object.
Example
ts
import { createAnimatedSplat, createSplatPartsIn } from 'gameable/splat';
const sink = await createAnimatedSplat(renderer, { capacity: 1_000_000 + trunk.count });
const parts = createSplatPartsIn(1_000_000, [trunk.count]);
parts.bind(sink.splat);
parts.fill(0, trunk);
parts.show(0, true); // then, each frame: parts.flush(renderer)createSplatShadows()
ts
function createSplatShadows(
renderer,
scene,
options?
): SplatShadows;Start shadows for one scene. Nothing is drawn until a character is added; on the WebGL fallback everything is a no-op.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | The engine's renderer. |
scene | Object3D | The scene the ground (when there is no place) is added to. |
options | SplatShadowsOptions | The level, the ground switch, how fast a new light turns in and the biases. |
Returns
The shadows.
Example
ts
import { createSplatShadows } from 'gameable/splat';
const shadows = createSplatShadows(engine.renderer, engine.scene, { characterBias: 0.08 });
shadows.addCharacter(record, { rig, holder, splat });
// Every frame, after the characters are posed and before the frame renders:
shadows.update(dt);
console.log(shadows.info.tiles, shadows.info.rendered);createWebglSkinnedSplat()
ts
function createWebglSkinnedSplat(renderer, options): WebglSkinnedSplat;Build a dynamic splat for the WebGL fallback, fed by a gaussian source.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | An initialised renderer on the WebGL fallback. |
options | CreateWebglSkinnedSplatOptions | Capacity, bounds, draw order and colour space. |
Returns
The splat.
Throws
When the renderer is on WebGPU (use createAnimatedSplat there).
Example
ts
import { createWebglSkinnedSplat } from 'gameable/splat';
const splat = createWebglSkinnedSplat(renderer, {
capacity: 144_492,
boundingSphere: { center: [0, 0.9, 0], radius: 1.2 },
colorSpace: 'srgb',
});
scene.add(splat.object3D);decodeSpzGaussians()
ts
function decodeSpzGaussians(bytes, matrix): JoinedGaussians;Unpack an .spz file (already gunzipped; versions 2 and 3) straight into a joined object's layout, each gaussian put through a matrix on the way: its centre as a point, its covariance as A S A^T (A the matrix's turn and scale).
The numbers are three's own SPZLoader's (the same tables for size and colour, the same turn), so a place drawn through here is the place the loader would have drawn. View-dependent colour is not carried: a made place has none. The function stands by itself (no imports, no outer names) because it also runs inside a worker, from its own source text.
Parameters
| Parameter | Type | Description |
|---|---|---|
bytes | Uint8Array | The file after gunzip. |
matrix | ArrayLike<number> | null | Sixteen numbers, column by column, from the file's frame to the object's; null leaves the gaussians where the file has them. |
Returns
The gaussians.
Throws
When the bytes are not an .spz of version 2 or 3.
Example
ts
import { decodeSpzGaussians } from 'gameable/splat';
const stream = new Blob([file]).stream().pipeThrough(new DecompressionStream('gzip'));
const bytes = new Uint8Array(await new Response(stream).arrayBuffer());
const layer = decodeSpzGaussians(bytes, null);
console.log(layer.count, layer.min, layer.max);defaultShadowQuality()
ts
function defaultShadowQuality(hints?): ShadowQuality;The level a device starts at: soft on a computer, simple on a phone, the contact shadow alone on a phone with 2 GB of memory or less.
Parameters
| Parameter | Type | Description |
|---|---|---|
hints | ShadowDeviceHints | What is known about the device; the browser is asked for anything missing. |
Returns
The level.
directionFromAngles()
ts
function directionFromAngles(azimuth, elevation): [number, number, number];A unit direction from azimuth and elevation.
Parameters
| Parameter | Type | Description |
|---|---|---|
azimuth | number | Degrees around y from +z toward +x. |
elevation | number | Degrees above the horizon. |
Returns
[number, number, number]
The unit vector, y up.
estimateKeyLightFromPanorama()
ts
function estimateKeyLightFromPanorama(panorama, options?): KeyLightEstimate;Find the key light in a 360° picture: the brightest compact region above the horizon.
The picture is reduced to at most 128 x 64, every direction above minElevation is scored by the light inside a narrow lobe around it (a 12° half-width), and the best is refined to the centroid of the light near it. The balance follows from irradiance: keyToFill compares a surface facing the key with one facing away, and shadowStrength is the key's share of the light on the surface facing it, the share its shadow takes away. An 8-bit picture clips a sun at white, so its key reads weaker than it was and the shadow comes out lighter.
Parameters
| Parameter | Type | Description |
|---|---|---|
panorama | Panorama | The picture, three's equirectangular orientation. |
options | PanoramaKeyLightOptions | Search limits. |
Returns
The key light; confidence near 0 for an evenly lit (overcast) picture.
estimateKeyLightFromSH()
ts
function estimateKeyLightFromSH(sh, options?): KeyLightEstimate;Read the key light from a light probe: nine radiance coefficients of the light arriving at one point, in three's SphericalHarmonics3 order, either nine RGB triples (the engine's radianceSH) or 27 flat numbers (a baked probe grid's sh).
The first band's direction is where the most light comes from; keyToFill compares the irradiance facing it with the irradiance facing away. A probe is smooth by nature: it finds the side the light comes from, not a small lamp's exact position.
Parameters
| Parameter | Type | Description |
|---|---|---|
sh | | ArrayLike<number> | readonly readonly [number, number, number][] | The probe. |
options | { minElevation?: number; } | The key is held no lower than minElevation degrees (default 20). |
options.minElevation? | number | - |
Returns
The key light; confidence near 0 for light that is the same from every side.
estimateKeyLightFromSurfels()
ts
function estimateKeyLightFromSurfels(samples, options?): KeyLightEstimate & object;Find the light a character was captured under from its own colours.
The capture-lighting idea (brightness against surface normal) with one change that matters for a clothed person: within each group of samples of one material (one body part, one colour), brightness relative to the group's mean is fitted against the normal's offset from the group's mean normal, L / mean(L) - 1 = d . (n - mean(n)), so a dark coat on the back and a pale face on the front do not read as light from the front. The fitted d points at the key; its length is the key's strength against the ambient light.
Parameters
| Parameter | Type | Description |
|---|---|---|
samples | SurfelSamples | One sample per gaussian: outward normal, luminance, weight and group. |
options | SurfelKeyLightOptions | Group filters and the elevation range. |
Returns
KeyLightEstimate & object
The key light in the samples' own space; confidence near 0 when the colours carry no shading.
getGPUDevice()
ts
function getGPUDevice(renderer): GPUDevice;The one GPUDevice in the process — the renderer's.
AGENTS.md hard rule 7: nothing calls navigator.gpu.requestAdapter(). The device only exists after await renderer.init(), and the backend object is replaced when the renderer falls back to WebGL, so never cache either across init.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | An initialised WebGPURenderer. |
Returns
GPUDevice
The device the renderer owns.
Throws
When the renderer is not initialised, or fell back to WebGL.
Example
ts
await renderer.init();
const device = getGPUDevice(renderer);
device.createShaderModule({ code });initCountingSortPatch()
ts
function initCountingSortPatch(target?): boolean;Patch CountingSort.prototype.compute so a sort is one renderer.compute([...]) call.
Idempotent. gameable/splat applies it from the splat() module's init, from createSplatObject and from createAnimatedSplat, so a game never has to; it is exported for hosts that build splats some other way.
Parameters
| Parameter | Type | Description |
|---|---|---|
target? | object | Prototype to patch. Tests pass a fake; production never sets it. |
Returns
boolean
True when the prototype carries the patch after the call.
Example
ts
import { initCountingSortPatch } from 'gameable/splat';
initCountingSortPatch(); // before the first frame that sorts a splatisCountingSortPatched()
ts
function isCountingSortPatched(target?): boolean;Whether a prototype already carries the counting-sort patch.
Parameters
| Parameter | Type | Description |
|---|---|---|
target? | object | Prototype to check. Defaults to CountingSort.prototype. |
Returns
boolean
True when initCountingSortPatch has run against it.
Example
ts
import { initCountingSortPatch, isCountingSortPatched } from 'gameable/splat';
initCountingSortPatch();
console.log(isCountingSortPatched()); // trueisWebGPUBackend()
ts
function isWebGPUBackend(renderer): boolean;Whether this renderer got a real WebGPU backend rather than the WebGL fallback.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | A renderer, initialised or not. |
Returns
boolean
True for the WebGPU backend.
Example
ts
await renderer.init();
if (!isWebGPUBackend(renderer)) console.warn('static splats only');loadSplat()
ts
function loadSplat(url, options?): Promise<SplatAsset>;Fetch and decode a splat file.
Parameters
| Parameter | Type | Description |
|---|---|---|
url | string | SPZ, PLY, SPLAT, KSPLAT, a .sog or a SOG folder's meta.json. |
options | LoadSplatOptions | Format override, abort signal, custom fetch. |
Returns
Promise<SplatAsset>
The decoded asset.
Throws
On a non-2xx response, an unrecognised format or a decoder failure.
Example
ts
import { createSplatObject, loadSplat } from 'gameable/splat';
const asset = await loadSplat('/models/arena.spz');
attachSrgbPass(renderer, scene); // from gameable/core: sRGB splats draw in its pass
scene.add(createSplatObject(asset));loadSplatPlace()
ts
function loadSplatPlace(
renderer,
url,
options?
): Promise<SplatPlace>;Open a streaming place: SuperSplat's tiled bundle with levels of detail, by its lod-meta.json URL (the files beside it are fetched as the camera needs them) or as a .ssog / .zip (fetched whole, then decoded piece by piece).
The returned place draws itself: add place.object3D to the scene and every frame it is drawn it chooses each leaf's level for that camera, loads the files the chosen pieces need, uploads them nearest first and drops files no piece needs after a cooldown. A SuperSplat scene's up is -y: turn it (object3D.rotation.z = Math.PI) as SuperSplat's viewer does.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | An initialised WebGPURenderer on the WebGPU backend. A single SOG (a .sog, or a folder's meta.json) opens the same way, as a place of one level: it streams in piece by piece and is drawn after the view test, but it has nothing coarser to show, so it holds all its gaussians whatever the budget. |
url | string | The bundle's lod-meta.json, or a .ssog / .zip of the bundle; or a single SOG. |
options | SplatPlaceOptions | Budget (desktop 6 M, phone 1.5 M by default) and the rest. |
Returns
Promise<SplatPlace>
The place; its firstPicture resolves when the coarsest level is all on screen.
moveNeedsSort()
ts
function moveNeedsSort(
x,
y,
z,
scaleX,
scaleY
): boolean;Whether a joined splat must be sorted again after the view has moved (without turning: a turn sorts by itself).
Two things are worked out when the object is sorted and go stale as the eye moves: which centres are in view (the rest are not drawn), and the depth bins, finest at the eye. A move towards a point that is on the screen brings nothing new into view (the view is a cone, and a cone moved along a line inside itself stays inside itself) and keeps the order, so it only coarsens the bins near the eye, slowly: such a move is let run two metres. Any other move (sideways, back) sorts again after a quarter of a metre. At racing speed this is the difference between a sort every frame and one every sixth.
Parameters
| Parameter | Type | Description |
|---|---|---|
x | number | The move since the last sort in the camera's own frame: to its right. |
y | number | Up. |
z | number | Towards the viewer (a move straight on is negative). |
scaleX | number | The projection's x scale (projectionMatrix.elements[0]). |
scaleY | number | The projection's y scale (projectionMatrix.elements[5]). |
Returns
boolean
True when a sort is due.
Example
ts
import { moveNeedsSort } from 'gameable/splat';
console.log(moveNeedsSort(0, 0, -1, 1, 1.8)); // false: a metre straight on
console.log(moveNeedsSort(0.3, 0, 0, 1, 1.8)); // true: 30 cm to the sidepanoramaFromGaussians()
ts
function panoramaFromGaussians(
cloud,
at,
options?
): Panorama & object;Draw a small panorama of a place's gaussians as seen from one point: each pixel takes the colour of the nearest surface in its direction (the front layer of gaussians), so a lamp behind a wall is not seen and a window beside the character is. Feed the result to estimateKeyLightFromPanorama; it is the place's light probe at that point.
Parameters
| Parameter | Type | Description |
|---|---|---|
cloud | GaussianCloud | The place's gaussians. |
at | ArrayLike<number> | The probe point in world space (a character's chest height is a good one). |
options | GaussianPanoramaOptions | Size and filters. |
Returns
Panorama & object
A linear RGB panorama, width x width / 2.
parseShadowQuality()
ts
function parseShadowQuality(value): "auto" | ShadowQuality | null;Read a quality from untrusted text (a URL switch, a saved setting).
Parameters
| Parameter | Type | Description |
|---|---|---|
value | string | null | undefined | The text. |
Returns
"auto" | ShadowQuality | null
The level, 'auto', or null when the text is neither.
parseSplat()
ts
function parseSplat(
buffer,
url,
format?,
options?
): Promise<SplatAsset>;Decode a buffer that has already been fetched.
Separated from loadSplat so tests can drive every decoder from memory, and so an asset pipeline that already holds the bytes does not fetch twice. A SOG is either its .sog zip or a folder's meta.json, whose images are then fetched from beside url.
Parameters
| Parameter | Type | Default value | Description |
|---|---|---|---|
buffer | ArrayBuffer | undefined | The file. |
url | string | undefined | Where it came from, for sniffing, a SOG folder's images and error messages. |
format | SplatFormatOption | 'auto' | Decoder to use, or 'auto'. |
options | ParseSplatOptions | {} | SOG bands, and the fetch a SOG folder's images come through. |
Returns
Promise<SplatAsset>
The decoded asset.
Throws
When the file is a streaming place ('ssog'), which is not one geometry.
Example
ts
import { parseSplat } from 'gameable/splat';
const asset = await parseSplat(bytes, 'arena.spz');
console.log(asset.count, asset.shDegree);placeGaussians()
ts
function placeGaussians(
position,
covariance,
color,
matrix
): JoinedGaussians;Put decoded gaussians (a splat asset's own arrays) through a matrix, into a joined object's layout: each centre as a point, each covariance as A S A^T.
Parameters
| Parameter | Type | Description |
|---|---|---|
position | ArrayLike<number> | Centres, three floats each. |
covariance | ArrayLike<number> | Covariances, six floats each: c00, c01, c02, c11, c12, c22. |
color | ArrayLike<number> | Colours, four bytes each: red, green, blue, opacity. |
matrix | ArrayLike<number> | null | Sixteen numbers, column by column, to the object's frame; null leaves them. |
Returns
The gaussians.
Example
ts
import { placeGaussians } from 'gameable/splat';
const g = asset.geometry;
const layer = placeGaussians(
g.getAttribute('position').array,
g.getAttribute('covariance').array,
g.getAttribute('color').array,
[2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 10, 0, 0, 1], // twice the size, ten metres along x
);readPlaceIndex()
ts
function readPlaceIndex(json, baseUrl): PlaceIndex;Read lod-meta.json into a PlaceIndex.
Parameters
| Parameter | Type | Description |
|---|---|---|
json | LodMetaJson | The parsed file. |
baseUrl | string | Where it came from: file names resolve against it. |
Returns
The index.
Throws
When the file is not a streaming bundle's index.
registerGltfSplatExtension()
ts
function registerGltfSplatExtension(gltfLoader): void;Teach a GLTFLoader to read KHR_gaussian_splatting.
Register it before the first load; meshes carrying the extension then come back as GaussianSplat nodes inside the glTF scene graph. Their render order is not set for you, because the glTF author owns that graph — walk the result and set SPLAT_RENDER_ORDER if splats are interleaving with your transparent meshes.
Parameters
| Parameter | Type | Description |
|---|---|---|
gltfLoader | GltfLoaderLike | The loader to extend. |
Returns
void
Nothing.
Example
ts
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { registerGltfSplatExtension } from 'gameable/splat';
const loader = new GLTFLoader();
registerGltfSplatExtension(loader);
const gltf = await loader.loadAsync('/models/scene.glb');releaseStorageAttribute()
ts
function releaseStorageAttribute(renderer, attribute): boolean;Destroy the GPUBuffer behind one storage attribute.
The counterpart to acquireSplatGPUBuffers, and the reason this file exists: three creates a storage attribute's buffer through backend.createStorageAttribute and destroys it through backend.destroyAttribute, and neither is reachable from the public API. BufferAttribute.dispose() only dispatches a dispose event, which Geometries.js listens for on vertex and index attributes but never on storage ones — so without this call every disposed dynamic splat leaks its buffers for the lifetime of the page.
Safe to call more than once and on an attribute that was never uploaded: it returns false rather than letting destroyAttribute dereference a missing buffer.
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | The renderer that materialised the attribute. |
attribute | object | A StorageBufferAttribute (or any BufferAttribute). |
Returns
boolean
True when a GPU buffer was destroyed.
Example
ts
for (const attribute of splat.storageAttributes()) releaseStorageAttribute(renderer, attribute);releaseStorageCopies()
ts
function releaseStorageCopies(renderer, attributes): object;Drop the CPU copy of storage attributes the GPU already holds, and say how many bytes that freed.
three keeps every StorageBufferAttribute's array after uploading it. A dynamic splat's arrays are zeros the owner never reads again (it writes the GPU buffers directly), and for a streaming place of millions of gaussians they are hundreds of megabytes of page memory. An attribute the backend has a GPUBuffer for gets an empty array of the same type in place of its copy; three only reads array again when the attribute's version changes (needsUpdate), which a dynamic splat never asks for. Its memory accounting (renderer.info.memory) kept the size at creation. An attribute not yet on the GPU (the sort's buffers before the first sort) is left alone: call again after a frame. On the WebGL fallback nothing is released (its transform feedback, PBO textures and readback read the arrays).
Parameters
| Parameter | Type | Description |
|---|---|---|
renderer | WebGPURenderer | An initialised WebGPURenderer on the WebGPU backend. |
attributes | Iterable<object> | The attributes, e.g. splat.storageAttributes(). |
Returns
object
Bytes released, and how many attributes are still waiting for their GPU buffer.
bytes
ts
bytes: number;waiting
ts
waiting: number;Example
ts
const { bytes, waiting } = releaseStorageCopies(renderer, sink.splat.storageAttributes());
if (waiting > 0) requestAnimationFrame(() => releaseStorageCopies(renderer, sink.splat.storageAttributes()));shadowQualityPreset()
ts
function shadowQualityPreset(quality): ShadowQualityPreset;What a quality level draws.
Parameters
| Parameter | Type | Description |
|---|---|---|
quality | ShadowQuality | The level. |
Returns
Its map size, filter and contact switch.
sniffSplatFormat()
ts
function sniffSplatFormat(url, bytes): SplatFileFormat;Guess the container format from the URL and the first bytes.
The extension wins when it is one we know, because .ksplat has no magic number and .splat has no header at all. Content sniffing is the fallback, and it can only recognise the formats that are self-describing: SPZ (gzip, or NGSP for v4), PLY (ply\n), a SOG (a zip holding meta.json, or that meta.json itself) and a streaming place, 'ssog' (a zip with lod-meta.json at its top, or that lod-meta.json). A zip is told apart by its central directory when bytes is the whole file, else by its first entry's name.
Parameters
| Parameter | Type | Description |
|---|---|---|
url | string | The URL the bytes came from. Query and hash are ignored. |
bytes | Uint8Array | At least the first 8 bytes of the file (the whole file tells zips apart best). |
Returns
The format.
Throws
When neither the extension nor the content identifies it.
Example
ts
import { sniffSplatFormat } from 'gameable/splat';
sniffSplatFormat('/models/arena.spz', new Uint8Array(8)); // 'spz'
sniffSplatFormat('/places/room.sog', new Uint8Array(8)); // 'sog'splat()
ts
function splat(options?): EngineModule;The splat EngineModule.
Registers the splat asset type, so { "id": "arena", "type": "splat", "src": "arena.spz" } in assets.json resolves to a SplatAsset, and publishes a small service for putting one in the scene.
Parameters
| Parameter | Type | Description |
|---|---|---|
options | SplatModuleOptions | Optional format override. |
Returns
The module, to be passed in createEngine({ modules }).
Example
ts
import { createEngine } from 'gameable/core';
import { splat } from 'gameable/splat';
const engine = await createEngine({ canvas, manifest: '/assets.json', modules: [splat()] });
engine.get('splat').add('arena');
engine.start();strengthOf()
ts
function strengthOf(keyToFill): number;How dark a shadow of the key is: the key's share of the light on a surface facing it.
Parameters
| Parameter | Type | Description |
|---|---|---|
keyToFill | number | Key over fill, 1 or more. |
Returns
number
0 to 0.9.
takeSlotRuns()
ts
function takeSlotRuns(slots, count): SlotRange[] | null;Take room for a number of gaussians from an allocator, in as many runs as it needs: a layer let go leaves a run a larger layer does not fit, and a layer is written a piece at a time anyway, so it need not be in one run.
Parameters
| Parameter | Type | Description |
|---|---|---|
slots | { available: number; largestFree: number; allocate: SlotRange; } | The allocator. |
slots.available | number | - |
slots.largestFree | number | - |
slots.allocate | - | |
count | number | How many gaussians. |
Returns
SlotRange[] | null
The runs, in the order the layer fills them; null (nothing taken) when there is not room for all of it.
Example
ts
import { createSlotAllocator, takeSlotRuns } from 'gameable/splat';
const slots = createSlotAllocator(1000);
const runs = takeSlotRuns(slots, 400);
console.log(runs); // [{ offset: 0, count: 400 }]