How map tiles work — the deep guide

Every web map you have ever panned — Google Maps, your food-delivery app, a hiking planner — is built from tiles: small square pieces of map, fetched one by one and stitched together in the browser. This guide explains the whole machine: the tile pyramid, how raster and vector tiles are made, how Leaflet and MapLibre draw them, what retina means, why caching decides everything, and how to choose. Each section links to a live example you can open and copy.

1 · The z/x/y tile pyramid 2 · Raster tiles — pixels baked on the server 3 · Vector tiles — data drawn by the browser 4 · Raster vs vector — honest comparison 5 · Leaflet vs MapLibre vs OpenLayers 6 · Retina (@2x) explained 7 · Caching — why the second visitor is free 8 · Styling and customization 9 · The GDPR angle — why tile hosting matters 10 · Load Leaflet/MapLibre from Tilezza 11 · Which should you use? FAQ

1 · The z/x/y tile pyramid

A web map is a pyramid of zoom levels. At zoom 0 the whole world fits on one 256×256 px tile. Each zoom level doubles the resolution in both directions, so the number of tiles quadruples: zoom 1 has 4 tiles, zoom 2 has 16… and zoom 19 has about 275 billion. A tile is addressed by three numbers — z/x/y (zoom, column, row) — which is exactly what you see in a tile URL:

https://tilezza.eu/raster/voyager/12/2264/1432.png
                              └z┘ └─x─┘ └─y─┘

When you pan or zoom, the map library computes which tiles the viewport touches (typically 6–16 of them), requests the missing ones, and slides the rest. That trick — invented for Google Maps in 2005 and standardised as the "slippy map" scheme — is why a map of the whole planet feels instant: you only ever download the handful of squares you are looking at.

Coordinates use the Web Mercator projection (EPSG:3857). It distorts areas near the poles, but it has one killer property: north is always up and squares stay squares at every zoom.

2 · Raster tiles — pixels baked on the server

A raster tile is a finished picture: a 256×256 PNG in which the server has already drawn the roads, water, labels — everything. The browser just displays images; there is nothing left to compute.

How a raster tile is born (our actual pipeline)

OpenStreetMap data (planet, ODbL)
  → Protomaps build → one big .pmtiles archive (vector data, zooms 0–14)
  → tileserver-gl + MapLibre GL Native renders the style on the server (~0.3–1 s per tile)
  → nginx caches the finished PNG on disk for 30 days
  → every later request for that tile: ~0.6 ms (measured), like any static file

The expensive step is the render. That is why the first visitor to an unexplored corner of the map at a deep zoom waits noticeably, while everyone after them gets the tile instantly — see caching below. It is also why raster tiles cost more of your monthly quota than vector tiles (3 units vs 1 on our pricing): we do the heavy lifting.

Try it: the examples page opens with a raster style gallery — 10 ready-made looks, one URL each, working in Leaflet with 5 lines of code.

3 · Vector tiles — data drawn by the browser

A vector tile (.mvt, Mapbox Vector Tile format) contains no pixels at all. It is a compact binary package of the geometry and attributes inside that square: road centrelines with their class, building footprints, water polygons, place names with their importance rank. Think of it as "the map's database, cut into squares".

The drawing happens in your visitor's browser, on the GPU, using a style document — a JSON file that says "motorways are orange, 4 px wide at zoom 10; city labels use Noto Sans 14 px…". The same tiles can look completely different under different styles, and the style can change at runtime without downloading the map again.

What has to exist for vector maps to work

PieceWhat it isOn Tilezza
Tilesthe .mvt data squares/tiles/{z}/{x}/{y}.mvt
Style JSONcolors, widths, fonts, rules/api/style/voyager (+10 more)
Glyphspre-rendered font atlases for labels/fonts/… self-hosted
Spriteone image with all the little icons/sprites/… self-hosted
Renderera JS library (MapLibre GL)served from us too

Because the browser does the work, the server's job is trivial — it streams bytes out of an archive. Our vector endpoint serves a tile in 1.8 ms median and sustains over 13,000 requests/second on modest hardware. That efficiency is why the free tier is vector-first.

Try it: examples 3 and 4 are live vector maps — switch between all 11 styles and recolor the map with URL parameters, no rebuild, no waiting.

4 · Raster vs vector — the honest comparison

Raster (PNG)Vector (MVT + style)
Who rendersour server, once, then cachedthe visitor's browser, every frame
Client requirementsanything that can show imagesJS library + WebGL (any phone since ~2016)
Zoom feelsteps, slight blur between levelscontinuous, always sharp
Rotation / tiltnoyes
Restyle / recolorpick another pre-made styleanything, at runtime, per-URL
Label languageas rendered?lang=de — your choice
Bandwidth~15–70 KB / tile~5–50 KB / tile + one-time fonts
Integration effort1 line in Leaflet2 script tags + 3 lines
Cost to serve (us)render CPU + cache diskalmost nothing
Quota weight (Tilezza)3 units (retina 5)1 unit

Neither is "better". Raster is the universal, zero-effort option; vector is the modern, flexible, cheap-to-serve option. Big consumer maps (Google, Apple, Mapbox-based apps) are vector; embedded widgets, e-mail maps, print workflows and very-low-end device audiences are raster.

5 · Leaflet vs MapLibre vs OpenLayers

Leaflet (~42 KB)

The most popular map library on the web. DOM/Canvas based, tiny, thousands of plugins, wonderfully simple: one line adds a raster layer. It does not render vector tiles natively — with Leaflet, use our raster endpoints. Ideal for: content sites, dashboards, anything embedded.

MapLibre GL JS (~250 KB)

The open-source successor of Mapbox GL JS (forked when Mapbox closed its license in 2020). Renders vector tiles on the GPU via WebGL: continuous zoom, rotation, pitch, runtime styling, collision-aware labels. This is what "app-like" maps use. Ideal for: products, SaaS dashboards, anything interactive.

