Introduction
gdal3.js puts GDAL, the engine behind most of the GIS world, wherever JavaScript runs: WebAssembly in the browser and Node, and a real native library on iOS and Android. Same API everywhere, no server, no native install.
What it is
gdal3.js compiles GDAL, the real upstream C++, and exposes it to JavaScript:
- Browser, Node & Cloudflare Workers via WebAssembly.
- iOS & Android as a native library over JSI (React Native, New Architecture).
- Built on cpp.js, the C++-to-JavaScript toolchain: GDAL's classes are bound automatically, and a cpp.js bundler plugin (
@cpp.js/plugin-vite,-webpack,-rollup,-rspack,-metro) wires the wasm, data and worker assets, no asset-path wrangling.
You get the GDAL library itself, not its command-line apps: its operations exposed as API calls (translate, vectorTranslate/ogr2ogr, warp, rasterize, buildVRT, demProcessing, grid, nearblack, footprint, info…), the class API (Dataset, Driver, GCP), and ~80 utility statics on Gdal. This build ships 189 format drivers, the set its bundled dependencies enable; GDAL itself supports more, depending on which libraries are compiled in. v3 is a ground-up rewrite on cpp.js; the architecture guide goes under the hood.
That split is deliberate: with v3, compiling and packaging GDAL and the C++ libraries its drivers need is left to cpp.js, which ships them prebuilt through its package registry (an upstream upgrade is a single dependency bump). gdal3.js then concentrates on the parts that face you: easy, ergonomic usage, optimization (leaner, tunable builds) and documentation. Both are maintained by the same author (Buğra Sarı), so the cpp.js layer evolves in lockstep with gdal3.js rather than drifting as a third-party dependency.
Two ways to use it
GDAL is available from both sides of the bridge, and we recommend the C++ path when you need the complete GDAL API or maximum performance: in C++ you reach every GDAL class, mix gdal3.js's wrapper with GDAL's own headers, and batch a whole pipeline without crossing the JS↔wasm boundary on each call. The JavaScript binding exposes a curated wrapper over the same classes.
From C++, write the operation against the wrapper (a class under src/native) and pull in GDAL's own headers where you want the raw API; cpp.js exposes it all to JavaScript:
#pragma once
#include <string>
#include <gdal.h> // GDAL's own header
#include <gdal3js/Gdal.h> // gdal3.js's high-level wrapper
#include <gdal3js/Dataset.h>
class Gis {
public:
static int driverCount() { return GDALGetDriverCount(); }
static std::string convert(std::string in, std::string out) {
auto ds = Gdal::open(in);
ds->vectorTranslate(out, {"-f", "GPKG"})->close();
return out;
}
};
import { initCppJs, Gis } from "./native/gis.h";
await initCppJs();
await Gis.driverCount(); // e.g. 189
await Gis.convert(inputPath, "out.gpkg");
From JavaScript, import the wrapper, and GDAL's own headers too when you want a raw call, and use them directly:
import { initCppJs, Gdal } from "gdal3.js/Gdal.h"; // wrapper
import { GDALGetDriverCount } from "@cpp.js/package-gdal/gdal.h"; // GDAL's own header
const Module = await initCppJs();
await GDALGetDriverCount(); // e.g. 189, straight from GDAL
const ds = await Gdal.openEx(inputPath);
const opts = await Module.toVector("VectorString", ["-f", "GPKG"]);
await ds.vectorTranslate("out.gpkg", opts);
Either way, gdal3.js ships a high-level C++ wrapper for GDAL, modern-C++ classes (Gdal, Dataset, Driver, GCP) over GDAL's C/C++ API, so you rarely reach for the raw C calls by hand, and the same classes are exposed to JS. The quick start has the full file layout, and the architecture guide goes under the hood.
Next steps
- Quick start: install, wire your bundler, convert your first file.
- Bundlers: per-bundler setup (Vite, Webpack, Rollup, Rspack, Metro).
- Examples: task-oriented recipes for common operations.
- API reference: the full surface.