LERC for WebAssembly
v4.2.0WebAssemblyLERC 4.2.0 for browsers, Node.js and edge runtimes, precompiled for wasm32, single-threaded and multi-threaded as @crossbind/port-lerc-wasm.
npm install @crossbind/port-lerc-wasm@betaInstall
crossbind itself arrives with your bundler plugin, or with a new project from npm create crossbind@beta; Bundlers has Vite, Webpack, Rspack and Rollup.
Usage
Each example runs here, in this tab, and prints what the site build checked.
Each example also has a JavaScript only tab: the same task with no C++ file, calling LERC's own headers from @crossbind/port-lerc directly. All 4 work that way.
Compress heights to within 1 cm
LERC's main job: lerc_encode stores a float raster so that no value moves by more than the error you allow, and lerc_decode reads it back. The example checks the bound on every one of the 65,536 heights.
LERC 4.2.0: 262144 B of float32 heights -> 94775 B largest error 0.0099 m, within 1 cm: true
The same lerc_computeCompressedSize, lerc_encode and lerc_decode calls the C++ codec makes. JavaScript now allocates every buffer, copies the float32 bytes in and out as one character per byte, reads the unsigned int blob size back with readNumberAt and works out the error bound, one float32 step under 1 cm, itself. LERC has its version only as the LERC_VERSION_MAJOR, LERC_VERSION_MINOR and LERC_VERSION_PATCH macros, which the binding does not export, so the first line has no version.
LERC: 262144 B of float32 heights -> 94775 B largest error 0.0099 m, within 1 cm: true
Read a blob's header without decoding it
lerc_getBlobInfo answers from the header alone: size, data type, bands, valid pixels, the value range and the largest error the encoder allowed. A tile viewer uses it to size its buffers, or to skip an empty tile, before decoding. The blob comes from the codec in the first example, which asks for one float32 step less than 10 cm.
Lerc2 v6: 256x256, 1 band of float32, 65536 valid pixels, 67591 B heights 1032.268 to 1500.945 m, stored within 0.099878 m
What the C++ wrapper packed into JSON, JavaScript reads straight from the two arrays lerc_getBlobInfo fills: an unsigned int[11] and a double[3] it allocates itself and reads one number at a time with readNumberAt, in the order of InfoArrOrder and DataRangeArrOrder in Lerc_types.h. Both lines print as in the C++ version.
Lerc2 v6: 256x256, 1 band of float32, 65536 valid pixels, 67591 B heights 1032.268 to 1500.945 m, stored within 0.099878 m
Keep integer data exact
Class maps and sensor counts must not change at all. With a maxZError of 0, LERC stores integers exactly (it raises 0 to 0.5, which rounds back to the same whole number) and float32 bit for bit. Here, a 12-bit sensor band in 16-bit pixels.
uint16, 256x256: 131072 B -> 59091 B identical: true
A maxZErr of 0 through the same three calls, with data type 3 (dt_ushort), and the 16-bit values cross as their bytes. The C++ decoder read the type and the size from the blob header so it could decode any blob; this one already knows them and passes them straight to lerc_decode.
uint16, 256x256: 131072 B -> 59091 B identical: true
Leave out pixels that have no data
Rasters mark gaps with a NoData value such as -9999. Stored as a height, it stretches the value range of every block it lands in. Passed as the validity mask (pValidBytes in lerc_encode and lerc_decode), it costs at most a bit per pixel, and decoding hands the mask back.
-9999 stored as a height: 147759 B -9999 as missing pixels: 98308 B 1273 gaps come back as -9999, largest error elsewhere 0.0099 m
JavaScript builds the validity mask itself, a byte per pixel written with writeBytes, and lerc_decode fills a second one on the way back. Missing pixels decode as 0, so putting -9999 back is a JavaScript loop, and the error bound, one float32 step under 1 cm at the largest value each encode stores, is worked out in JavaScript too.
-9999 stored as a height: 147759 B -9999 as missing pixels: 98308 B 1273 gaps come back as -9999, largest error elsewhere 0.0099 m
What is different on WebAssembly
- In a browser the module runs in a Worker by default (
useWorker), so every call returns a promise:awaitcalls and constructors alike. - The module has its own filesystem:
m.FSwrites files,m.getFileBytesreads them back andm.autoMountFilesmountsFileobjects from an<input type=file>./memfslives in memory;/opfspersists across reloads and needs the Worker. See Filesystem. - In Node.js,
m.FSis the real disk, so use real paths there. - Multi-threaded builds (
runtime: 'mt') need COOP and COEP headers in production. See Threading.
Other platforms
- LERC overview: the apps, every platform's setup and the packages.
- LERC for Android: React Native apps on Android.
- LERC for iOS: React Native apps on iOS.
- LERC for WASI: command-line programs under wasmtime.
Facts on this page come from the port manifests in the repository and from what npm served on beta when the site was built. See the Libraries guide for the full consumer flow.