OpenLayers (~150 KB)

The veteran GIS workhorse. Reads practically every geo-format ever invented (WMS, WMTS, GeoJSON, KML, MVT…), strong in projections and scientific/enterprise GIS. Renders vector tiles on Canvas — functional, somewhat less silky than GL. Ideal for: GIS-heavy applications that already live in the OpenLayers ecosystem.

All three work with Tilezza — raster with any of them, vector best with MapLibre (see the MapLibre quickstart).

6 · Retina (@2x) explained

Modern phone screens pack 2–3 physical pixels into each CSS pixel. A normal 256 px tile gets stretched and looks slightly soft. A @2x tile is rendered at 512×512 for the same geographic square, so labels stay razor-sharp. The cost: roughly 3–4× the bytes and double the render work — which is why it counts 5 quota units and lives in the paid tiers. In Leaflet it is literally one option: detectRetina: true (example 5). Vector maps do not need any of this: they render at the screen's native resolution by definition — another point for vector on mobile.

7 · Caching — why the second visitor is free

Tile serving is a cache game. The pyramid is enormous, but visitors cluster on the same places: city centres at z10–16, whole countries at z6–8. Three cache layers work for you:

Browser cache — we send Cache-Control: public, max-age=604800; a returning visitor re-downloads nothing. Our disk cache — every rendered raster tile is kept ~30 days; measured effect: a cold render takes ~0.3–1 s, a cached hit 0.6 ms — a ~1000× difference. Your CDN (optional) — you may put your own CDN in front of Tilezza; its hits never reach us and cost you nothing (we count only requests that arrive at our servers — that is the metering rule, in writing).

8 · Styling and customization

Raster: choose among 11 pre-made styles (10 raster + the vector-only indigo). Vector: the same styles become starting points — override colors straight in the style URL, no build step:

https://tilezza.eu/api/style/voyager?key=YOUR_KEY
    &water=0088ff          ← any layer color (water, land, parks, forest,
    &boundaries=cc3355        roads, highway, buildings, boundaries, labels)
    &labels=333333
    &lang=de               ← label language
    &font=Montserrat       ← label typeface (self-hosted glyphs)
    &hide_pois=1 &lite=1   ← decluttering / mobile mode

Beyond the URL parameters, MapLibre lets you change anything at runtime (map.setPaintProperty('water','fill-color','#004')). This split is worth internalising: raster = pick a look, vector = own the look. Play with it live in example 3.

9 · The GDPR angle — why tile hosting matters

Every tile request carries the visitor's IP address and referer to whoever hosts the tiles. If your map loads tiles, fonts or the map library from US-controlled CDNs, your visitors' data crosses jurisdictions before your page even finishes loading — the exact pattern European DPAs sanctioned in the Google-Fonts rulings.

Tilezza's answer is boring on purpose: everything first-party, everything in the EU. Tiles, styles, font glyphs, sprites — and even the JS libraries — come from one EU-operated host. Our access logs are IP-free by design (we cannot leak what we never store), and there are no third-party requests to disclose in your privacy policy.

10 · Load Leaflet / MapLibre from Tilezza — our mini-CDN

The last stray third-party request on most map pages is the library itself (unpkg, jsDelivr, cdnjs — all US-controlled). Skip it: we serve pinned, immutable copies of both libraries. Copy-paste and your page makes zero non-EU requests:

<!-- Leaflet 1.9.4 (raster maps) -->
<link rel="stylesheet" href="https://tilezza.eu/lib/leaflet-1.9.4.css">
<script src="https://tilezza.eu/lib/leaflet-1.9.4.js"></script>

<!-- MapLibre GL 5.6.0 (vector maps) -->
<link rel="stylesheet" href="https://tilezza.eu/lib/maplibre-gl-5.6.0.css">
<script src="https://tilezza.eu/lib/maplibre-gl-5.6.0.js"></script>

Versioned filenames, long immutable caching, open CORS. The default Leaflet marker images are included (/lib/images/…). We add new versions under new names — your pinned URL never changes behaviour under you.

11 · So which should you use?

Your situationUse
"I want a map on my site with the least effort" (blog, contact page, WordPress)Raster + Leaflet — 5 lines, done
Product / dashboard with interaction, custom brand colorsVector + MapLibre + style API
Audience on very old / weak devicesRaster
Mostly mobile audience, sharpness mattersVector (native-resolution rendering)
Existing GIS stack, exotic projections, WMS neighboursOpenLayers (raster or vector)
Free tier, maximum valueVector — 1 unit per tile, all 11 styles, full zoom

Get a free API key   Open the live examples

FAQ

Why does my map load slowly the first time at high zoom?

You hit uncached raster tiles: the server renders each one (~0.3–1 s). The result is cached for weeks, so this is a first-visitor cost, concentrated in rarely-visited areas. Vector tiles do not have this problem.

Do vector tiles work without JavaScript?

No — a renderer (MapLibre/OpenLayers) must run in the browser. If you cannot add scripts, use raster tiles, which work as plain images.

Can I use Tilezza tiles in QGIS / mobile apps?

Yes. The endpoints are standard XYZ (raster) and TileJSON+MVT (vector) — QGIS, MapLibre Native (iOS/Android), and most SDKs accept them directly with your API key.

What counts against my quota?

Every tile request that reaches our servers, weighted by cost: vector 1, raster 3, retina 5 units. Your own CDN's cache hits are free. Full details on the pricing page — or watch it live on the usage meter.

Where does the map data come from?

OpenStreetMap contributors (ODbL license), packaged through the Protomaps pipeline, refreshed monthly. Attribution is embedded in every style — please keep it visible.