The API reference of the @playcanvas/splat-transform 3.10.0 library: reading, processing and writing 3D Gaussian splat data in Node.js and the browser.
The same pages as Markdown, for AI agents: llms-full.txt.
Class · extends ReadStream
ReadStream wrapper that adds read-ahead buffering to reduce async overhead. Reads larger chunks from the inner stream and buffers excess data for subsequent small reads. Useful for sources with high per-call overhead.
Example
// Wrap a stream with 4MB read-ahead buffering
const buffered = new BufferedReadStream(rawStream, 4 * 1024 * 1024);
const data = await buffered.readAll();
new BufferedReadStream(inner: ReadStream, chunkSize?: number)
Create a caching wrapper around a stream.
Parameters
inner (ReadStream): The underlying stream to read fromchunkSize (number, optional, default 65536): Minimum bytes to read at once from inner stream (default 64KB)close(): void
Release resources and abort any pending operations.
pull(target: Uint8Array): Promise<number>
Pull data into the provided buffer.
Parameters
target (Uint8Array): Buffer to fill with dataReturns Promise<number>: Number of bytes read, or 0 for EOF
bytesRead: number = 0readonly expectedSize: numberreadAll(): Promise<Uint8Array<ArrayBufferLike>>Class
A named column of typed array data within a DataTable.
Columns store homogeneous numeric data efficiently using JavaScript typed arrays.
Example
const positions = new Column('x', new Float32Array([1.0, 2.0, 3.0]));
console.log(positions.name); // 'x'
console.log(positions.dataType); // 'float32'
Class
A table of columnar data representing Gaussian splat properties.
DataTable is the core data structure for splat data. Each column represents a property (e.g., position, rotation, color) as a typed array, and all columns must have the same number of rows.
Standard columns include:
x, y, zrot_0, rot_1, rot_2, rot_3 (quaternion)scale_0, scale_1, scale_2 (log scale)f_dc_0, f_dc_1, f_dc_2 (spherical harmonics DC)opacity (logit)f_rest_0 through f_rest_44Example
const table = new DataTable([
new Column('x', new Float32Array([0, 1, 2])),
new Column('y', new Float32Array([0, 0, 0])),
new Column('z', new Float32Array([0, 0, 0]))
]);
console.log(table.numRows); // 3
console.log(table.numColumns); // 3
clone(options?: object): DataTable
Creates a copy of this DataTable, optionally selecting specific rows and/or columns.
Parameters
options (object, optional): Optional selection criteria.
options.columns (string[], optional): Column names to include. If omitted, all columns are copied.options.rows (Uint32Array<ArrayBufferLike> | number[], optional): Row indices to include (and their order). If omitted, all rows are copied.Returns DataTable: A new DataTable with copied data.
Example
const full = table.clone();
const subset = table.clone({ rows: [0, 2, 4], columns: ['x', 'y', 'z'] });
permuteRowsInPlace(indices: Uint32Array<ArrayBufferLike> | number[]): void
Permutes the rows of this DataTable in-place according to the given indices.
After calling, row i will contain the data that was previously at row indices[i].
This is a memory-efficient alternative to clone({ rows }) that modifies the table
in-place rather than creating a copy. It reuses ArrayBuffers between columns to
minimize memory allocations.
Parameters
indices (Uint32Array<ArrayBufferLike> | number[]): Array of indices defining the permutation. Must have the same
length as the number of rows, and must be a valid permutation
(each index 0 to n-1 appears exactly once).Class
A file system that writes files to in-memory buffers.
Useful for generating output without writing to disk, such as when creating data for download or further processing.
Example
const fs = new MemoryFileSystem();
await writeSource({ filename: 'output.ply', outputFormat: 'ply', source, pool, options: {} }, fs);
// Get the generated data
const data = fs.results.get('output.ply');
Class · implements ReadFileSystem
ReadFileSystem for reading from named memory buffers. Useful for testing or when data is already in memory.
createSource(filename: string, progress?: ProgressCallback): Promise<ReadSource>
Create a readable source for the given path/identifier.
Parameters
filename (string): Path or identifier for the resourceprogress (ProgressCallback, optional): Optional callback for progress reportingReturns Promise<ReadSource>: Promise resolving to a ReadSource
get(name: string): Uint8Array<ArrayBufferLike>
Get a stored buffer by name.
Parameters
name (string): Name/path of the bufferReturns Uint8Array<ArrayBufferLike>: The stored data or undefined
set(name: string, data: Uint8Array): void
Store a named buffer.
Parameters
name (string): Name/path for the bufferdata (Uint8Array): Data to storeClass
Abstract base class for streaming data from a source. Uses a pull-based model where the consumer provides the buffer.
new ReadStream(expectedSize?: number)
Parameters
expectedSize (number, optional): Optional size hint for buffer pre-allocationbytesRead: number = 0
Total bytes read from this stream so far.
readonly expectedSize: number
Size hint for buffer pre-allocation in readAll(). May be undefined if size is unknown.
close(): void
Release resources and abort any pending operations.
abstract pull(target: Uint8Array): Promise<number>
Pull data into the provided buffer.
Parameters
target (Uint8Array): Buffer to fill with dataReturns Promise<number>: Number of bytes read, or 0 for EOF
readAll(): Promise<Uint8Array<ArrayBufferLike>>
Read entire stream into a single buffer. Uses expectedSize hint if available, grows dynamically if needed.
Returns Promise<Uint8Array<ArrayBufferLike>>: Complete data as Uint8Array
Class · implements Renderer
Default human-readable text renderer. Emits one event per line - no
carriage-return rewriting, no TTY detection, no buffering. Bars render
as [#### ...... ] duration, with # appended incrementally on each
barTick and the remainder padded with . on barEnd. output
events are treated as line-oriented: their text is written to the
pipeable sink with a trailing \n appended (callers should not include
one themselves).
Verbosity is consulted directly from the shared logger on each event, so this renderer alone decides what to display - the core delivers every scope/bar lifecycle event so embedders consuming the event stream see a faithful record. The display rules are:
quiet - suppresses every scope/bar lifecycle line (start, tick,
end - including failed ends). Errors, warnings and output still
show.normal (default) - shows scope/bar headers and bar progress;
shows failed scopeEnd / barEnd footers (the "failed in ..."
cascade from logger.error / unwindAll(true)); hides successful
scopeEnd footers.verbose - shows everything, including successful scopeEnd
footers ("done in ...").Sinks are injected (no process reference here) so the renderer works in
both Node CLI and browser/bundle contexts: the CLI passes
process.stderr.write for status and process.stdout.write for raw
output; library/browser consumers can pass a console.log line buffer.
mem: boolean
When true, scope-end and bar-end lines gain a [peak cpu=X] suffix
(extended to [peak cpu=X gpu=Y] when
TextRendererOptions.getPeakGpuMemory is also supplied) sourced
from TextRendererOptions.getPeakCpuMemory. No effect when the
probe is omitted. Defaults to true when getPeakCpuMemory is
provided so embedders that supply a probe see the overlay
automatically. Mutable so the host can toggle the overlay without
re-installing the renderer.
handle(event: LogEvent): void
Handle a log event.
Parameters
event (LogEvent): The event to render.Class
A source-to-engine coordinate transform comprising translation, rotation and uniform scale. Lives alongside a DataTable to describe how raw column data maps to PlayCanvas engine coordinates.
Example
const t = new Transform().fromEulers(0, 0, 180);
console.log(t.isIdentity()); // false
const inv = t.clone().invert();
console.log(t.mul(inv).isIdentity()); // true
static PLY: Readonly<Transform>
PLY coordinate convention: 180-degree rotation around Z. Used by formats that store Gaussian data in PLY-style coordinates: PLY, splat, KSplat, SPZ, and SOG.
clone(): Transform
Creates a deep copy of this transform.
Returns Transform: A new Transform with the same values.
equals(other: Transform, epsilon?: number): boolean
Tests whether this transform equals another within the given tolerance. Quaternion comparison accounts for double-cover (q and -q represent the same rotation).
Parameters
other (Transform): The transform to compare against.epsilon (number, optional, default 1e-6): Floating-point tolerance. Defaults to 1e-6.Returns boolean: True if the transforms are equal within the tolerance.
fromEulers(x: number, y: number, z: number): Transform
Sets this transform to a rotation-only transform from Euler angles in degrees.
Parameters
x (number): Rotation around X axis in degrees.y (number): Rotation around Y axis in degrees.z (number): Rotation around Z axis in degrees.Returns Transform: This transform (for chaining).
getMatrix(result: Mat4): Mat4
Fills the provided Mat4 with the TRS matrix for this transform.
Parameters
result (Mat4): The Mat4 to fill.Returns Mat4: The filled Mat4.
invert(): Transform
Inverts this transform in-place.
Returns Transform: This transform (for chaining).
isIdentity(epsilon?: number): boolean
Tests whether this transform is effectively identity within the given tolerance.
Parameters
epsilon (number, optional, default 1e-6): Floating-point tolerance. Defaults to 1e-6.Returns boolean: True if identity within the tolerance.
mul(other: Transform): Transform
Sets this transform to this * other.
Parameters
other (Transform): The transform to multiply with.Returns Transform: This transform (for chaining).
mul2(a: Transform, b: Transform): Transform
Sets this transform to the composition of a * b. Handles aliasing (either a or b may be this).
Parameters
Returns Transform: This transform (for chaining).
transformPoint(point: Vec3, result: Vec3): Vec3
Transforms a point by this TRS transform: result = translation + rotation * (scale * point).
Parameters
point (Vec3): The input point.result (Vec3): The Vec3 to write the result into (may alias point).Returns Vec3: The transformed point.
Class · implements ReadFileSystem
ReadFileSystem for reading from URLs using fetch. Supports optional base URL for relative paths.
Automatically detects whether the server supports Range requests. If Range requests are supported, uses streaming with Range headers for efficient seeking. If not supported (e.g., Python's SimpleHTTPRequestHandler), falls back to downloading the entire file into memory first.
new UrlReadFileSystem(baseUrl?: string)
Parameters
baseUrl (string, optional, default ''): Optional base URL to prepend to filenamescreateSource(filename: string, progress?: ProgressCallback): Promise<ReadSource>
Create a readable source for the given path/identifier.
Parameters
filename (string): Path or identifier for the resourceprogress (ProgressCallback, optional): Optional callback for progress reportingReturns Promise<ReadSource>: Promise resolving to a ReadSource
Class
A small cross-platform (Node + browser) worker pool running the CPU-heavy
tasks defined in tasks.ts off the main thread. The worker entry is built and
shipped as dist/worker.mjs; the pool spawns it from a URL resolved
relative to the library bundle. Node and bundlers that rewrite
new Worker(new URL('./worker.mjs', import.meta.url)) (e.g. Vite, webpack)
pick it up automatically; with other bundlers, set WorkerQueue.workerUrl
to the deployed worker asset (mirroring WebPCodec.wasmUrl).
Workers spawn lazily on demand and run one task at a time. When workers are
unavailable (running from source via tsx, maxWorkers = 0, or spawn fails)
every task runs inline on the calling thread instead - same code, same
results, just serial.
static get maxWorkers(): number
static set maxWorkers(value: number)
Maximum number of worker threads. Defaults to one less than the available hardware concurrency, capped at 4. (Peak memory scales with worker count, since each holds its own WebP WASM heap; 4 captures most of the parallelism for SOG writes.)
static get workerUrl(): string
static set workerUrl(value: string)
URL of the worker script, or null when auto-resolved relative to the bundle.
static destroy(): Promise<void>
Waits for in-flight tasks to settle, then terminates all workers. Optional: idle workers don't keep the Node process alive, and workers respawn lazily on the next run() call.
Returns Promise<void>: A promise that resolves once all workers are terminated.
Class
A file system that writes files into a ZIP archive.
Creates a ZIP file containing all written files. Used internally for bundled output formats like .sog files. Archives past the classic 4 GiB / 65535-entry limits are written with zip64 records; smaller archives use the classic layout only.
Example
const outputWriter = await fs.createWriter('bundle.zip');
const zipFs = new ZipFileSystem(outputWriter);
// Write files into the zip
const writer = await zipFs.createWriter('data.json');
await writer.write(jsonData);
await writer.close();
// Finalize the zip
await zipFs.close();
Class · implements ReadFileSystem
Virtual filesystem for reading files from a zip archive. Wraps any ReadSource and provides memory-efficient streaming access.
close(): void
Close the zip filesystem and underlying source.
createSource(filename: string, _progress?: ProgressCallback): Promise<ReadSource>
Create a readable source for the given path/identifier.
Parameters
filename (string): Path or identifier for the resource_progress (ProgressCallback, optional)Returns Promise<ReadSource>: Promise resolving to a ReadSource
getEntry(filename: string): Promise<ZipEntry>
Get entry metadata.
Parameters
filename (string): Entry nameReturns Promise<ZipEntry>: Entry metadata or undefined if not found
list(): Promise<string[]>
List all entries in the zip file.
Returns Promise<string[]>: Array of entry names
Interface
Determinate progress bar handle. Closed explicitly via end(), or
implicitly when an enclosing Group's end() (or a
Logger.unwindAll) pops it as part of cleanup.
Carries a [Symbol.dispose] slot directly (rather than extending the
built-in Disposable lib type) so the published .d.ts stays free of
any reference to the Disposable interface. Symbol.dispose itself is
still a TS 5.2+ / esnext.disposable (or es2024.disposable) lib
symbol, so consumers compiling against these declarations need that
lib enabled (or skipLibCheck: true). Callers on TS 5.2+ / Node 20+
can adopt using bar = logger.bar(...) because using only requires
the [Symbol.dispose] shape structurally.
[dispose](): void
Dispose hook so using syntax closes the bar on scope exit.
end(): void
Close the bar and emit final timing.
tick(n?: number): void
Advance the bar by n ticks.
Parameters
n (number, optional): Number of ticks to advance (default 1).update(current: number): void
Set the bar's absolute progress. Clamped to [0, total]. Suppresses
a barTick event when the value is unchanged.
Parameters
current (number): Absolute progress value.Interface
A CPU-resident buffer holding one layer's data for one chunk of gaussians.
A "chunk" is a row-range — a contiguous subset of gaussians; a ChunkData
holds one layer's data for one such chunk (it carries a .layer, and you
acquire and bind position/geometric/color/other separately). Buffers
are acquired from a ChunkDataPool, filled by
ChunkSource.read, used by consumers (writers, kernels that upload
data to the GPU themselves), and then released back to the pool. The
underlying ArrayBuffer is reused across subsequent acquisitions of the same
byte size.
count is the number of gaussians of valid data this buffer holds; it
matches the source's chunkSize except for the final (short) chunk. The
backing data buffer is allocated at full chunk capacity (chunkSize * stride), so the valid region is always the leading count * stride bytes.
stride is the bytes per gaussian, dictated by the layer (and, for color
and other, by the SH band count or extras schema).
readonly count: number
Number of gaussians of valid data this buffer holds.
readonly data: ArrayBuffer
CPU buffer holding this layer's interleaved per-gaussian records. Its
capacity may exceed count * stride (it is sized for a full chunk);
only the leading count * stride bytes are meaningful.
readonly fields: ChunkFieldMap
Field name -> byte offset / component descriptor within the stride.
readonly layer: ChunkLayer
Which layer this buffer holds.
readonly stride: number
Bytes per gaussian for this buffer's layer.
field(name: string): Float32Array<ArrayBufferLike> | Uint32Array<ArrayBufferLike>
Extract one named field as a tight (de-interleaved) typed-array over the valid rows. The result is a copy — fields are generally a sub-span of the stride, so a zero-copy view isn't possible.
Parameters
name (string)Returns Float32Array<ArrayBufferLike> | Uint32Array<ArrayBufferLike>
release(): void
Return this buffer to its ChunkDataPool for reuse. After this call the buffer must not be referenced again.
Interface
A pool-backed allocator for ChunkData buffers.
The pool owns a single chunkSize (the gaussians-per-chunk granularity);
every buffer it hands out is backed by an ArrayBuffer of full capacity
(chunkSize * stride). A short final chunk's buffer therefore shares a pool
slot with full-chunk buffers of the same layer stride — the pool keys on the
buffer's byte size, so reuse is by capacity, not by the (possibly smaller)
count.
No GraphicsDevice is required: these are CPU buffers. A consumer that needs
GPU-resident data uploads chunkData.data itself.
Pool growth is bounded by maxPooledBytes (default 2 GB). On release, if
pooling the buffer would exceed the cap, it is dropped (left to the garbage
collector) instead. Call ChunkDataPool.trim to free pooled buffers
down to a target.
readonly bytesInUse: number
Total bytes currently held by callers (not in the pool).
readonly bytesPooled: number
Total bytes free-listed and ready to be reused.
readonly chunkSize: number
Gaussians-per-chunk granularity this pool allocates for.
acquire(layer: ChunkLayer, layout: LayerLayout, count: number): ChunkData
Acquire a ChunkData buffer for the given layer/layout holding
count gaussians of valid data (0 < count <= chunkSize). Reuses a
pooled buffer of matching capacity if available; otherwise allocates a
new one.
Parameters
layer (ChunkLayer)layout (LayerLayout)count (number)Returns ChunkData
destroy(): void
Drop all pooled buffers. Buffers in use are unaffected.
trim(targetBytes: number): void
Free pooled buffers until bytesPooled <= targetBytes.
Parameters
targetBytes (number)Interface
Lazy, chunked, layered view onto gaussian splat data.
Sources are opened over a file (or derived from another source via a
combinator) and expose only metadata up front — no gaussian data is loaded
at open time except for formats whose decode is fundamentally whole-blob
(SPZ, MJS). Data is materialized into caller-allocated ChunkData buffers
on demand via ChunkSource.read.
Memory ownership is on the caller: buffers are acquired from a
ChunkDataPool, filled by read, used, and released back to the pool. The
source itself never holds long-lived buffer memory on the caller's behalf.
close(): Promise<void>
Release any open file handles or internal decode state. Idempotent; safe to call multiple times.
Returns Promise<void>
read(request: ReadRequest): Promise<void>
Fill the caller's destination buffers from the source, selecting rows
either by chunk index or by an explicit index list (see
ReadRequest). Layers present in the request are filled; absent
layers are skipped. All passed buffers must share the same count —
the chunk size for a chunk request, or count for a gather.
Every source supports both selections: a chunk is a contiguous range and a gather is an arbitrary one, but the per-row decode is the same.
Parameters
request (ReadRequest)Returns Promise<void>
Interface
Named, timed scope returned from Logger.group. Manages the scope's
lifecycle only - free-form messages, nested groups and bars are emitted via
the global logger (they auto-indent under whatever is on top of the
active-scope stack).
Open scopes with logger.group(name) and close them with sub.end() after
the body. Embedders that catch their own exceptions (rather than letting
them propagate to a logger.error() call) should call
Logger.unwindAll from their catch to close any scopes/bars left
dangling on the stack.
Carries a [Symbol.dispose] slot directly (rather than extending the
built-in Disposable lib type) so the published .d.ts stays free of
any reference to the Disposable interface. Symbol.dispose itself is
still a TS 5.2+ / esnext.disposable (or es2024.disposable) lib
symbol, so consumers compiling against these declarations need that
lib enabled (or skipLibCheck: true). Callers on TS 5.2+ / Node 20+
can adopt using g = logger.group(...) because using only requires
the [Symbol.dispose] shape structurally.
[dispose](): void
Dispose hook so using syntax closes the group on scope exit.
end(): void
Close the group, popping anything still open above it on the stack (defensively handles forgotten inner scopes) and emit the timing event.
Interface
Interface for a file system that can create readable sources. Implementations exist for various backends (URL, Node FS, Zip, Memory).
createSource(filename: string, progress?: ProgressCallback): Promise<ReadSource>
Create a readable source for the given path/identifier.
Parameters
filename (string): Path or identifier for the resourceprogress (ProgressCallback, optional): Optional callback for progress reportingReturns Promise<ReadSource>: Promise resolving to a ReadSource
Interface
Interface representing a readable data source. Provides size information and creates streams for reading.
readonly seekable: boolean
Whether range reads are supported. If false, read() must be called with no arguments or start=0.
readonly size: number
The size of the source in bytes, or undefined if unknown. For compressed sources (e.g., gzipped HTTP), this may be approximate.
close(): void
Release any resources held by this source.
read(start?: number, end?: number): ReadStream
Create a stream for reading data, optionally with a byte range.
Parameters
start (number, optional): Starting byte offset (inclusive), defaults to 0end (number, optional): Ending byte offset (exclusive), defaults to size/EOFReturns ReadStream: A ReadStream for pulling data
throws Error if range requested on non-seekable source
Interface
Renderer interface. Receives the full stream of semantic lifecycle
events (LogEvent) and decides how to display them. The core
does not filter scope/bar events by verbosity, so renderers see a
faithful record of every scope open/close and bar progress update -
embedders consuming the event stream can rely on this for progress
UIs that must close themselves on completion. Visibility decisions
(e.g. hiding successful scopeEnd footers at non-verbose
verbosity) are the renderer's responsibility; logger.getVerbosity
is available to consult.
message events for info, warn and debug are gated by verbosity
at the façade (see LoggerCore.isLevelVisible) before reaching
the renderer; error is always delivered.
handle(event: LogEvent): void
Handle a log event.
Parameters
event (LogEvent): The event to render.Interface
Output streams and optional memory-usage probe for TextRenderer.
getPeakCpuMemory?: () => number
Optional peak CPU-side memory probe in bytes (monotonic). Used by
the [peak cpu=X] overlay gated by the renderer's mem field. In
Node this is typically derived from process.resourceUsage().maxRSS
(which is kernel-tracked and reflects the whole process - including
ArrayBuffers - rather than just the V8 heap).
getPeakGpuMemory?: () => number
Optional peak GPU memory probe in bytes (monotonic, like
getPeakCpuMemory). When supplied alongside getPeakCpuMemory and
reporting a non-zero value, the --memory overlay gains a gpu=Y
entry: [peak cpu=X gpu=Y]. Zero suppresses the entry, so runs
that never touch the GPU keep the CPU-only overlay. In the CLI this
is fed by the engine's VRAM counters (see node-device.ts).
output?: (chunk: string) => void
Receives output events, one logical unit per call, each already
terminated with \n by the renderer. Hand this to the pipeable
channel (typically process.stdout.write.bind(process.stdout)).
Defaults to the same sink as write when omitted.
write: (chunk: string) => void
Receives all status chunks (scopes, bars, messages). May contain
partial-line writes (e.g. progress-bar # ticks). For TTY output,
hand this to a stream that flushes on partials
(process.stderr.write.bind(process.stderr) in Node) so bars
render in place. For non-interactive output (CI logs, file
redirects), wrap in a line buffer that holds chunks until a \n
arrives - the bar's incremental writes then coalesce into a single
complete line per bar.
Interface
Metadata for a voxel octree file.
asset: { generator: string }
Asset metadata
Properties
generator (string): Tool that generated the filegridBounds: { max: number[]; min: number[] }
Grid bounds aligned to 4x4x4 block boundaries
Properties
max (number[])min (number[])leafDataCount: number
Total number of Uint32 entries in the leafData array
leafSize: number
Voxels per leaf dimension (always 4)
nodeCount: number
Total number of Uint32 entries in the nodes array
numInteriorNodes: number
Number of interior nodes
numMixedLeaves: number
Number of mixed leaf nodes
sceneBounds: { max: number[]; min: number[] }
Scene bounds (in PlayCanvas coordinate space for v1.1+)
Properties
max (number[])min (number[])treeDepth: number
Maximum tree depth
version: string
File format version
voxelResolution: number
Size of each voxel in world units
Type alias
A camera animation track evaluated in frame time.
type CameraTrack = undefined
Type alias
Description of a single named field within a layer's per-gaussian record.
byteOffset is the offset from the start of one gaussian's record;
components is the count of type elements that make up the field.
type ChunkField = undefined
Type alias
Map from field name to its layout within a chunk's stride.
type ChunkFieldMap = Readonly<Record<string, ChunkField>>
Type alias
ChunkLayer identifiers — the disjoint storage tiers of a gaussian source.
position - xyz (vec3<f32>)geometric - rotation quaternion + scale + opacitycolor - DC + spherical harmonics rest coefficientsother - user-defined extra columns (e.g. blind data)type ChunkLayer = "position" | "geometric" | "color" | "other"
Type alias
Static description of a ChunkSource's contents — what's in it and
how it's laid out. Populated at open() time; never changes thereafter.
chunkSize is the gaussian count per chunk; all chunks are this size except
the final one in each LOD, which holds lodCounts[lod] % chunkSize gaussians
(or chunkSize if the count divides evenly).
layouts exposes the byte stride and named field map for each available
layer, used by callers when acquiring ChunkData buffers from a ChunkDataPool.
LOD is a structural axis: a chunk belongs to exactly one LOD. Sources never carry a per-gaussian LOD tag.
type ChunkSourceMetadata = undefined
Type alias
Collision mesh shape generated alongside voxel output.
smooth - marching cubes with lossless coplanar merge.faces - direct watertight voxel-boundary faces.type CollisionMeshShape = "smooth" | "faces"
Type alias
String identifiers for typed array element types.
type ColumnType = "int8" | "uint8" | "int16" | "uint16" | "int32" | "uint32" | "float32" | "float64"
Type alias
Simplify splats to a target count using NanoGS progressive pairwise merging.
Instead of discarding low-visibility splats, this iteratively merges nearby similar splats into single approximating Gaussians using Mass-Preserving Moment Matching (MPMM), preserving scene structure and appearance.
Removal is allocated at a uniform rate everywhere; for the adaptive variant
call decimateSourceAdaptive() directly.
type Decimate = undefined
Type alias
Where intermediate generations spill when they exceed the in-memory
budget. remove deletes a spill file once its generation is consumed
(optional; without it temp files are left behind).
type DecimateAdaptiveSpill = undefined
Type alias
Where intermediate generations spill when they exceed the in-memory
budget. remove deletes a spill file once its generation is consumed
(optional; without it temp files are left behind).
type DecimateSpill = undefined
Type alias
A function that creates a PlayCanvas GraphicsDevice on demand.
Used for GPU-accelerated operations such as SOG compression and voxelization. The application is responsible for caching if needed.
type DeviceCreator = () => Promise<GraphicsDevice>
Type alias
Descriptor for a single column in the other layer.
type ExtraColumn = undefined
Type alias
Header-only structural metadata for a splat file — the lightweight counterpart to a full read, for validating/inspecting a file (e.g. before upload) without decoding its gaussian data. Reports every LOD level.
For formats opened via readFile, integrity (truncation/corruption) is
enforced by the readers themselves, which throw on a size mismatch — so a
returned FileInfo implies a sound file. The sog and spz header peeks
validate the header but not the payload.
type FileInfo = undefined
Type alias
Remove spherical harmonic bands above a threshold.
type FilterBands = undefined
Type alias
Keep only splats within a bounding box.
type FilterBox = undefined
Type alias
Filter splats by comparing a column value.
For opacity, scale_0/1/2, and f_dc_0/1/2, the value is specified in user-friendly
(transformed) space: linear opacity (0-1), linear scale, and linear color (0-1).
The value is automatically converted to raw PLY space before comparison.
To compare against raw PLY values directly (without the user-friendly conversion),
use the _raw suffix (e.g. opacity_raw, scale_0_raw, f_dc_0_raw).
If the DataTable has a pending spatial transform and the column is affected by it
(position, rotation, scale, or SH columns), the transform is applied (baked in)
before comparison. This applies to both regular and _raw columns.
type FilterByValue = undefined
Type alias
Filter Gaussians to keep only those in the connected cluster at a seed position.
GPU-voxelizes the scene at a coarse resolution, finds the connected component of occupied blocks containing the seed, and keeps only Gaussians whose AABB overlaps that cluster.
type FilterCluster = undefined
Type alias
Remove Gaussians that don't meaningfully contribute to any solid voxel.
GPU-voxelizes the scene at a given resolution, then evaluates each Gaussian's opacity contribution at occupied voxel centers. Discards Gaussians whose contribution is below a minimum threshold at every solid voxel.
type FilterFloaters = undefined
Type alias
Remove splats containing NaN or Infinity values.
type FilterNaN = undefined
Type alias
Keep only splats within a sphere.
type FilterSphere = undefined
Type alias
Print structural metadata (per-LOD counts, columns, SH bands) to the logger — the cheap, header-level counterpart to Stats (no data specifics).
type Info = undefined
Type alias
Supported input file formats for Gaussian splat data.
ply - PLY format (standard 3DGS training output)sog - PlayCanvas SOG format (WebP-compressed)lod - Streamed SOG (lod-meta.json) formatlcc - XGrids LCC formatlcc2 - XGrids LCC2 (octree) formatspz - Niantic Labs compressed formatsplat - Antimatter15 splat formatksplat - Kevin Kwok's compressed splat formatmjs - JavaScript module generatortype InputFormat = "mjs" | "ksplat" | "splat" | "sog" | "ply" | "spz" | "lcc" | "lcc2" | "lod"
Type alias
The byte stride and per-field map for a single layer of a source.
Sources publish a LayerLayout per available layer in their metadata.
Callers pass a layout (along with a gaussian count) to a ChunkDataPool's
acquire to receive a properly-sized ChunkData.
type LayerLayout = undefined
Type alias
Per-LOD column statistics: identity (lod, numGaussians), the column-name
axis (columns), and the aligned measurement arrays (data).
JSON.stringify of this is the stats JSON output shape (NaN fields — e.g.
an all-NaN column's min — serialize as null).
type LodStats = undefined
Type alias
A LOD's measurements in columnar (struct-of-arrays) form: every field is an
array index-aligned with the owning LodStats's columns.
type LodStatsData = undefined
Type alias
Semantic event delivered to a Renderer. Renderers can filter, format and display these as they wish.
scopeStart / scopeEnd represent the open/close of a Group.
They carry optional index / total fields when the scope is part of a
numbered series, which renderers can use to switch to a [N/T] name style.
barStart / barTick / barEnd represent a determinate progress bar.
The bar's name is repeated on every event so the renderer can keep its
label stable across in-place updates while tracking progress via current
and total.
output is the pipeable channel: each event represents a single logical
unit of output (typically one line - or a multi-line block treated as a
unit) that the renderer is expected to terminate with a newline. Callers
should not include a trailing \n themselves.
type LogEvent = { depth: number; index?: number; kind: "scopeStart"; name: string; total?: number } | { depth: number; durationMs: number; failed?: boolean; index?: number; kind: "scopeEnd"; name: string; total?: number } | { depth: number; kind: "barStart"; name: string; total: number } | { current: number; depth: number; kind: "barTick"; name: string; total: number } | { current: number; depth: number; durationMs: number; failed?: boolean; kind: "barEnd"; name: string; total: number } | { depth: number; kind: "message"; level: MessageKind; text: string } | { kind: "output"; text: string }
Type alias
Public type alias for the logger object. Embedders can type-hint against this to inject a configured logger.
type Logger = typeof logger
Type alias
Severity tag for free-form messages (ordered descending by severity).
type MessageKind = "error" | "warn" | "info" | "debug"
Type alias
Reorder splats by Morton code (Z-order curve) for improved spatial locality.
type MortonOrder = undefined
Type alias
Options for read/write operations.
type Options = undefined
Type alias
Supported output file formats for Gaussian splat data.
ply - Standard PLY formatcompressed-ply - Compressed PLY formatsplat - antimatter15 / PlayCanvas viewer .splat formatspz - Niantic Labs SPZ formatglb - Binary glTF with KHR_gaussian_splatting extensioncsv - CSV text format (for debugging/analysis)sog - PlayCanvas SOG format (separate files)sog-bundle - PlayCanvas SOG format (bundled into single .sog file)lod - Multi-LOD format with chunked datahtml - Self-contained HTML viewer (separate assets)html-bundle - Self-contained HTML viewer (all assets embedded)voxel - Sparse voxel octree format for collision detectionimage - Rasterized RGBA image (lossless WebP) rendered from a camera viewtype OutputFormat = "csv" | "sog" | "sog-bundle" | "lod" | "compressed-ply" | "ply" | "splat" | "spz" | "glb" | "html" | "html-bundle" | "voxel" | "image"
Type alias
Parameter passed to MJS generator scripts (see ReadFileOptions.params).
type Param = undefined
Type alias
A processing action to apply to splat data.
Actions can transform, filter, or analyze the data:
translate - Move splats by a Vec3 offsetrotate - Rotate splats by Euler angles (degrees)scale - Uniformly scale splatsfilterNaN - Remove splats with NaN/Inf values or a zero-norm rotationfilterByValue - Keep splats matching a column conditionfilterBands - Remove spherical harmonic bands above a thresholdfilterBox - Keep splats within a bounding boxfilterSphere - Keep splats within a spherefilterFloaters - Remove splats not contributing to any occupied voxel (GPU)filterCluster - Keep splats in the connected cluster at a seed position (GPU)stats - Print per-LOD, per-column statistics to loggerinfo - Print structural metadata to loggermortonOrder - Reorder splats by Morton code for spatial localitydecimate - Simplify to target count via progressive pairwise mergingtype ProcessAction = Translate | Rotate | Scale | FilterNaN | FilterByValue | FilterBands | FilterBox | FilterSphere | FilterFloaters | FilterCluster | ProcessParam | Stats | Info | MortonOrder | Decimate
Type alias
Options for processing actions that require external resources.
type ProcessOptions = undefined
Type alias
Parameter for .mjs generator modules.
type ProcessParam = undefined
Type alias
Progress callback for tracking read operations.
type ProgressCallback = (bytesLoaded: number, totalBytes: number | undefined) => void
Type alias
Options for reading a Gaussian splat file.
type ReadFileOptions = undefined
Type alias
A read request to a ChunkSource, selecting source rows in one of two ways:
chunkIndex): the contiguous run
[chunkIndex·chunkSize, +chunkSize) of the chosen LOD, filled into output
rows [0, count). All passed buffers must have count equal to
meta.chunkSize for non-final chunks or the trailing count for the last.indices/indexOffset/count): arbitrary source rows
indices[indexOffset .. indexOffset + count) of the chosen LOD, filled into
output rows [0, count). Indices need not be sorted and may repeat.Gather underpins the LOD writer's "positions resident, heavy data fetched per
output chunk" pass — for a fixed-stride file source each row is a byte-range
read, so a unit pulls only its own gaussians (≈ 1× total reads, no whole-scene
residency). The two are the same operation with a different row selection; the
decode is identical, which is why a source serves both from one read.
The arms are disjoint on the indices key, so an implementation discriminates
with 'indices' in request (gather) vs the chunk path otherwise.
type ReadRequest = ReadTarget & { chunkIndex: number } | ReadTarget & { count: number; indexOffset: number; indices: Uint32Array }
Type alias
Fields common to every ReadRequest: which LOD to read and the destination buffers for whichever layers the caller wants filled. Layers omitted from the request are skipped.
type ReadTarget = undefined
Type alias
Rotate splats by Euler angles.
type Rotate = undefined
Type alias
Uniformly scale all splats.
type Scale = undefined
Type alias
Spherical harmonics band count.
type SHBands = 0 | 1 | 2 | 3
Type alias
Statistics for an entire source: one LodStats per LOD level.
type SourceStats = undefined
Type alias
How a scene was trained, and therefore how a renderer must evaluate it.
default - ordinary gaussians, no special evaluationantialiased - trained with antialiasing (mip-splatting style screen-space filter)2dgs - trained as 2D gaussian surfels (no third scale axis)The variants are mutually exclusive, hence one enum rather than independent
flags. A source that carries no tag reads as default.
type SplatModel = "default" | "antialiased" | "2dgs"
Type alias
Print per-LOD, per-column statistics (with the structural info block) to the logger — the data-level counterpart to Info.
type Stats = undefined
Type alias
A camera pose on a track: position, look-at target, vertical fov in
degrees and, optionally, a unit up vector (the renderer's up option
applies when absent, so tracks predating up keep working).
type TrackPose = undefined
Type alias
Translate splats by a 3D vector offset.
type Translate = undefined
Type alias
Union of all typed array types supported for column data.
type TypedArray = Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array
Type alias
Verbosity level controlling which messages reach the renderer.
quiet - errors and warnings only.normal - tasks, bars, info, warn, error (default).verbose - normal + debug messages.type Verbosity = "quiet" | "normal" | "verbose"
Type alias
Options for writing a rendered splat image.
type WriteImageOptions = undefined
Type alias
Options for writeSource.
type WriteSourceOptions = undefined
Type alias
Options for writing a voxel octree file.
type WriteVoxelOptions = undefined
Type alias
Metadata for a zip file entry.
type ZipEntry = undefined
Function
bakeTransform(src: ChunkSource, targetSpace: Transform): ChunkSource
Bake a source's pending coordinate-space transform into a target space,
lazily and per chunk — the streaming analog of convertToSpace.
Computes delta = targetSpace⁻¹ · meta.transform once; each read delegates
to the parent (filling the caller's buffers with raw data) and then applies
delta in place to whichever layers were requested, exactly as
transformColumns does for a DataTable:
position — full TRS via transformPointgeometric — compose the rotation onto the quaternion; add log(scale) to the log-scalescolor — rotate the SH rest coefficients (DC is unaffected)other — untouched (user data, not coordinate-dependent)The returned source reports meta.transform = targetSpace (its data is now
baked). Consumers (writers, GPU feeds) wrap their input with this once and
never reimplement transform handling.
Parameters
src (ChunkSource): The parent source.targetSpace (Transform): The coordinate space to bake into (e.g. Transform.PLY).Returns ChunkSource: A derived source whose reads yield data in targetSpace.
Function
combine(dataTables: DataTable[]): DataTable
Combines multiple DataTables into a single DataTable.
Merges rows from all input tables. Columns are matched by name and type; columns that don't exist in all tables will have undefined values for rows from tables lacking that column.
If tables have differing source transforms, all data is first converted to engine coordinate space (identity transform) before combining.
Parameters
dataTables (DataTable[]): Array of DataTables to combine.Returns DataTable: A new DataTable containing all rows from all input tables.
Example
const combined = combine([tableA, tableB, tableC]);
console.log(combined.numRows); // tableA.numRows + tableB.numRows + tableC.numRows
Function
computeStats(input: DataTable | ChunkSource, pool?: ChunkDataPool): Promise<SourceStats>
Compute per-LOD, per-column statistics for splat data in a single streaming pass — exact min/max/mean/stdDev/NaN/Inf, an approximate median, and a 16-bin histogram per column, in columnar form (see SourceStats).
Accepts either a ChunkSource (read chunk-by-chunk, constant memory) or a
legacy DataTable (bridged transiently; yields a single LOD). Values are the
raw, unbaked values — any pending transform is not applied.
Parameters
input (DataTable | ChunkSource): The source or table to analyze (left unchanged).pool (ChunkDataPool, optional): Optional pool for the temporary read buffers; defaults to a fresh pool.Returns Promise<SourceStats>: The per-LOD statistics.
Example
const stats = await computeStats(dataTable);
const { columns, mean } = stats.lods[0];
console.log(mean[columns.indexOf('opacity')]);
Function
concatSource(allSources: ChunkSource[], pool: ChunkDataPool): ChunkSource
Concatenate several sources end-to-end into one, as a lazy view.
Output gaussians are the inputs' gaussians in order: all of sources[0], then
all of sources[1], and so on. Every source must agree on layout (chunk size,
SH bands, available layers, extra columns) and on the pending coordinate-space
transform — concatenating data in mismatched spaces is silently wrong, so
a transform mismatch throws (the caller must bake to a common space first).
Single-LOD only. Reads stitch contiguous row ranges: an output chunk is filled
by block-copying the overlapping span out of each contributing source chunk
(order is preserved, so each overlap is a contiguous byte range — one
set() per layer, not a per-row gather). Source chunks are read on demand;
peak extra memory is one source chunk-set of temporaries.
Parameters
allSources (ChunkSource[]): The sources to concatenate (at least one; empty sources contribute no rows but are still closed by close()).pool (ChunkDataPool): Pool for the temporary source read buffers; chunkSize must match the sources'.Returns ChunkSource: A derived source serving the concatenated gaussians chunk-by-chunk.
Function
createChunkDataPool(options?: object): ChunkDataPool
Create a CPU ChunkData pool.
Parameters
options (object, optional): Pool options.
options.chunkSize (number, optional): Gaussians per chunk (default 1M). Should match
the chunkSize of any source it services.options.maxPooledBytes (number, optional): Cap on bytes held in the free list (default 2 GB).Returns ChunkDataPool: A new ChunkDataPool.
Function
dataTableToChunkSource(dataTable: DataTable, chunkSize?: number, indices?: Uint32Array, model?: SplatModel): InMemoryChunkSource
Convert a legacy DataTable into a ChunkSource by repacking its
columnar data into the canonical per-layer interleaved layout.
Detects SH band count from the highest f_rest_* index, identifies
non-standard columns as other-layer extras, and copies each gaussian's
fields into the appropriate per-layer buffer.
Used during the 3.0 migration by readers that haven't yet been ported to native chunked decoding — they call this at the end of their existing decode to upgrade to the new return type.
When indices is supplied, only those rows are repacked, in that order — a
direct ordered-subset gather (e.g. the LOD writer's per-unit gather), avoiding
a separate DataTable.clone({ rows }) copy.
Parameters
dataTable (DataTable): The legacy table to convert.chunkSize (number, optional, default DEFAULT_CHUNK_SIZE): Gaussians per chunk (default DEFAULT_CHUNK_SIZE).indices (Uint32Array, optional): Optional ordered row indices to gather; output row i is dataTable row indices[i].model (SplatModel, optional): How the scene was trained (a DataTable carries no tag of its own). Defaults to default.Returns InMemoryChunkSource: A CPU-resident InMemoryChunkSource over the repacked data.
Function
decimateSource(source: ChunkSource, pool: ChunkDataPool, opts: DecimateOptions): Promise<ChunkSource>
Chunk-native, memory-bounded decimation to an exact target count.
Design: positions resident; KD blocks as an IO pattern only; per-block exact global 16-NN + edge costs (GPU when a device is supplied) reduced to K resident candidates; global bucketed greedy matching with chain closure; a second heavy pass moment-matches groups and streams the output.
The returned source supports a single sequential pass (it computes the
merge stream on demand) — the PLY-terminal consumption model. Its close
releases the input source and any intermediate spill files. Deep targets
run multiple generations; intermediates land in RAM when small enough,
else in temp PLY spills under opts.spill.scratchDir.
Parameters
source (ChunkSource): Input (consumed: the returned source owns it). Single LOD, gaussian layers required.pool (ChunkDataPool): Chunk-data pool; its chunk size must match the source's.opts (DecimateOptions): Options.Returns Promise<ChunkSource>: The decimated stream-once source with exact metadata.
Function
decimateSourceAdaptive(source: ChunkSource, pool: ChunkDataPool, opts: DecimateOptions): Promise<ChunkSource>
Chunk-native, memory-bounded decimation to an exact target count.
Design: positions resident; KD blocks as an IO pattern only; per-block exact global 16-NN + edge costs (GPU when a device is supplied) reduced to K resident candidates; global bucketed greedy matching with chain closure; a second heavy pass moment-matches groups and streams the output.
The returned source supports a single sequential pass (it computes the
merge stream on demand) — the PLY-terminal consumption model. Its close
releases the input source and any intermediate spill files. Deep targets
run multiple generations; intermediates land in RAM when small enough,
else in temp PLY spills under opts.spill.scratchDir.
Parameters
source (ChunkSource): Input (consumed: the returned source owns it). Single LOD, gaussian layers required.pool (ChunkDataPool): Chunk-data pool; its chunk size must match the source's.opts (DecimateOptions): Options.Returns Promise<ChunkSource>: The decimated stream-once source with exact metadata.
Function
fmtBytes(n: number): string
Format a byte count using binary (1024-based) units.
Parameters
n (number): The number of bytes.Returns string: The formatted string (e.g. 1.5MB).
Function
fmtCount(n: number): string
Format a count using SI suffixes (K/M/B/T) above 1000.
Parameters
n (number): The count to format.Returns string: The formatted string.
Function
fmtTime(ms: number): string
Format a duration in milliseconds as a human-readable string.
1.234s).MmS.SSSs.HhMmS.SSSs.Parameters
ms (number): The duration in milliseconds.Returns string: The formatted string.
Function
getOutputFormat(filename: string, options: Options): OutputFormat
Determines the output format based on file extension and options.
Parameters
filename (string): The filename to analyze.options (Options): Options that may affect format selection.Returns OutputFormat: The detected output format.
throws Error if the file extension is not recognized.
Example
const format = getOutputFormat('scene.ply', {}); // returns 'ply'
const format2 = getOutputFormat('scene.sog', {}); // returns 'sog-bundle'
Function
isSplatModel(value: unknown): value is SplatModel
Narrow an externally-supplied string to a SplatModel. Per-format readers use this on whatever their container spells the tag as.
Parameters
value (unknown): The candidate value.Returns value is SplatModel: True if value is a model name.
Function
loadCameraTrack(json: unknown, defaultFov: number, defaultUp?: Vec3Like): CameraTrack
Build a camera track from parsed JSON, detecting the source by shape.
Parameters
json (unknown): Parsed contents of an editor document.json, a viewer
settings.json, or a plain { frameRate?, frames[] } list.defaultFov (number): Vertical fov in degrees for poses that carry none.defaultUp (Vec3Like, optional, default DEFAULT_UP): Up vector for poses that carry none (only frame-list
entries can carry their own). Default: world +Y.Returns CameraTrack: The track.
Function
materializeToDataTable(src: ChunkSource, pool: ChunkDataPool, layers?: Set<ChunkLayer>): Promise<DataTable>
Materialize a ChunkSource into the legacy columnar DataTable
representation.
Each requested layer is read chunk-by-chunk and scattered into the
appropriate named columns (x, y, z, rot_*, scale_*, opacity, f_dc_*, f_rest_*, plus extras).
Parameters
src (ChunkSource): The source to materialize.pool (ChunkDataPool): The ChunkData pool used for the temporary read buffers; its chunkSize must be >= the source's.layers (Set<ChunkLayer>, optional): Optional layer filter; when set, only these layers (intersected with the source's) are read and allocated. Omit for every available layer. Consumers of a subset (e.g. voxelization needs only position + geometric) skip the unused columns entirely rather than loading and discarding them.Returns Promise<DataTable>: A DataTable holding the source's gaussians in canonical column form.
Function
processDataTable(dataTable: DataTable, processActions: ProcessAction[], options?: ProcessOptions): Promise<DataTable>
Applies a sequence of processing actions to splat data.
Actions are applied in order and can include transformations (translate, rotate, scale), filters (NaN, value, box, sphere, bands), and analysis (stats).
Parameters
dataTable (DataTable): The input splat data.processActions (ProcessAction[]): Array of actions to apply in sequence.options (ProcessOptions, optional): Optional resources for GPU-dependent actions (e.g. filterCluster).Returns Promise<DataTable>: The processed DataTable (may be a new instance if filtered).
Example
import { Vec3 } from 'playcanvas';
const processed = await processDataTable(dataTable, [
{ kind: 'scale', value: 0.5 },
{ kind: 'translate', value: new Vec3(0, 1, 0) },
{ kind: 'filterNaN' },
// opacity value is in linear space (0-1), automatically converted to logit for comparison
{ kind: 'filterByValue', columnName: 'opacity', comparator: 'gt', value: 0.1 }
]);
Function
processSource(source: ChunkSource, actions: ProcessAction[], pool: ChunkDataPool, options?: ProcessOptions): Promise<ChunkSource>
Apply a sequence of processing actions to a ChunkSource, the streaming
analog of processDataTable. Transforms compose lazily onto the pending
meta.transform (via mapSource); filters scan the source and return a
filtered view (via filterSource); stats streams a one-pass
per-LOD accumulation (via computeSourceStats) — a diagnostic pass
that leaves the data unchanged.
Supports only the SOURCE_ACTION_KINDS; throws on any unsupported
action rather than silently dropping it (processSourceBridged owns
the processDataTable fallback for everything else).
Parameters
source (ChunkSource): The input source.actions (ProcessAction[]): Actions to apply in order.pool (ChunkDataPool): Pool for the filter passes' temporary read buffers.options (ProcessOptions, optional): Process options; sourceFormat is reported by info/stats.Returns Promise<ChunkSource>: The processed source (a view chain over source).
Function
processSourceBridged(source: ChunkSource, actions: ProcessAction[], pool: ChunkDataPool, options?: ProcessOptions): Promise<ChunkSource>
Apply an ordered action list to a ChunkSource, streaming the
chunk-native runs and bridging only the DataTable-only runs. Consecutive
actions are grouped into maximal same-mode runs (order preserved): a
chunk-native run (SOURCE_ACTION_KINDS) goes through processSource;
a DataTable-only run (decimate, the GPU voxel filters, …)
materializes once, runs processDataTable, and re-bridges to a source via
dataTableToChunkSource. So the not-yet-chunked ops do their work inline as
islands and everything around them keeps streaming.
Parameters
source (ChunkSource): The input source (consumed; the returned source owns it).actions (ProcessAction[]): Actions to apply in order.pool (ChunkDataPool): Pool for the chunk-native passes and the bridge's chunk size.options (ProcessOptions, optional): Process options (e.g. createDevice for the GPU islands).Returns Promise<ChunkSource>: The processed source.
Function
readFile(readFileOptions: ReadFileOptions): Promise<ChunkSource[]>
Reads a Gaussian splat file and returns its data as ChunkSources (usually one; a Streamed SOG/LCC/LCC2 container yields a single structural multi-LOD source).
Readers are chunk-native: ply/splat/spz/lcc/lcc2/lod return lazy /
streaming sources whose close() releases the underlying file(s); whole-blob
formats (sog/mjs/ksplat) are decoded up front and returned resident.
Callers that need a DataTable materialize at their own boundary (and call
source.close() when done).
Per-format progress (decoding bars, multi-payload bars) is emitted directly
by each reader through the global logger; install a renderer via
logger.setRenderer(...) to consume those events.
Parameters
readFileOptions (ReadFileOptions): Options specifying the file to read and how to read it.Returns Promise<ChunkSource[]>: Promise resolving to an array of chunk sources containing the splat data.
Example
import { readFile, getInputFormat, UrlReadFileSystem } from '@playcanvas/splat-transform';
const filename = 'scene.ply';
const fileSystem = new UrlReadFileSystem('https://example.com/');
const sources = await readFile({
filename,
inputFormat: getInputFormat(filename),
fileSystem
});
Function
readFileInfo(readFileOptions: ReadFileOptions): Promise<FileInfo>
Read a splat file's structural metadata as efficiently as the format allows — from the header alone wherever possible, without decoding gaussian data, and across every LOD level.
sog is peeked from meta.json (no WebP decode) and spz from its 16-byte
header (no payload decode: gzip-wrapped v1-3 files inflate only enough of the
stream to reach the header); every other format opens via readFile
(header-only for the lazy readers; eager for ksplat/mjs) and reads its
meta. For those, integrity is enforced by the readers, which throw on a size
mismatch, so a returned FileInfo implies a structurally sound file; the
sog/spz peeks validate the header but not the payload. A FileInfo doesn't
imply splat data either: a permissive container (e.g. a point-cloud PLY) reads
fine with gaussian: false. To test "is this a valid splat", check both: a
throw means unreadable, gaussian is the data verdict.
Parameters
readFileOptions (ReadFileOptions): Same inputs as readFile.Function
readPly(source: ReadSource, pool: ChunkDataPool): Promise<ChunkSource>
Open a gaussian-splat PLY as a ChunkSource. The single public PLY reader.
Standard uncompressed binary PLY is read lazily: only the header is parsed
at open time, and each read seeks the requested chunk's byte range, pulls
just those records, and de-interleaves the requested layers into the caller's
buffers. Standard gaussian properties map to the position/geometric/color
layers; non-standard properties (e.g. normals) become other-layer extras.
Compressed PLY (the packed chunk+vertex format) is read lazily too: each chunk
is range-read and dequantized on demand (see readCompressedChunked). Either
way the data is labelled Transform.PLY.
The source must be seekable (range reads).
Parameters
source (ReadSource): A seekable read source over the PLY file.pool (ChunkDataPool): The chunk-data pool whose chunkSize defines the chunking granularity.Returns Promise<ChunkSource>: A lazy ChunkSource over the file.
Function
resolveSplatModel(models: SplatModel[]): SplatModel
Resolve the model of a combined scene. Mixing models can't be represented in
one output, so any disagreement falls back to default — the safe read, since
every variant renders acceptably (if not optimally) as ordinary gaussians.
Parameters
models (SplatModel[]): The models of the inputs being combined.Returns SplatModel: The agreed model, or default when they disagree.
Function
selectLod(src: ChunkSource, level: number): ChunkSource
View a single LOD level of a multi-LOD source as a single-LOD source — the
inverse of stackLods. Reads (chunk or gather) forward to the parent
with lod: level; metadata is narrowed to that level's counts.
Several selectLod views typically share one parent (one per level, then
re-stacked), so close() is a no-op: the caller owns the parent's
lifetime and closes it once. (A view closing the shared parent would break the
other levels — e.g. a per-level pass that materializes one level.)
Parameters
src (ChunkSource): The parent (multi-LOD) source.level (number): The LOD level to expose (0-based).Returns ChunkSource: A single-LOD source over src's level level.
Function
sortMortonColumns(x: ArrayLike<number>, y: ArrayLike<number>, z: ArrayLike<number>, indices: Uint32Array): void
Sort indices in place into morton (Z-order) using columnar positions: point
g's coordinates are x[g], y[g], z[g]. The columnar sibling of
sortMortonInterleaved for resident position columns (e.g. the LOD
writer's slim centroids) and DataTable x/y/z columns.
Parameters
x (ArrayLike<number>): X coordinates, indexed by gaussian.y (ArrayLike<number>): Y coordinates, indexed by gaussian.z (ArrayLike<number>): Z coordinates, indexed by gaussian.indices (Uint32Array): Indices to sort in place.Function
sortMortonInterleaved(positions: ArrayLike<number>, indices: Uint32Array, stride?: number): void
Sort indices in place into morton (Z-order) using interleaved positions
[x, y, z, x, y, z, ...] — the natural packing of the position layer.
stride lets the same call sort a wider record whose first three words are
xyz, e.g. a packed [x, y, z, w] GPU texture row viewed as floats, without
copying the positions out first.
Parameters
positions (ArrayLike<number>): Interleaved xyz; gaussian g is at positions[g*stride + {0,1,2}].indices (Uint32Array): Indices to sort in place.stride (number, optional, default 3): Words per gaussian in positions (default 3).Function
stackLods(sources: ChunkSource[]): ChunkSource
Stack N single-LOD sources into one structural multi-LOD source: output LOD
i is sources[i]. read dispatches by request.lod to the matching source
(read at its own LOD 0). numGaussians is LOD 0's count; lodCounts[i] is
sources[i]'s gaussian count.
This is how per-detail-level inputs become one structural scene for the LOD
writer — multi-PLY --tag-lod tags (one source per tagged level) or a DataTable
split by its lod column — replacing the old per-gaussian lod tag array. LOD
is a structural axis here; no source carries a per-gaussian LOD tag.
All inputs must share layout (chunk size, SH bands, layers, extras, transform)
and be single-LOD; the stacked metadata is inherited from sources[0].
Parameters
sources (ChunkSource[]): The per-LOD single-LOD sources, in output-LOD order.Returns ChunkSource: A multi-LOD source dispatching by LOD to the inputs.
Function
writeImage(options: WriteImageOptions, fs: FileSystem): Promise<void>
Renders the splat scene to a lossless WebP image written via fs, or to
a sequence of them along a camera track.
Parameters
options (WriteImageOptions): Render parameters and target filename.fs (FileSystem): File system abstraction for writing the output.Returns Promise<void>
Example
await writeImage({
filename: 'view.webp',
source,
pool,
cameraPosition: { x: 0, y: 0, z: 5 },
fov: 60,
width: 1920, height: 1080,
createDevice: async () => myDevice
}, fs);
Function
writeSource(writeSourceOptions: WriteSourceOptions, fs: FileSystem): Promise<void>
Write a ChunkSource to a file. Formats with a source writer
(ply, sog, compressed-ply, splat, image) consume the source
directly; the rest (csv, spz, glb, html, voxel) still take a
DataTable, so the source is materialized right here and the table stays
a private detail of those writers until each is ported.
lod output is written via writeLodSource (multi-LOD + env), not here.
Each writer is responsible for opening its own Writing log group and
emitting filename (size) info entries per output file.
Parameters
writeSourceOptions (WriteSourceOptions): The source, format and options to write.fs (FileSystem): File system abstraction for writing files.Returns Promise<void>
Function
writeVoxel(options: WriteVoxelOptions, fs: FileSystem): Promise<void>
Voxelizes Gaussian splat data and writes the result as a sparse voxel octree.
This function performs GPU-accelerated voxelization of Gaussian splat data and outputs two or three files:
filename (.voxel.json) - JSON metadata including bounds, resolution, and array sizesThe binary file layout is:
Parameters
options (WriteVoxelOptions): Options including filename, data, and voxelization settings.fs (FileSystem): File system for writing output files.Returns Promise<void>
Example
import { writeVoxel, MemoryFileSystem } from '@playcanvas/splat-transform';
const fs = new MemoryFileSystem();
await writeVoxel({
filename: 'scene.voxel.json',
dataTable: myDataTable,
voxelResolution: 0.05,
opacityCutoff: 0.1,
collisionMesh: true,
createDevice: async () => myGraphicsDevice
}, fs);
Variable
Public logger surface.
Open named, timed scopes with Logger.group. Pass { index, total }
to render the group as part of a numbered series. Indeterminate progress is
reported with Logger.bar. Free-form messages route through info /
warn / error / debug, indented under whatever is on top of the
active-scope stack.
Both group and bar are pure-push operations: opening a new scope simply
places it on top of the stack without auto-closing siblings, so call order
directly determines nesting. Close scopes with handle.end() after the
body. Callers that route failures through Logger.error get scope
cleanup for free; embedders that swallow exceptions should call
Logger.unwindAll from their catch to close every still-open scope.
const logger: { bar: any; debug: any; error: any; getVerbosity: any; group: any; info: any; output: any; setRenderer: any; setVerbosity: any; unwindAll: any; warn: any }
Properties
bar (any)debug (any)error (any)getVerbosity (any)group (any)info (any)output (any)setRenderer (any)setVerbosity (any)unwindAll (any)warn (any)Variable
The splat-transform revision (short Git hash of HEAD at build time).
const revision: "$_CURRENT_REVISION" = '$_CURRENT_REVISION'
Variable
The splat-transform version (semver MAJOR.MINOR.PATCH).
const version: "$_CURRENT_VERSION" = '$_CURRENT_VERSION'