# SplatTransform - 3D Gaussian Splat Converter [![NPM Version](https://img.shields.io/npm/v/@playcanvas/splat-transform.svg)](https://www.npmjs.com/package/@playcanvas/splat-transform) [![NPM Downloads](https://img.shields.io/npm/dw/@playcanvas/splat-transform)](https://npmtrends.com/@playcanvas/splat-transform) [![License](https://img.shields.io/npm/l/@playcanvas/splat-transform.svg)](https://github.com/playcanvas/splat-transform/blob/main/LICENSE) [![Discord](https://img.shields.io/badge/Discord-5865F2?style=flat&logo=discord&logoColor=white&color=black)](https://discord.gg/RSaMRzg) [![Reddit](https://img.shields.io/badge/Reddit-FF4500?style=flat&logo=reddit&logoColor=white&color=black)](https://www.reddit.com/r/PlayCanvas) [![X](https://img.shields.io/badge/X-000000?style=flat&logo=x&logoColor=white&color=black)](https://x.com/intent/follow?screen_name=playcanvas) | [User Guide](https://developer.playcanvas.com/user-manual/splat-transform/) | [API Reference](https://api.playcanvas.com/splat-transform/) | [Blog](https://blog.playcanvas.com/) | [Forum](https://forum.playcanvas.com/) | SplatTransform is an open source library and CLI tool for converting and editing Gaussian splats. It can: πŸ“₯ Read PLY, Compressed PLY, SOG, Streamed SOG, SPZ, SPLAT, KSPLAT, LCC and LCC2 formats πŸ“€ Write PLY, Compressed PLY, SOG, Streamed SOG, SPZ, GLB, CSV, HTML Viewer, LOD, Voxel and WebP image formats πŸ“Š Generate statistical summaries for data analysis πŸ”— Merge multiple splats πŸ”„ Apply transformations to input splats πŸŽ›οΈ Filter out Gaussians or spherical harmonic bands πŸ”€ Reorder splats for improved spatial locality βš™οΈ Procedurally generate splats using JavaScript generators The library is platform-agnostic and can be used in both Node.js and browser environments. ## Installation Install or update to the latest version: ```bash npm install -g @playcanvas/splat-transform ``` For library usage, install as a dependency: ```bash npm install @playcanvas/splat-transform ``` For running on a backend with Docker (including GPU/Vulkan setup), see the [Docker Backend](https://developer.playcanvas.com/user-manual/splat-transform/docker/) guide. > [!TIP] > For one-off conversions without installing anything, try [SuperSplat Convert](https://superspl.at/convert) β€” a browser-based frontend to splat-transform. See the [Convert page docs](https://developer.playcanvas.com/user-manual/supersplat/convert/) for details. ## Guides - [Generating Streamed SOG](https://developer.playcanvas.com/user-manual/splat-transform/streamed-sog/) β€” build a multi-LOD Streamed SOG from a single PLY. - [LOD Streaming](https://developer.playcanvas.com/user-manual/gaussian-splatting/building/lod-streaming/) β€” load and render Streamed SOG output in a PlayCanvas app. - [Collision Mesh Generation](https://developer.playcanvas.com/user-manual/splat-transform/collision/) β€” generate voxel/collision data from a splat scene. - [Docker Backend](https://developer.playcanvas.com/user-manual/splat-transform/docker/) β€” run splat-transform on a backend (incl. GPU/Vulkan setup). - [Library Usage](https://developer.playcanvas.com/user-manual/splat-transform/library/) β€” drive splat-transform programmatically from Node.js or the browser. ## Format Specifications | Format | Description | | ------ | ----------- | | [PLY](https://developer.playcanvas.com/user-manual/gaussian-splatting/formats/ply/) | Industry-standard uncompressed format for source, editing and interchange | | [SOG](https://developer.playcanvas.com/user-manual/gaussian-splatting/formats/sog/) | Super-compressed format for web delivery (`meta.json` + WebP textures, bundled or unbundled) | | [Streamed SOG](https://developer.playcanvas.com/user-manual/gaussian-splatting/formats/streamed-sog/) | Multi-LOD chunked SOG for streaming very large scenes (`lod-meta.json`) | | [Voxel](https://developer.playcanvas.com/user-manual/splat-transform/voxel-format/) | Sparse voxel octree for collision detection (`.voxel.json` / `.voxel.bin`) | ## CLI Usage ```bash splat-transform [GLOBAL] input [ACTIONS] ... output [ACTIONS] ``` **Key points:** - Input files become the working set; ACTIONS are applied in order - The last file is the output; actions after it modify the final result - Use `null` as output to discard file output ## Supported Formats | Format | Input | Output | Description | | ------ | ----- | ------ | ----------- | | `.ply` | βœ… | βœ… | Standard PLY format | | `.sog` | βœ… | βœ… | Bundled super-compressed format (recommended) | | `meta.json` | βœ… | βœ… | Unbundled super-compressed format (accompanied by `.webp` textures) | | `lod-meta.json` | βœ… | βœ… | Streamed LOD data stored in SOG chunks | | `.compressed.ply` | βœ… | βœ… | Compressed PLY format (auto-detected and decompressed on read) | | `.spz` | βœ… | βœ… | Compressed splat format (Niantic format, v2–4) | | `.lcc` | βœ… | ❌ | LCC file format (XGRIDS) | | `.lcc2` | βœ… | ❌ | LCC2 file format (XGRIDS, octree) | | `.ksplat` | βœ… | ❌ | Compressed splat format (mkkellogg format) | | `.splat` | βœ… | ❌ | Compressed splat format (antimatter15 format) | | `.mjs` | βœ… | ❌ | Generate a scene using an mjs script (Beta) | | `.glb` | ❌ | βœ… | Binary glTF with [KHR_gaussian_splatting](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_gaussian_splatting) extension | | `.csv` | ❌ | βœ… | Comma-separated values spreadsheet | | `.html` | ❌ | βœ… | HTML viewer app (single-page or unbundled) based on SOG | | `.voxel.json` | ❌ | βœ… | Sparse voxel octree for collision detection | | `.webp` | ❌ | βœ… | Lossless WebP image rendered from a camera view via GPU rasterizer | | `null` | ❌ | βœ… | Discard output (useful with `--stats` for analysis-only runs) | ### Antialiased and 2DGS scenes Scenes trained with antialiasing or as 2DGS are tagged, and the tag is preserved where the output format can hold it. `--info` reports it as `model`. On read: a PLY header comment β€” Brush's `comment SplatRenderMode: default | mip | 2dgs` or Postshot's `comment antialiased 0 | 1` (last one wins); SPZ's antialiased header bit; a SOG `meta.json` `"model"` entry. A PLY with `scale_0`/`scale_1` but no `scale_2` is read as 2DGS regardless of comments, and the missing column is materialized as a zero-thickness scale so the rest of the pipeline is unaffected. On write: `.ply` and `.compressed.ply` carry `comment SplatRenderMode: mip | 2dgs` (Brush's spelling, whichever form was read); `.sog` and `meta.json` carry `"model": "antialiased" | "2dgs"`; `.spz` sets its antialiased bit, and warns that it cannot represent 2DGS. A 2DGS PLY output drops the `scale_2` column again. Other output formats have nowhere to record it and drop the tag silently. Combining inputs whose models disagree warns and writes the result untagged. ## Actions Actions execute in the order specified and can be repeated. Any action may appear after any input or output file: ```none -t, --translate Translate Gaussians by (x, y, z) -r, --rotate Rotate Gaussians by Euler angles (x, y, z), in degrees -s, --scale Uniformly scale Gaussians by factor -H, --filter-harmonics <0|1|2|3> Remove spherical harmonic bands > n -N, --filter-nan Remove Gaussians with NaN values, most Inf values, or a zero-norm (unrenderable) rotation quaternion; retains +Infinity in opacity and -Infinity in scale_* -B, --filter-box Remove Gaussians outside box (min, max corners) -S, --filter-sphere Remove Gaussians outside sphere (center, radius) -V, --filter-value Keep Gaussians where cmp ∈ {lt,lte,gt,gte,eq,neq} opacity, scale_*, f_dc_* use transformed values (linear opacity 0-1, linear scale, linear color 0-1). Append _raw for raw PLY values (e.g. opacity_raw). -d, --decimate Simplify at a uniform rate everywhere Use n% for a percentage. --decimate-adaptive allocates removal by local error instead: much better on mixed-scale content such as skies, at higher memory cost. Memory-bounded and streaming: scales to scenes of 100M+ Gaussians. Must be the final action, and the output must be .ply (write a decimated PLY first, then convert in a second invocation). A deep target on a huge scene needs --scratch-dir for its intermediate levels; nothing is written to disk without it. --scratch-dir Directory for decimation intermediates (explicit only) -F, --filter-floaters [size,op,min] Remove Gaussians not contributing to any solid voxel. Evaluates each Gaussian at occupied voxel centers. Default: size=0.05, opacity=0.1, min=0.004 (1/255). Bare flag (no value) uses all defaults. -C, --filter-cluster [res,op,min] Keep only the connected cluster at --seed-pos. GPU-voxelizes at coarse resolution (res world units/voxel). Default: res=1.0, opacity=0.999, min=0.1. Bare flag (no value) uses all defaults. -p, --params Pass parameters to .mjs generator script -l, --tag-lod Tag the Gaussians with LOD level n (n >= 0, or -1 for environment) --stats [text|json] Print file info, per-column statistics and the fill/overdraw ratio to stdout. Default: text --info [text|json] Print structural metadata (format, per-LOD counts, extra columns) to stdout. Default: text -m, --morton-order Reorder Gaussians by Morton code (Z-order curve) ``` ## CLI Options These options configure a run as a whole rather than operating on splat data β€” most groups apply only when reading or writing a specific format. ### General Options ```none -h, --help Show this help and exit -v, --version Show version and exit -q, --quiet Suppress non-error output --verbose Show debug-level diagnostics --memory Show peak memory in progress output --tty Interactive bar rendering (default on a TTY; --no-tty to disable) -w, --overwrite Overwrite output file if it exists --webp-effort <0-9> Lossless WebP compression effort for image, SOG, HTML and LOD output. Higher tries harder to reduce size. Default: libwebp’s default lossless settings. ``` ### GPU Options Used by SOG compression and GPU voxelization (`--filter-cluster`, `--filter-floaters`, `.voxel.json` output). ```none --list-gpus List available GPU adapters and exit -g, --gpu Device for GPU operations: GPU adapter index | 'cpu' ('cpu' disables GPU and is incompatible with GPU-only features like --filter-cluster) ``` ### SOG Compression Options Apply when writing `.sog`, `meta.json`, `lod-meta.json`, or `.html` outputs. ```none -i, --sh-iterations Iterations for SH compression (more=better). Default: 10 --max-workers Worker threads for SOG encoding (0 = inline/serial). Default: 4 ``` ### SPZ Output Options Apply when writing `.spz` outputs. ```none --spz-version <3|4> The SPZ format version to write. Default: 4 ``` ### HTML Viewer Output Options Apply when writing `.html` outputs. ```none --viewer-settings HTML viewer settings JSON file --unbundled Generate unbundled HTML viewer with separate files ``` > [!NOTE] > See the [SuperSplat Viewer Settings Schema](https://github.com/playcanvas/supersplat-viewer?tab=readme-ov-file#settings-schema) for details on how to pass data to the `--viewer-settings` option. ### LOD Input Options Apply when reading `lod-meta.json`, `.lcc`, and `.lcc2` files. ```none -L, --select-lod Comma-separated LOD levels to read from streamed SOG / LCC / LCC2 input ``` ### LOD Output Options Apply when writing `lod-meta.json` (multi-LOD streaming SOG bundle). ```none --lod-chunk-count Approximate number of Gaussians per LOD chunk in K. Default: 512 --lod-chunk-extent Approximate size of an LOD chunk in world units (m). Default: 16 --lod-chunk-min Gaussians in K below which a chunk is not split for extent. Default: 8 ``` A chunk is split when it holds more than `--lod-chunk-count` Gaussians, or when it is wider than `--lod-chunk-extent` and holds more than `--lod-chunk-min`. The minimum keeps sparse regions such as sky or distant background from being cut into thousands of near-empty chunks: below it a region stays one chunk however wide it is. Dense regions are unaffected. See [Generating Streamed SOG](https://developer.playcanvas.com/user-manual/splat-transform/streamed-sog/) for an end-to-end walkthrough. ### Voxel Output Options Apply when writing `.voxel.json` (sparse voxel octree for collision detection). See the [Collision Mesh](https://developer.playcanvas.com/user-manual/splat-transform/collision/) guide for a deep dive on each step and tuning. ```none --voxel-size Voxel size for .voxel.json. Default: 0.05 --voxel-opacity Voxel opacity threshold for .voxel.json. Default: 0.1 --voxel-external-fill [size] Seal exterior voxels via boundary flood fill (interior scenes). [size] (world units) is the dilation distance applied before the flood fill to bridge small wall gaps. --seed-pos is used to verify the volume is enclosed at the seed; the fill is skipped if the seed is reachable from outside. Default size: 1.6 --voxel-floor-fill [size] Fill each column upward from bottom until hitting solid (exterior scenes). Optional size (world units): only patch XZ areas surrounded by floor within 2*size; large empty exterior areas are left alone. Default size: 1.6 --voxel-carve [h,r] Carve navigable space using capsule flood fill from seed. Default: height=1.6, radius=0.2 --seed-pos Seed position for voxel fill/carve and --filter-cluster. Default: 0,0,0 --collision-mesh [smooth|faces] Generate collision mesh (.collision.glb). Default: smooth ``` ### Image Output Options Apply when writing `.webp` (lossless WebP rendered via GPU rasterizer). ```none --projection Camera projection. Default: pinhole. equirect = 360°×180Β° panorama from --camera-pos; --camera-fov must be omitted; --resolution must be 2:1 (default 2048x1024). --camera-pos Camera position in world space. Default: 2,1,-2 --camera-target Camera target point. Default: 0,0,0 --camera-up World up vector. Default: 0,1,0 --camera-fov Vertical field of view in degrees. Default: 60. Rejected with --projection equirect. --resolution Output resolution, e.g. 1920x1080. Default: 1280x720 (pinhole) or 2048x1024 (equirect) --camera-near Near clip distance. Default: 0.2 (matches reference 3DGS) --background Background color in [0,1]. Default: 0,0,0,1 --f-stop Aperture as a photographic f-stop (e.g. 2.8, 5.6, 11). Enables defocus blur; smaller = more blur. Pinhole only. Default: disabled (no defocus). --focus-distance Camera-space Z of the focus plane (world units). Default: distance to --camera-target. Pinhole only; only meaningful with --f-stop. --dof-samples Aperture samples per instant with --f-stop. Default: 32. More samples reduce sampling artifacts at greater cost; multiplies --motion-samples when combined. --sensor-size Vertical sensor height in world units. Gives --f-stop a physical meaning. Default: 0.024 (35mm full-frame, world units = meters). Scale to your world: world unit = decimeter β†’ 0.24, world unit = millimeter β†’ 24. --camera-pos-end End camera position. When set, enables camera motion blur: the camera moves from --camera-pos (shutter open) to --camera-pos-end (shutter close) and the frame averages renders at instants across the shutter. Default: disabled. --camera-target-end End camera target. Default: same as --camera-target. Only with --camera-pos-end. --camera-up-end End up vector. Default: same as --camera-up. Only with --camera-pos-end. --shutter <0..1> Fraction of the startβ†’end segment averaged, centered on its midpoint. Default: 0.5. With --camera-track, fraction of the frame interval averaged around each frame. Default for tracks: off. 1.0 = full interval; 0.5 = 180Β° shutter. --motion-samples Renders averaged per motion-blurred frame, at evenly spaced instants across the shutter. Cost is NΓ— a single render; too few show as discrete copies where the motion between instants exceeds a couple of pixels. Default: 16. --camera-track Render a camera animation as a frame sequence: a supersplat editor project (.ssproj directory or its document.json), a viewer settings.json with animTracks, or a JSON { frameRate, frames: [{ position, target, fov, up }] }. Frames are written as .NNNN.webp. Replaces --camera-pos/--camera-target; the track's target is the defocus focus point. A frame's up vector tilts the camera; frames without one use --camera-up. With --shutter, each frame is motion-blurred over that fraction of the frame interval. --frames Inclusive frame range of the track to render. Default: all frames. ``` ## Examples ### Basic Operations ```bash # Simple format conversion splat-transform input.ply output.csv # Convert from .splat format splat-transform input.splat output.ply # Convert from .ksplat format splat-transform input.ksplat output.ply # Convert to compressed PLY splat-transform input.ply output.compressed.ply # Uncompress a compressed PLY back to standard PLY # (compressed .ply is detected automatically on read) splat-transform input.compressed.ply output.ply # Convert to SOG bundled format splat-transform input.ply output.sog # Convert to SOG unbundled format splat-transform input.ply output/meta.json # Convert from SOG (bundled) back to PLY splat-transform scene.sog restored.ply # Convert from SOG (unbundled folder) back to PLY splat-transform output/meta.json restored.ply # Convert to standalone HTML viewer (bundled, single file) splat-transform input.ply output.html # Convert to unbundled HTML viewer (separate CSS, JS, and SOG files) splat-transform --unbundled input.ply output.html # Convert to HTML viewer with custom settings splat-transform --viewer-settings settings.json input.ply output.html ``` ### Transformations ```bash # Scale and translate splat-transform bunny.ply -s 0.5 -t 0,0,10 bunny_scaled.ply # Rotate by 90 degrees around Y axis splat-transform input.ply -r 0,90,0 output.ply # Chain multiple transformations splat-transform input.ply -s 2 -t 1,0,0 -r 0,0,45 output.ply ``` ### Filtering ```bash # Remove entries containing NaN and Inf splat-transform input.ply --filter-nan output.ply # Filter by opacity values (keep only splats with opacity > 0.5) splat-transform input.ply -V opacity,gt,0.5 output.ply # Strip spherical harmonic bands higher than 2 splat-transform input.ply --filter-harmonics 2 output.ply # Simplify to 50000 splats via progressive pairwise merging splat-transform input.ply --decimate 50000 output.ply # Simplify to 25% of original splat count splat-transform input.ply -d 25% output.ply ``` ### Advanced Usage ```bash # Combine multiple files with different transforms splat-transform -w cloudA.ply -r 0,90,0 cloudB.ply -s 2 merged.compressed.ply # Apply final transformations to combined result splat-transform input1.ply input2.ply output.ply -t 0,0,10 -s 0.5 ``` ### Statistics Generate per-column statistics for data analysis or test validation: ```bash # Print stats, then write output splat-transform input.ply --stats output.ply # Print stats without writing a file (discard output) splat-transform input.ply --stats null # Print stats as JSON for scripting splat-transform input.ply --stats json null # Print stats before and after a transform splat-transform input.ply --stats -s 0.5 --stats output.ply ``` The output starts with the file info block (including the `gaussian` verdict β€” `false` for a readable container that isn't splat data, such as a plain point-cloud PLY), followed by min, max, median, mean, stdDev, nanCount, infCount and a histogram for each column, one table per LOD. Each LOD also reports a `fillRatio` β€” total splat footprint area over the scene's robust (p1–p99) cross-section, approximately the average overdraw layer count: healthy scenes score in the ones-to-hundreds, while degenerate or adversarial scenes that would overwhelm a GPU with fill score orders of magnitude higher, making the value suitable for automated publish gating. A `+Infinity` scale propagates to an infinite ratio, which serializes as `null` in JSON β€” treat that as a reject. The JSON form is the same info fields plus a columnar per-LOD `stats` array. The stats are computed in a single streaming pass; the median is approximated from a 1024-bin histogram (error within ~1/1000 of the column's range), all other fields are exact. ### Generators (Beta) Generator scripts can be used to synthesize gaussian splat data. See [gen-grid.mjs](generators/gen-grid.mjs) for an example. ```bash splat-transform gen-grid.mjs -p width=10,height=10,scale=10,color=0.1 scenes/grid.ply -w ``` ### Voxel Format The voxel format stores sparse voxel octree data for collision detection. It consists of two files: `.voxel.json` (metadata) and `.voxel.bin` (binary octree data). Pass `--collision-mesh` to also emit a `.collision.glb` mesh derived from the voxel grid. For a step-by-step walkthrough of each option (with illustrations), see the [Collision Mesh](https://developer.playcanvas.com/user-manual/splat-transform/collision/) guide. #### Recommended pipeline ```bash splat-transform input.ply \ --filter-cluster --seed-pos x,y,z \ [--voxel-external-fill | --voxel-floor-fill] [--voxel-carve] \ [--collision-mesh [smooth|faces]] \ output.voxel.json ``` `--filter-cluster` isolates the central scene and discards stray floaters before voxelization. `--seed-pos` is shared by `--filter-cluster` and the voxel fill/carve passes β€” set it once to a known-walkable point inside the scene. #### Interior scenes (rooms, indoor scans) Use `--voxel-external-fill` to seal the void around the room interior, then `--voxel-carve` to hollow out the navigable space: ```bash splat-transform room.ply \ --filter-cluster --seed-pos 0,1,0 \ --voxel-external-fill --voxel-carve \ --collision-mesh room.voxel.json ``` #### Exterior scenes (outdoor objects, terrain) Use `--voxel-floor-fill` to fill the ground beneath surfaces, optionally followed by `--voxel-carve`: ```bash splat-transform terrain.ply \ --filter-cluster --seed-pos 0,0,0 \ --voxel-floor-fill \ --collision-mesh terrain.voxel.json ``` #### Other examples ```bash # Voxelize with custom resolution and opacity threshold splat-transform --voxel-size 0.1 --voxel-opacity 0.3 input.ply output.voxel.json # Custom carve capsule (height, radius) splat-transform --seed-pos 1,0,0 --voxel-carve 2.0,0.3 input.ply output.voxel.json # Watertight voxel-face collision mesh splat-transform --collision-mesh faces input.ply output.voxel.json ``` ### Image Rendering Render a splat scene to a lossless WebP image from a given camera view. Rendering runs on the GPU. ```bash # Default 1280x720 render splat-transform input.ply view.webp # Custom camera and resolution splat-transform input.ply view.webp \ --camera-pos 2,1,-2 --camera-target 0,0,0 \ --camera-fov 50 --resolution 1920x1080 # Transparent background splat-transform input.ply view.webp --background 0,0,0,0 # Defocus blur (focus on camera-target, f/2.8 aperture) splat-transform input.ply view.webp --f-stop 2.8 # Smoother aperture sampling for strongly defocused edges splat-transform input.ply view.webp --f-stop 1 --dof-samples 64 # Defocus with explicit focus distance and a smaller world scale splat-transform input.ply view.webp \ --f-stop 2.8 --focus-distance 3 --sensor-size 0.1 # 360Β° equirectangular panorama from camera position splat-transform input.ply pano.webp \ --projection equirect --camera-pos 0,1,0 --camera-target 0,1,1 # Camera motion blur (dolly from start to end pose over a 180Β° shutter, 16 instants averaged) splat-transform input.ply view.webp \ --camera-pos 2,1,-2 --camera-pos-end 3,1,-2 \ --shutter 0.5 --motion-samples 16 ``` Depth of field and motion blur resolve visibility separately for each sample, then average in linear light before encoding the final image. Reconstructed splat colours are treated as sRGB; transparent outputs use premultiplied linear colour during averaging. When both effects are enabled, each shutter instant uses `--dof-samples` aperture views. ### Device Selection for SOG Compression When compressing to SOG format, you can control which device (GPU or CPU) performs the compression: ```bash # List available GPU adapters splat-transform --list-gpus # Let WebGPU automatically choose the best GPU (default behavior) splat-transform input.ply output.sog # Explicitly select a GPU adapter by index splat-transform -g 0 input.ply output.sog # Use first listed adapter splat-transform -g 1 input.ply output.sog # Use second listed adapter # Use CPU for compression instead (much slower but always available) splat-transform -g cpu input.ply output.sog ``` > [!NOTE] > When `-g` is not specified, WebGPU automatically selects the best available GPU. Use `--list-gpus` to list available adapters with their indices and names. The order and availability of adapters depends on your system and GPU drivers. Use `-g ` to select a specific adapter, or `-g cpu` to force CPU computation. > [!WARNING] > CPU compression can be significantly slower than GPU compression (often 5-10x slower). Use CPU mode only if GPU drivers are unavailable or problematic. ## Getting Help ```bash # Show version splat-transform --version # Show help splat-transform --help ``` --- ## Library Usage SplatTransform exposes a programmatic API for reading, processing, and writing Gaussian splat data. Scenes flow through lazy, chunked `ChunkSource`s, so resident memory is bounded by chunk size rather than scene size β€” the same pipeline the CLI uses to process scenes of hundreds of millions of Gaussians. ```typescript import { Vec3 } from 'playcanvas'; import { readFile, writeSource, getInputFormat, getOutputFormat, createChunkDataPool, processSourceBridged, UrlReadFileSystem, MemoryFileSystem } from '@playcanvas/splat-transform'; // Read a PLY file from a URL as a lazy, chunked source const fileSystem = new UrlReadFileSystem('https://example.com/'); const [source] = await readFile({ filename: 'scene.ply', inputFormat: getInputFormat('scene.ply'), fileSystem }); // Apply actions: transforms compose lazily, filters stream chunk-by-chunk const pool = createChunkDataPool(); const processed = await processSourceBridged(source, [ { kind: 'scale', value: 0.5 }, { kind: 'translate', value: new Vec3(0, 1, 0) }, { kind: 'filterNaN' } ], pool); // Stream the result to an in-memory PLY const memFs = new MemoryFileSystem(); await writeSource({ filename: 'output.ply', outputFormat: getOutputFormat('output.ply', {}), source: processed, pool, options: {} }, memFs); await processed.close(); // Get the output data const outputBuffer = memFs.results.get('output.ply'); ``` > [!NOTE] > For the full walkthrough β€” key exports, file system abstractions, processing actions, custom logging β€” see the [Library Usage](https://developer.playcanvas.com/user-manual/splat-transform/library/) guide. The TypeDoc reference for every export lives at [api.playcanvas.com/splat-transform](https://api.playcanvas.com/splat-transform/).