S2 Cell Viewer — inspect S2 cells, tokens & levels on a map
Explore Google's S2 geometry directly on the map. Pan and zoom to draw the S2 grid at any level from 0 to 30, tap a cell to read its token and 64-bit ID, and share the exact view with a link. Everything runs in your browser — no coordinates are uploaded.
What the S2 grid actually is
S2 is a hierarchical spatial index that wraps the sphere onto the six faces of a cube, then walks each face with a Hilbert space-filling curve. That curve is the trick that makes S2 useful: points close together on the ground almost always end up close together along the one-dimensional curve, so a two-dimensional region maps to a small number of contiguous integer ranges. Databases love that, which is why S2 underpins Google- and Uber-scale geospatial systems.
Each face is recursively divided into four quadrants, 31 levels deep (0–30). Level 0 is a whole cube face; level 30 is a roughly centimetre-scale leaf cell. Every cell has exactly four children and one parent, so zooming between levels is pure integer arithmetic — no re-projection, no lookups.
Tokens vs. 64-bit IDs (and the BigInt trap)
Every S2 cell has a canonical unsigned 64-bit integer ID. The top 3 bits pick the cube face, the middle bits are the interleaved Hilbert position, and a single trailing 1-bit marks the level. The same value written as stripped hexadecimal is the token — shorter, URL-safe, and what you should pass around.
The classic bug: an S2 ID routinely exceeds 253, but a JavaScript
number is a double that only holds integers exactly up to 253. Parse
a cell ID into a plain number and its low bits vanish, turning it into a different
cell. Keep IDs as BigInt or as the hex token string end to end — never
parseInt, never JSON.parse into a number.
S2 level → approximate cell size
S2 cells are not all the same size at a given level: the cube-to-sphere projection stretches cells near face edges and shrinks them near face centres. The figures below are approximate averages from the published S2 cell-statistics table — good for picking a level, not for exact area math.
| Level | Avg edge (approx) | Avg area (approx) | Rough real-world scale |
|---|---|---|---|
| 8 | ~36 km | ~1,300 km² | a large metro area |
| 10 | ~9 km | ~81 km² | a town / district |
| 12 | ~2 km | ~5 km² | a neighbourhood |
| 14 | ~560 m | ~0.3 km² | a few city blocks |
| 16 | ~140 m | ~0.02 km² | a building cluster |
| 20 | ~9 m | ~77 m² | a room / small yard |
| 30 | ~9 mm | ~0.7 cm² | a fingernail (leaf cell) |
Common S2 tasks
The two operations you reach for most are point → cell (which S2 cell
contains this coordinate?) and token → polygon (draw a cell's boundary).
Tap the map above to do the first interactively. For the second, note that the
universal geometry inspector accepts S2 tokens directly — paste a token like
808f7e3 and it converts the cell to a polygon you can view or re-export as WKT
or GeoJSON.
Point → cell and token round-trip in JavaScript (s2js)
import { s2 } from 's2js';
const { LatLng, cellid } = s2;
// point → leaf cell → level-12 parent
const leaf = cellid.fromLatLng(LatLng.fromDegrees(37.77, -122.42));
const cell = cellid.parent(leaf, 12);
// IDs are BigInt — keep them that way
console.log(typeof cell); // "bigint"
const token = cellid.toToken(cell); // "808f7e3"
// token → cell (round-trips exactly; a plain number would not)
const back = cellid.fromToken(token);