Short answer

WebGL lets a browser push Earth data rendering onto the GPU, which is the only practical way to draw hundreds of thousands of features, large rasters, or point clouds interactively. The core decisions are the data delivery format (vector tiles, raster/COG tiles, or GPU buffers), the rendering library (MapLibre GL for styled basemaps, deck.gl for large analytical layers, CesiumJS for 3D globes), and keeping heavy work off the main thread. Get those right and a multi-million-point dataset stays at 60 fps; get them wrong and the tab freezes regardless of how good the GPU is.

Why WebGL, not SVG or Canvas

Browser mapping has three rendering paths. SVG (the D3 era) creates a DOM element per feature — beautiful for a few hundred shapes, fatal past a few thousand because layout and reflow are CPU-bound. 2D Canvas rasterises on the CPU each frame, which stalls on large redraws and pan/zoom. WebGL uploads geometry once into GPU memory as typed arrays and lets the GPU's parallel pipeline transform and rasterise every vertex per frame. That architectural difference — geometry living in GPU buffers rather than the DOM — is why WebGL maps can interact smoothly with datasets that crush the other two.

The cost is that you think in terms of vertices, buffers, shaders, and draw calls. Libraries hide most of it, but the performance failures still trace back to those concepts.

Choosing a delivery format

Vector tiles

Vector tiles (the Mapbox Vector Tile spec, encoded as protobuf) slice features into a tile pyramid. The browser receives geometry, not pixels, so it can restyle, filter, and pick features client-side. This is the default for interactive geological and GIS layers: faults, units, boundaries, anything you want to recolour or query without a server round-trip. MapLibre GL consumes a JSON style document that binds sources to layers with data-driven paint expressions.

Raster and Cloud Optimized GeoTIFF tiles

For imagery, hillshade, NDVI, or any continuous raster, raster tiles are the right tool. Rather than pre-rendering a fixed pyramid, modern stacks serve a Cloud Optimized GeoTIFF (COG) — a GeoTIFF with internal tiling and overviews laid out so an HTTP range request can fetch just the bytes for one tile. A dynamic tiler (TiTiler, or the older gdal2tiles for static pyramids) reads the COG and returns 512×512 PNG/WebP tiles. You can also read COG tiles directly in the browser with geotiff.js plus range requests, skipping the tile server entirely for read-only viewers.

deck.gl GPU layers

When the dataset is millions of points, arcs, or polygons — borehole collars, AIS tracks, a LiDAR-derived point cloud — deck.gl packs the attributes into typed-array buffers and renders them in a single GPU pass. ScatterplotLayer, HexagonLayer/ScreenGridLayer (GPU aggregation), ArcLayer, and Tile3DLayer (for 3D Tiles / point clouds) cover most Earth-data cases. deck.gl interleaves cleanly with MapLibre so you can keep a styled basemap underneath your analytical overlay.

Worked pattern: a million-point dataset that stays smooth

The failure mode is parsing and converting data on the main thread. The fix is a pipeline:

  1. Deliver compactly. Serve the points as a binary format (Arrow, or a flat typed array) rather than a 200 MB GeoJSON string. GeoJSON forces a full text parse on the main thread.
  2. Parse in a Web Worker. Decode and reproject off-thread so the UI never blocks.
  3. Hand deck.gl typed arrays directly via its binary attribute API (data: {length, attributes: {getPosition: {value: Float32Array, size: 2}}}), so it skips per-object iteration and uploads straight to a GPU buffer.
  4. Aggregate when zoomed out. Swap the raw ScatterplotLayer for a ScreenGridLayer or hexbin so you draw thousands of aggregated cells instead of millions of overlapping dots.

The same dataset rendered as GeoJSON through SVG would never finish parsing; through this pipeline it pans at 60 fps.

Reprojection and the Web Mercator constraint

MapLibre and most tile ecosystems render in Web Mercator (EPSG:3857). Earth data rarely arrives that way — it might be in a UTM zone, a national grid, or geographic EPSG:4326. Reproject before delivery: gdalwarp -t_srs EPSG:3857 src.tif merc.tif for rasters, ogr2ogr -t_srs EPSG:3857 out.fbg in.gpkg for vectors, or transform server-side. deck.gl can render in other projections, and CesiumJS works on a 3D ellipsoid, but if you are on a Mercator basemap, your overlays must agree or they will sit in the wrong place. Be aware that Mercator badly distorts area at high latitudes — for any analysis of size or density, do the maths in a projected CRS and only display in Mercator.

Common pitfalls and why they happen

  • GeoJSON as a transport format for big data. It is human-readable, so it is reached for by default, but a large GeoJSON is a giant string the main thread must parse synchronously. Use vector tiles or binary.
  • Too many draw calls. Splitting features across dozens of layers means dozens of GPU draw calls per frame. Consolidate; let one layer hold many features.
  • Reprojecting in the browser per frame. Coordinate transforms belong in the data pipeline, not the render loop.
  • A plain GeoTIFF served whole. Without COG structure, the client must download the entire file to show one tile. Convert with gdal_translate ... -of COG.
  • 3D for its own sake. A globe looks impressive but a tilted, rotating view makes precise comparison harder. Use Cesium/3D Tiles when elevation or globe context is the point, not as decoration.

QA and validation

  • Profile the frame. Use the browser's performance panel; if the main thread is the bottleneck, your problem is parsing or layout, not the GPU.
  • Test with production-scale data, not a 500-feature sample — the architecture that works at 500 features is not the one that works at 5 million.
  • Verify alignment against a trusted reference layer; misregistration almost always means a CRS mismatch with the Mercator basemap.
  • Watch GPU memory on modest hardware; uploading every attribute at full precision can exhaust mobile GPUs. Drop to Float32 and only the attributes you render.

Bathyl perspective

We build web visualisations so technical depth survives the trip to the browser: the CRS is declared, the source and date travel with the layer, and uncertainty stays visible rather than being smoothed away by a glossy interface. WebGL is the means, not the message — the job is an interface a geologist can interrogate, not a demo that impresses for ten seconds and answers nothing.

Related reading

Sources