pointcloud-viewer
A LiDAR cloud read by Rust compiled to WebAssembly, then laid on the map by three.js: it all happens in the browser.
- Stack
- Rust · WASM · React · three.js
- Year
- 2026
- Repository
- github.com/Popoch39/pointcloud-viewer ↗
Anatomy
7 parts · only one countsPick the file
A .las or .laz goes off to be converted; a .bin already converted opens straight away.
openFile()78
const openFile = useCallback(79
(file: File) => {80
if (/\.(bin|pcbk)$/i.test(file.name)) {Work in the background
Converts off to one side so the page never freezes, then hands back the result uncopied.
self.onmessage13
let wasmReady: Promise<unknown> | null = null15
self.onmessage = async (event: MessageEvent<BakeRequest>) => {16
const post = (message: BakeWorkerMessage, transfer: Transferable[] = []) =>Convert the points
Rust rereads each point, re-centring it on the cloud’s corner to stay precise in 32 bits.
bake_to_pcbk()48
pub fn bake_to_pcbk(49
las_bytes: Vec<u8>,50
mut on_progress: impl FnMut(u64, u64),Read the in-house format
A 72-byte header, then 18 bytes per point, read in place without decoding a thing.
parseBakedPointCloud()41
export function parseBakedPointCloud(buffer: ArrayBuffer): BakedPointCloud {42
if (buffer.byteLength < HEADER_SIZE) {43
throw new Error('PCBK buffer is smaller than its header')Put the cloud on the map
Only the cloud’s corner goes from Lambert-93 to GPS; every point hangs off it.
computeAnchor()22
export function computeAnchor(cloud: BakedPointCloud, epsg: number): Anchor {23
const { worldOffset, bboxMin, bboxMax } = cloud24
const lngLat = toWgs84(epsg, worldOffset.x, worldOffset.y)Draw the points
three.js draws the cloud over the base map, in a single upload to the GPU.
PointCloudLayer25
export class PointCloudLayer implements CustomLayerInterface {26
readonly id = 'pointcloud'27
readonly type = 'custom' as constChange the colours
RGB, intensity, class or altitude: a single value changes, the points stay put.
createPointsMaterial()127
export function createPointsMaterial(128
cloud: BakedPointCloud,129
mode: ColorMode,
Aim at the ground: the head turns towards the pointer · click the wheel: colourTouch the ground: the head turns as far as the point · tap the wheel: colour
Parts list
- 01 LAS/LAZ card The chosen file. A .las or .laz goes off to be baked in a Worker; a .bin already baked opens on the spot. Code: src/hooks/use-bake.ts, lines 78–87 ↗
- 02 Sealed housing The Web Worker: it reads the file’s bytes, initialises WebAssembly only once, reports progress and hands back the PCBK by transfer, without a copy. Code: src/workers/bake-worker.ts, lines 13–37 ↗
- 03 Mirror drum The Rust bake compiled to WebAssembly: it walks through the points of the LAS/LAZ, re-centres them on the minimum of each axis so they stay precise in f32, then writes the 72-byte header and the planar blocks. Code: webassembly/src/lib.rs, lines 48–132 ↗
- 04 PCBK disc The in-house format: checks the magic and the version, then lays TypedArray views over the buffer, decoding nothing. Code: src/lib/baked-pointcloud.ts, lines 41–91 ↗
- 05 Corner marker The anchor: only the World Offset is reprojected (proj4 to WGS84, then Mercator), and the whole cloud is placed around it. Code: src/lib/map-anchor.ts, lines 22–46 ↗
- 06 Point cloud The three.js layer on MapLibre’s WebGL context: positions sent as they are, matrices composed in doubles on every frame. Code: src/lib/pointcloud-layer.ts, lines 25–121 ↗
- 07 Filter wheel The colour mode (RGB, intensity, classification, altitude): one uniform changes, the geometry stays. Code: src/lib/pointcloud-shaders.ts, lines 127–156 ↗