DocsGetting startedIntroduction
guide·~5 min·updated 2026.04.20
v0.0.4

Introduction to MapGPU

MapGPU is a WebGPU-native GIS engine. One npm package (mapgpu), 12 tree-shakeable subpaths, a WASM-backed spatial core, and a render pipeline that targets the GPU end-to-end — raster tiles, vector tiles, 3D buildings, glTF models and custom WGSL all on the same encoder.

§ 01Overview

WebGPU-native matters because the whole draw graph lives on one modern API. Layers share command encoders, uniforms, and bind groups instead of fighting over the WebGL context. Projection math and tile decoding run off-thread in Rust/WASM. There is no WebGL fallback to constrain the shader surface, so features like compute, storage buffers and multi-sample 3D all work as a first-class path.

  • Dual-projection globe — Mode2D (Web Mercator plane) and Mode3D (vertical-perspective sphere) driven by the same scene and renderers.
  • OGC adapters — WMS, WFS, OGC API Features/Maps, plus CZML, GPX, KML, XYZ and vendor providers (Bing, Mapbox, ArcGIS, WMTS).
  • Rust/WASM spatial core — projection, triangulation (earcutr), MVT parsing, spatial clustering, terrain meshing and hillshade.
◆ one packageone package · twelve subpaths · zero monorepo overhead

§ 02Requirements

MapGPU runs wherever WebGPU does. Today that means:

  • Chrome / Edge 113+ — stable, no flags needed.
  • Safari 18 beta — experimental, enable in Develop → Feature Flags.
  • Firefox Nightly — behind dom.webgpu.enabled / launch with --enable-unsafe-webgpu.
  • Node 20+ — only needed to run the examples and the benchmark harness from source.
◆ noteWebGPU is the only backend. There is no WebGL fallback — if the browser cannot negotiate an adapter, the view will not initialize.

§ 03Install

Single package. Every capability is a subpath import — only what you use ships to the client.

bashinstall · npm / pnpm / yarn
1$ npm install mapgpu
2$ pnpm add mapgpu
3$ yarn add mapgpu

Import only the subpaths you actually touch — the rest is tree-shaken out of your bundle:

tsimports.ts
1import { MapView } from "mapgpu/core";
2import { RenderEngine } from "mapgpu/render";
3import { RasterTileLayer } from "mapgpu/layers";

§ 04Your first map

A MapView needs three things: a container, a RenderEngine, and at least one layer. This is the canonical hello-world:

tsapp.ts
1import { MapView } from "mapgpu/core";
2import { RenderEngine } from "mapgpu/render";
3import { RasterTileLayer } from "mapgpu/layers";
4
5const view = new MapView({
6 container: document.getElementById("map")!,
7 center: [32.86, 39.93],
8 zoom: 10,
9 renderEngine: new RenderEngine(),
10});
11
12view.map.add(new RasterTileLayer({
13 id: "osm",
14 urlTemplate: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
15}));

That's it. MapView creates the canvas, boots WebGPU, starts the render loop. Every layer you add lives in the same pipeline, picks through the same ray caster, and survives a 2D ↔ 3D switch without being torn down.

§ 052D ↔ 3D mode

The view exposes a single strategy-pattern hinge: Mode2D (Web Mercator plane) and Mode3D (vertical-perspective sphere). Both run through the same RenderEngine — layers, renderers, events and picking all survive the transition.

tsmode.ts
1// pick the mode at construction
2const view = new MapView({ container, center, zoom, renderEngine, mode: "3d" });
3
4// or switch at runtime — layers stay mounted
5await view.switchTo("3d");
6await view.switchTo("2d");

§ 07Core concepts

MapView is the public entry — the thing you construct and hand a container to. Underneath, ViewCore owns the shared infrastructure: render engine, layer manager, tile scheduler, buffer cache, render loop. Mode2D and Mode3D are strategy implementations of IViewMode — each owns its own camera controller, projection, and per-frame render call, but they share every layer and renderer above them.

tsview.ts
1import { MapView } from "mapgpu/core";
2import { RenderEngine } from "mapgpu/render";
3
4const view = new MapView({
5 container: "#map",
6 center: [32.86, 39.93],
7 zoom: 8,
8 mode: "3d",
9 renderEngine: new RenderEngine(),
10});
11
12await view.switchTo("2d"); // swap strategy

§ 09OGC adapters

mapgpu/adapters covers WMS, WFS, OGC API Features, OGC API Maps, KML, GPX, CZML, Bing, Mapbox, ArcGIS and WMTS. Each protocol gets one abstraction — capabilities discovery, feature fetch, tile URL resolution — so the layer above it stays protocol-agnostic.

tswms.ts
1import { WmsAdapter } from "mapgpu/adapters";
2
3const wms = new WmsAdapter({
4 url: "https://ows.terrestris.de/osm/service",
5 layers: "OSM-WMS",
6});
7const capabilities = await wms.getCapabilities();

§ 10Draw, measure, snap

Tools operate on a target GraphicsLayer — they add, modify and remove features there. SnapEngine is independent: register any number of source layers and it generates snap candidates for every tool that opts in.

tstools.ts
1import { DrawPolygonTool, SnapEngine, MeasureLineTool } from "mapgpu/tools";
2import { GraphicsLayer } from "mapgpu/layers";
3
4const drawLayer = new GraphicsLayer({ id: "drawn" });
5view.map.add(drawLayer);
6
7const snap = new SnapEngine({ tolerance: 16 });
8snap.addSourceLayer(drawLayer);
9
10const draw = new DrawPolygonTool({ targetLayer: drawLayer, snapEngine: snap });
11// measure is its own tool:
12const measure = new MeasureLineTool({ measurementLayer: drawLayer });

§ 11Spatial analysis

Line-of-sight, buffer generation and elevation queries are surfaced through mapgpu/analysis. The hot paths run in the Rust/WASM spatial core — geodesic sampling, terrain intersection, Voronoi buffering — so analyses can fire per-frame without blocking the main thread.

tsanalysis.ts
1import { LosAnalysis, BufferAnalysis, ElevationQuery } from "mapgpu/analysis";
2
3const los = new LosAnalysis(/* provider */);
4const result = los.check({
5 from: [32.86, 39.93, 50],
6 to: [32.90, 39.95, 200],
7});
8
9const buffer = BufferAnalysis.forPoint([32.86, 39.93], 500);

§ 12Custom WGSL layer

Drop your own vertex + fragment shaders into the same render pass via WGSLLayer. Camera and frame uniforms are auto-injected — the camera and frame bindings are already bound by the time your shader runs.

tswgsl-layer.ts
1import { WGSLLayer } from "mapgpu/layers";
2
3view.map.add(new WGSLLayer({
4 vertexShader: /* wgsl */ `
5 @vertex fn vs(@location(0) p: vec2<f32>) -> @builtin(position) vec4<f32> {
6 return camera.viewProjection * vec4<f32>(p, 0.0, 1.0);
7 }
8 `,
9 fragmentShader: /* wgsl */ `
10 @fragment fn fs() -> @location(0) vec4<f32> {
11 return vec4<f32>(1.0, 0.36, 0.10, frame.opacity);
12 }
13 `,
14 vertexBufferLayouts: [{
15 arrayStride: 8,
16 attributes: [{ shaderLocation: 0, offset: 0, format: "float32x2" }],
17 }],
18 animated: true,
19}));