CHAPTER 19
How maps render: tiles, vectors, and the GPU pipeline
From lat/lon to pixels on screen: the full rendering pipeline for raster tiles, vector tiles, and WebGL-based maps like MapLibre and Mapbox.
Understanding how a map renders helps you make better performance decisions: when to use raster tiles, when to switch to vector, why overzooming looks blurry, and what actually happens when you call map.setCenter().
The raster tile pipeline
A traditional web map works like this:
- Pre-render: The server renders every zoom level (0–22) of the world into 256×256 PNG tiles, storing them as
/tiles/{z}/{x}/{y}.png - Request: The browser calculates which tiles cover the viewport and requests them
- Stitch: The browser lays the tiles on a grid with CSS absolute positioning
- Pan: Moving the map repositions the grid; new tiles load at the edges
Zoom 0: 1 tile (1×1 grid), whole world
Zoom 1: 4 tiles (2×2 grid)
Zoom 2: 16 tiles (4×4 grid)
...
Zoom n: 4ⁿ tiles
Total tiles at zoom 14: 4¹⁴ ≈ 268 million. Pre-rendering the full world to zoom 14 takes terabytes. Most tile providers only pre-render land/sea separation at high zoom and compute city tiles on demand.
Raster tile pyramid
z2: 4×4 = 16 tiles
Zoom
2
Tiles per axis
4
Total tiles
16
Tile coordinates
You saw the math in Chapter 5. The important rendering implication: at any zoom level, a tile is exactly 256×256 pixels (or 512×512 for HiDPI). The tile size never changes; the number of tiles grows.
Vector tiles (MVT)
MVT stands for Mapbox Vector Tile, now an open standard. Vector tiles are binary-encoded Protocol Buffer files, not images. Each tile contains geometry (points, lines, polygons) clipped to the tile boundary, plus attribute data for each feature.
Instead of pre-rendered images, vector tiles contain raw geometries and attributes. The browser renders them in real time using WebGL.
Raster tile: PNG image (opaque, ~50–200 KB)
Vector tile: Encoded features (lat/lon, properties, ~5–50 KB compressed)
Vector tiles enable:
- Client-side styling, change colours, fonts, filters without new tiles
- 3D extrusion, buildings, terrain
- Interactivity, hover/click on individual features
- Sharp text, labels re-render at native resolution
The Mapbox Vector Tile (MVT) format uses Protocol Buffers. Coordinates are quantised integers in a local tile coordinate space (usually 0–4096), not geographic degrees.
tile_x = Math.round((lon_merc - tile_origin_x) / tile_size * 4096)
tile_y = Math.round((lat_merc - tile_origin_y) / tile_size * 4096)
The quantisation to 4096 units per tile gives ~1/16th of a pixel resolution at zoom 14 (9.5 m/px), far more than visual precision requires. The integer encoding also compresses dramatically with delta-coding: consecutive vertices in a ring typically differ by small values.
The WebGL pipeline
Modern map renderers (Mapbox GL, MapLibre, deck.gl) use WebGL:
- Geometry upload, vertices uploaded to GPU memory as Float32Array
- Vertex shader, converts tile coordinates to clip space
- Rasterisation, GPU fills triangles and lines
- Fragment shader, computes final pixel colour (applies styles, lighting)
- Composite, multiple layers blended together
The vertex shader for a simple map tile looks roughly like:
uniform mat4 u_matrix; // projection + view + model
attribute vec2 a_pos; // tile coordinate (0–4096)
void main() {
gl_Position = u_matrix * vec4(a_pos, 0.0, 1.0);
}
Continue reading "How maps render: tiles, vectors, and the GPU pipeline"
You've reached the end of the free preview. Unlock all 22 paid chapters, including distance math, bearings, polygons, spatial indexing, and 3D map rendering, plus a downloadable PDF and the companion code repo.
- All 22 paid chapters with worked examples
- Downloadable PDF for offline reading
- Companion GitHub repo (JavaScript + Python)
- Free updates for life
Multiple payment options: card, Wise, and bank transfer.
Related chapters
- Web Mercator and tile math, the inputs to the renderer
- Pixel and world coordinates, how lat/lon becomes a screen position
- 3D buildings and feature extrusion, extrusion in the rendering pipeline
