Cell & Shape

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.

LevelAvg 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);

Frequently asked questions

What is an S2 cell token?
A token is the short, human-friendly hexadecimal string form of an S2 cell ID — for example "808f7e3" for a level-12 cell over San Francisco. It is the 64-bit cell ID written in hex with trailing zero digits stripped, so higher levels give longer tokens. Tokens are the safest way to move S2 cells between systems because, unlike the raw numeric ID, they survive JSON and JavaScript without losing precision.
How big is a level-N S2 cell?
Roughly: level 8 averages about 36 km per side, level 10 about 9 km, level 12 about 2 km, level 14 about 560 m, level 16 about 140 m, level 20 about 9 m, and level 30 is millimetre scale. These are approximate averages — because S2 projects a sphere onto six cube faces, a cell near a face centre is noticeably smaller than one near a face edge at the same level.
S2 vs H3 — which should I use?
S2 uses a hierarchy of squares mapped through a Hilbert curve and integer IDs, which makes range queries and quadtree-style parent/child math cheap; it powers Google and Uber-scale geo systems. H3 uses hexagons, which give uniform neighbour distances and are nicer for movement, flow, and coverage analytics. Choose S2 for indexing and range scans, H3 for spatial aggregation where neighbour symmetry matters.
Why do S2 cell IDs break in JavaScript?
S2 IDs are unsigned 64-bit integers, but a JavaScript number is a 64-bit float that can only represent integers exactly up to 2^53. Any S2 ID larger than that (most of them) silently loses its low bits, producing a different — often invalid — cell. Carry IDs as BigInt or as the hex token string, and never parse one with parseInt or JSON.parse into a plain number.
How do I turn an address into an S2 cell?
Use the search box above to jump the map to a place, then pick a level and tap the map: the viewer reports the S2 cell under that point with its token and ID. In code, geocode the address to a latitude/longitude, then call your S2 library to get the leaf cell and take its parent at the level you want.