DOCS · CONCEPTS · PERFORMANCE

Performance

What to expect, per runtime: WebAssembly overhead on browser and Node, native speed on mobile, plus bundle size and the settings that affect it.

REAL NUMBERS, ONE MACHINE Every number here is measured, not estimated, by driving gdal3.js itself (in Node and in real browsers) against the native GDAL CLI. Machine: Apple M3 Max (12 performance + 4 efficiency cores), 48 GB RAM, macOS, SSD. Tool versions: native GDAL 3.13.0, gdal3.js GDAL 3.13.1, Node v24.18.0. Median of repeated runs; the native baseline has its per-call process-start cost subtracted, so it is GDAL compute time, not the wall-clock of a cold CLI. Runner and full data: the tools/bench suite.

Browser & Node (WebAssembly)

Every figure here is a real measurement, taken by driving gdal3.js itself: in Node (the wasm-bundle) and in each browser engine (a small page that loads the library, mounts a file and times the call). Native is the GDAL CLI. The dataset is a 2048×2048 single-band GeoTIFF (4 MP) and a 50k-point vector; a real RGB or multi-band image would scale these up. Median of repeated runs. Full per-size and per-engine data: the bench suite.

Per operation

One table per operation, every platform as a row. gdal3.js here is the default multi-threaded build (the one the bundler plugins ship); native is the GDAL CLI. Median milliseconds.

Reproject a raster (warp), the only operation here that uses threads:

PLATFORM1 THREAD2 THREADS
native GDAL58 ms35 ms
gdal3.js (Node)107 ms57 ms
gdal3.js (browser, Chromium)98 ms50 ms

Note: a single-band 4 MP raster, shown at one thread then at NUM_THREADS=2. Of the operations here, warp gains the most from threads in the wasm build (see the threads note below).

Write a Cloud-Optimized GeoTIFF, by compression codec:

PLATFORMLZWZSTDDEFLATE
native GDAL43 ms20 ms31 ms
gdal3.js (Node)46 ms23 ms31 ms
gdal3.js (browser, Chromium)44 ms22 ms28 ms

Note: the same raster, three codecs that all ship in the wasm build. Choose by the usual size-versus-speed trade-off, exactly as you would natively.

Hillshade a DEM (gdaldem):

PLATFORMTIME
native GDAL20 ms
gdal3.js (Node)34 ms
gdal3.js (browser, Chromium)24 ms

Note: a single-band 4 MP DEM, single-threaded on every platform.

Convert 50k features to FlatGeobuf (ogr2ogr):

PLATFORMTIME
native GDAL217 ms
gdal3.js (Node)186 ms
gdal3.js (browser, Chromium)173 ms

Note: 50k random points. The wasm copy edges native here because ogr2ogr is allocation-heavy, where wasm's lean allocator and warm, long-lived module beat a freshly spawned CLI on the host's system allocator. That is platform-dependent, not a general "wasm is faster for vectors" result; compute-bound raster work still favours native.

Engines do differ, sometimes by a lot, so treat per-engine absolutes as indicative; the browser bench records Chromium, Firefox and WebKit side by side.

iOS & Android (native)

On React Native, GDAL runs as a real native library over JSI, so there's no WebAssembly overhead and no browser memory ceiling. Expect native GDAL performance.

When you need maximum throughput

On a server where you can install native binaries, a native GDAL binding will be faster than wasm. If raw server throughput is the priority, reach for one.

Bundle size & first load

The full build is roughly 14 MB gzipped on first load (≈11.5 MB wasm + ≈2.2 MB driver/PROJ data), the price of the complete 189-driver registry. It loads lazily when you call initCppJs() and is cached after the first run. For size-capped targets, drop unused dependencies from the build or use the in-progress minimal driver-subset build.

Runtime tips

Caching & offline

After the first visit the wasm and data come from HTTP cache; with a service worker (PWA) they're available entirely offline, there are no runtime calls to any server. Serve with compression (brotli) and far-future cache headers; the files are content-addressed by your bundler.