Supercluster
A blazing-fast geospatial point-clustering library for grouping map markers by zoom level.
Repository Health
Technical Analysis
Supercluster is a JavaScript library that clusters large sets of geographic points into hierarchical groups so maps stay readable and performant at every zoom level. Built by Mapbox to power clustering in Mapbox GL JS, it indexes points into a KD-tree per zoom level and can group millions of points in a few hundred milliseconds.
Rather than clustering on the fly for each render, Supercluster precomputes a full cluster hierarchy across all zoom levels during load(), so subsequent getClusters() calls for a given viewport and zoom are near-instant. It supports custom property aggregation via map/reduce functions, vector-tile-compatible tile output, and cluster expansion/drill-down APIs for building interactive “click to zoom” cluster UIs.
What You Get
- A single default export (the
Superclusterclass) with a minimal, well-documented method surface:load,getClusters,getTile,getChildren,getLeaves,getClusterExpansionZoom. - Precomputed cluster hierarchies across every zoom level, built once on
load()for near-instantgetClusters()queries afterward. - Vector-tile-compatible
getTile()output for integrating directly with tile-based map rendering pipelines like Mapbox GL JS. - Custom property aggregation via
map/reduceoptions, so clusters can carry summed, averaged, or otherwise combined properties from their constituent points.
Common Use Cases
- Rendering large marker datasets (thousands to millions of points) on interactive web maps without overwhelming the browser.
- Building ‘click to zoom’ cluster interactions in Leaflet, Mapbox GL JS, or Google Maps integrations.
- Powering server-side vector tile generation where cluster tiles need to be computed per zoom/x/y.
- Aggregating point data (e.g., counts, sums) into cluster-level summary statistics for dashboards and map-based data visualizations.
Under The Hood
Architecture Supercluster ships as a single default-exported Supercluster class in index.js. On load(points), every GeoJSON point is projected into spherical-mercator space and flattened into a numeric array (data) with a fixed stride (6 fields, or 7 when a reduce option is set), storing coordinates, the zoom the point was last processed at, its source index, its parent cluster id, and its point count. This flat array is indexed into a KDBush KD-tree per zoom level. _cluster(tree, zoom) walks from maxZoom down to minZoom, using tree.within() to find neighbors inside the pixel radius and greedily merging them into weighted-centroid clusters, encoding each new cluster id by bit-shifting the origin point’s index and zoom into a single integer. Queries (getClusters, getTile, getChildren, getLeaves) then do range/within lookups against the precomputed tree for the requested zoom, so the expensive clustering work happens once at load() time and reads are O(log n).
Tech Stack The library is plain ES-module JavaScript ("type": "module") with a single runtime dependency, kdbush (a KD-tree implementation by the same author, Volodymyr Agafonkin/mourner). It has no framework dependency and no TypeScript source — type definitions are published separately via @types/supercluster on DefinitelyTyped. The build pipeline uses Rollup (rollup.config.js) to produce browser-ready UMD/minified bundles in dist/, and linting runs through ESLint with the author’s eslint-config-mourner shared config.
Code Quality Tests live in test/test.js (181 lines) and run via Node’s built-in node --test runner against real-world fixture data (test/fixtures/places.json, a set of world city/place points), asserting exact tile output, children/leaves traversal, minPoints, generateId, and null-property edge cases through deep-equality checks rather than mocks. The core algorithm favors flat typed-array-style data structures and bit-packed integer ids over object allocation, trading some readability for clustering millions of points quickly; naming is terse but consistent with the surrounding geospatial-tiling ecosystem (geojson-vt, kdbush) it was designed to interoperate with.
API Design The public surface is deliberately small: construct with an options object, call load(points) once, then query with getClusters(bbox, zoom) or getTile(z, x, y). Method and option names track familiar GeoJSON/slippy-map conventions (bbox, zoom, tile z/x/y), and drill-down helpers (getChildren, getLeaves, getClusterExpansionZoom) are named to make interactive cluster-expansion UIs straightforward to build with minimal boilerplate — the README’s entire quick-start is three lines of code.