Skip to content
LidarKit

How it works

The mechanism, the limits, and how the lossless claim is tested.

At a glance

ConvertsLAZ → LAS (decompress) and LAS → LAZ (compress)
LAS versions1.0 – 1.4
Point formats0, 1, 2, 3, 6, 7, 8 — including extra bytes
Not supportedPoint formats 4, 5, 9, 10 (full waveform data)
Maximum file sizeNo fixed limit; bounded by your free disk space
LosslessYes — round trip verified byte-identical in an automated test
PreservedHeader, user VLRs, EVLRs, coordinate reference system, extra bytes
Where it runsYour browser, in a Web Worker. No server, no upload endpoint.
Account requiredNo
CostFree
BrowsersCurrent Chrome, Edge, Firefox, Safari
Compression librarylaz-perf (Apache-2.0), compiled to WebAssembly

The pipeline

A file moves through five stages, none of which involve a network request:

  1. Header parse. The first few kilobytes are read to validate the signature and pull out the version, point format, point count and VLR layout. This is what lets the tool tell you the file is an unsupported waveform format before doing any work.
  2. Chunked read. The file is sliced through the browser's File API rather than loaded into memory. Compression reads in batches of 50,000 points; decompression pulls input through a synchronous 64 KB read callback, which is possible because workers can useFileReaderSync.
  3. WebAssembly codec. Each batch is handed to laz-perf compiled to WASM. Peak heap usage is a few megabytes and the build caps it at 512 MB, so memory does not scale with file size.
  4. Container assembly. For compression, the LAZ container is assembled in TypeScript around the compressed chunks. This is deliberate — see below.
  5. Disk-backed output. Bytes are written to a temporary file in the origin-private file system through a synchronous access handle, giving random access without an in-memory copy. The download is a reference to that file. Browsers without sync access handles fall back to assembling a Blob in memory.

Why the container is assembled by hand

laz-perf ships its own file writer, and using it would have been the obvious shortcut. It rewrites the header and discards all user VLRs in the process — and since the coordinate reference system is stored in a VLR, as either GeoTIFF keys or a WKT string, the output loads happily somewhere entirely wrong.

That is a bad failure mode: it is silent, and you generally discover it long after the conversion. So the compression path uses only laz-perf's chunk compressor and builds the surrounding container itself, copying the original header, VLRs and EVLRs through unchanged. If you are evaluating any browser-based LAZ tool, this is the thing worth checking.

How the lossless claim is tested

"Lossless" is claimed by every tool in this space, so here is the specific check. An automated test builds spec-conformant LAS files, runs them through real compression and real decompression via the WebAssembly module, and asserts the final bytes equal the original bytes exactly — not just the points, but the header, VLRs and EVLRs too.

Coverage: LAS 1.2, 1.3 and 1.4; point formats 0 to 3; files with extra bytes; and files large enough to span multiple compression chunks, which is where a container-assembly bug would show up. Formats 6 to 8 convert in both directions but are not yet part of that round-trip test.

The raster path

The GeoTIFF viewer shares the privacy architecture but none of the code above — no WebAssembly is involved. A GeoTIFF is a directory of tags pointing at independently compressed tiles, which is a format a browser can read directly:

  1. Directory chain. The IFD chain is walked first, in a handful of short range reads, giving every pyramid level's size, tile offsets, compression, sample format and GeoTIFF keys. Classic TIFF and BigTIFF differ only in the width of three fields.
  2. Level choice. The smallest overview still larger than the screen needs is selected, so the residual downsample is always under 2× and a 600 MB orthophoto is read from a 20 MB level.
  3. Tile decode. Tiles are pulled one at a time. LZW and PackBits are decoded by this site's own code; Deflate goes through the browser's DecompressionStream and JPEG and WebP tiles through createImageBitmap. The horizontal and floating-point predictors are then reversed — the latter has to re-interleave byte planes, and getting that backwards produces a striped surface rather than an error.
  4. Shading and meshing. Each tile is resampled into the display grid and dropped. The finished grid is hillshaded with Horn's 3×3 estimator, tinted by a colour ramp, and decimated into a terrain mesh — all in the worker, so a slider drag re-shades without a re-decode.

The elevation grid stays resident in the worker afterwards. That is what makes changing the sun position a single array pass instead of reading the pyramid again.

Privacy, stated precisely

This site is static files. There is no upload endpoint, no server-side processing, no account system, and no database. Your file is read by JavaScript running in your own browser and never enters a network request.

What that is not: a security guarantee. Your browser, extensions and operating system are outside anyone's control but yours, so the honest claim is about this application's architecture rather than about your machine. The one third-party script on this site is Cloudflare Web Analytics, which counts page views without cookies and without storing anything on your device — which is why you are not being asked to dismiss a consent banner. It cannot see your files: filenames, file contents, coordinates and header metadata never leave the page, because they never leave your browser in the first place.

Limits worth knowing

  • Point formats 4, 5, 9 and 10 are rejected. They carry full waveform data, which LAZ compression does not handle.
  • One file at a time — there is no batch mode. Use PDAL or LAStools for bulk work.
  • Very large files on browsers without sync access handles fall back to in-memory assembly, where available RAM becomes the ceiling.
  • Conversion speed is your CPU, not a server's. On a large file that is usually a win, since there is no upload or queue.
  • The raster viewer reads one pyramid level rather than refining as you zoom, does not reproject, and cannot decode LERC, ZSTD or JPEG 2000 tiles.

Frequently asked questions

Where does the conversion actually happen?
Entirely in your browser, in a Web Worker on your own machine. The site is static files served from a CDN; it has no backend, no database and no upload endpoint, so there is no server for a file to be sent to even in principle.
How can it handle files bigger than available memory?
Input is read in chunks rather than loaded whole, and output is written incrementally to a temporary file in the browser's origin-private file system. Peak WebAssembly heap usage stays at a few megabytes regardless of input size, and the build caps it at 512 MB as a hard ceiling.
What proves the conversion is lossless?
An automated test converts LAS to LAZ and back, then asserts the output is byte-for-byte identical to the input — the public header, all VLRs and EVLRs, and every point record. It runs across LAS 1.2, 1.3 and 1.4, point formats 0 to 3, files with extra bytes, and files large enough to span multiple compression chunks.
Does this work offline?
Yes, once the page has loaded. The WebAssembly module and application code are all that is needed, so you can disconnect from the network and keep converting.