Skip to content

Scientific geometry

Trained splat .ply ingestion ​

scenes/trained-splat-ply-ingestion
const bytes = asPly(helix(40_000));
const set = readSplatPly(bytes);
plot.showSplats(plot.uploadSplats(set));
plot.frameScene();
return { readout: `${(bytes.length / 1e6).toFixed(1)} MB of .ply, ${set.opacities.length.toLocaleString("en-GB")} splats` };

Calls

4 in the code above, in the order it makes them

readSplatPly(bytes: Uint8Array): SplatSet

Decode a trained Gaussian splat set from the bytes of a .ply file.

The one format this library decodes. Everything else takes numbers a caller already has, which is what a plotting library is for; a trained splat set is the exception, because nobody has one as numbers -- it arrives as a hundred megabytes of .ply from a trainer.

What a trainer writes is not in the units anything draws in, and the file does not say so: the scales are logarithms, the opacity is a logit, and the quaternion is written w first and unnormalised. A reader that takes the numbers at face value produces a set that loads, draws, and is wrong in a way that looks like a bad training run. What comes back here has all of that undone.

Only the base colour is kept. A trained file usually carries higher spherical-harmonic terms as well, which make a surface's colour change with the angle it is seen from; a SplatSet is one colour per splat, so a set drawn from this reads as matte. That is the difference between a good reconstruction and a very good one, not between one that works and one that does not.

plot.showSplats(handle: SplatsHandle): void

Draw the splat set handle names.

plot.uploadSplats(set: SplatSet): SplatsHandle

Upload a Gaussian splat set and return a handle naming it.

plot.frameScene(): void

Point the camera at the middle of everything uploaded, far enough back to see it.

Call after uploading. A scene measured in grid cells or in millimetres is nowhere near where a camera starts, and without this the first thing a caller sees is an empty canvas.