Hacker News

Fable created novel 4D splat format

Structure

A .splat4d file has three parts. A small header carries the bounds, the quantization steps, and a chunk index with absolute byte ranges - everything a client needs to plan its fetches. The static section holds the per-splat masks and base values: fetch it once and the complete scene is on screen. The rest is one self-contained GOP chunk per ~1 s of video, with key streams laid out before delta streams.

"SP4D" + header JSON
STATIC section β†’ full first view
GOP chunk 0 [keys][deltas]
GOP chunk 1
…

Error Bounds

Every attribute of every splat in every decoded frame is within a user-chosen bound of the source - not on average, not in PSNR: pointwise and deterministic.

attribute bound default
position ± millimeters, L∞ per axis ±2 mm
color RGB Β± 8-bit levels per channel Β±4/255
opacity Β± 8-bit levels Β±4/255
rotation Β± quaternion component (units of 1/128, up to sign) exact (Β±0)
scale Β± relative %, per axis Β±2%

Mechanism

SZ/ZFP-style error-bounded quantization (step = 2Γ—bound β‡’ error ≀ bound by construction). After quantization everything is integer math - temporal deltas can never drift, and the Rust and JavaScript decoders reconstruct bit-identical values.

HTTP Delivery

The format is designed for plain HTTP Range requests against S3 / GCS / R2 / any static host - no server logic, no manifest files, no video container. A client needs exactly:

bytes=0-262143 β†’ magic + header JSON (all byte offsets are absolute)

Object stores support this natively. For browser clients, set CORS to allow the Range header and expose Content-Range:

[{
  "AllowedMethods": ["GET", "HEAD"],
  "AllowedOrigins": ["https://your-site"],
  "AllowedHeaders": ["Range"],
  "ExposeHeaders": ["Content-Range", "Content-Length", "Accept-Ranges"]
}]

Payloads are already zstd-compressed inside the container, so store objects with no Content-Encoding - range math stays byte-exact and nothing double-compresses.

Source Data

Eight sequences from three independent capture pipelines: Dynamic 3D Gaussians (CMU Panoptic dome - juggle, boxes, softball, tennis), Neu3D cooking scenes via SpacetimeGaussians/splaTV (flame = backyard BBQ, sear = kitchen chef), and Technicolor (birthday party, 659k splats) - all converted to per-frame antimatter15 .splat files (32 B/splat), 20 fps.

splat4d encodes use default bounds (Β±2 mm / Β±4 color / exact rot / Β±2% scale); gzip is per-frame -9. For context, the best generic lossless baseline (zstd-19 --long over the whole series) reaches only 2.5Γ—. Full methodology and more baselines: BENCHMARKS.md.

Performance

Loading benchmarks.json… Raw WebGPU, a line-by-line port of the antimatter15/splat renderer, pixel-verified against it.

metric local throttled 50 Mbps
full first view (header + static section) 141–157 ms 791 ms
scrub into unbuffered region β†’ keyframe visible - 145 ms
playback 60 fps @ 336k splats Β· worker decode 2.5–27 ms/frame Β· sort 1–25 ms

Encoding

A time series of antimatter15 .splat frames β†’ one small, seekable file:

# Python (pip install splats4d)
splat4d encode -i frames_dir -o out.splat4d

Comments

No comments yet. Start the discussion.