Flatbush: a static JavaScript index for points and rectangles
A very fast static spatial index for 2D points and rectangles in JavaScript 🌱
At a glance
- What is it?
- Flatbush builds a packed Hilbert R-tree for repeated 2D spatial queries, with a compact transferable index and no post-build updates. It suits map, visualization, and geometry workloads whose input set is complete before indexing.
- Who is it for?
- Flatbush is a practical choice for applications that know all points or rectangles before building an index, especially when queries run in browsers or workers and compact storage matters. It is the wrong choice for datasets that require insertion, deletion, or replacement after indexing.
- Can I use it commercially?
- Yes. ISC is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 7 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Star history
- mourner/flatbush1,607 ★ · +0
| Repository | 2026-10-16 | 2026-10-23 |
|---|---|---|
| mourner/flatbush | 1.6k | 1.6k |
0 Stars in the last 7 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Why Flatbush targets fixed 2D datasets
Flatbush addresses the need to search a large, unchanging collection of points or rectangles in JavaScript. The README lists maps, data visualizations, and computational geometry algorithms as relevant uses. Its defining constraint is equally important: the index is static, so items cannot be added or removed after `finish()` creates the tree. This is not a mutable spatial database. It is a specialized index for a batch that is already known.
That restriction permits Flatbush to store its packed Hilbert R-tree in one `ArrayBuffer`. The README describes faster indexing and search, along with lower memory use, when compared with RBush. It does not provide a universal workload threshold, so those are design claims rather than a guarantee for every dataset. The practical benefit is architectural: a fixed structure can be transferred to a worker, shared when configured for it, saved as a compact binary representation, or reconstructed without rebuilding from the original objects.
Flatbush returns indices rather than copies of the indexed geometry. Applications remain responsible for keeping those indices associated with their own data. This separation keeps the index compact, but query results must be mapped back to application records when those records are needed. The returned `minX`, `minY`, `maxX`, and `maxY` properties describe the index’s overall bounding box rather than the individual object properties supplied to `add()`.
How Flatbush constructs and queries its Hilbert R-tree
Flatbush implements the packed Hilbert R-tree algorithm. Construction is staged explicitly. An application creates the index with an expected item count, adds each rectangle through `index.add()`, and then calls `index.finish()`. The number of added items must match the count supplied to the constructor. This precondition makes complete construction part of the documented contract.
Each added item can be either a point or a rectangle. Supplying only `minX` and `minY` makes the maximum coordinates default to those values, creating a point. Supplying all four values stores a rectangle. `add()` returns a zero-based incremental identifier, and query results use those identifiers.
A bounding-box search returns items whose bounds intersect or touch the requested bounds. An optional filter can apply application-level conditions after candidate items are found. The callback receives the item index and its stored bounding-box coordinates. Returning a truthy value retains the item in the result.
The KNN method uses another spatial axis. `neighbors()` returns item indices ordered by distance from a supplied point. Its `maxResults` and `maxDistance` parameters both default to `Infinity`, although a caller can impose a finite result count or search radius. A filter passed to `neighbors()` selects potential results. The README distinguishes this behavior from `search()`, which also documents callback-based result handling.
Installing Flatbush and constructing a rectangle index
The README documents installation through NPM. Flatbush is an ES module, so application code imports the default export.
Use the documented NPM installation command:
npm install flatbushAfter installation, import the module as documented:
import Flatbush from 'flatbush';The README’s usage example initializes Flatbush for 1,000 items, adds their rectangle coordinates, and finishes the index. Constructor capacity, the number of additions, and the call to `finish()` must agree.
// initialize Flatbush for 1000 items
const index = new Flatbush(1000);
// fill it with 1000 rectangles
for (const p of items) {
index.add(p.minX, p.minY, p.maxX, p.maxY);
}
// perform the indexing
index.finish();The surrounding `items` collection and its coordinate properties are assumed by the README example. It then performs a bounding-box query and maps the returned indices back to the original objects.
// make a bounding box query
const found = index.search(minX, minY, maxX, maxY).map((i) => items[i]);The README also gives a direct KNN call for five results from the coordinates `10` and `10`.
const ids = index.neighbors(10, 10, 5); // returns 5 idsFiltering Flatbush search and neighbor results
Flatbush exposes application-defined filtering for both bounding-box searches and KNN queries, but their callback roles are not identical. For `search()`, the filter can decide whether a found item belongs in the result. The README also documents a separate form in which the same method invokes the callback to handle each result and provides its bounding-box coordinates.
This direct example filters records by a `foo` property:
const ids = index.search(10, 10, 20, 20, (i) => items[i].foo === 'bar');The callback receives the index returned by `add()`, not the original geometry object. Applications therefore need access to their own `items` collection when filtering needs properties that Flatbush does not store.
Flatbush also documents callback-based result handling for a bounding-box search:
index.search(10, 10, 20, 20, (i, x0, y0, x1, y1) => {
console.log(`Item found: ${items[i]}, bbox: ${x0} ${y0} ${x1} ${y1}`);
})By contrast, the `neighbors()` filter should only select potential KNN results. The README warns that it should not be used for result handling. This distinction matters because an application should not treat a neighbor filter as a separate result iterator.
Moving a Flatbush index between workers and storage boundaries
After indexing, Flatbush exposes the packed tree through `index.data`. Its type depends on the configured `ArrayBufferType`, which defaults to `ArrayBuffer`. The README gives a direct transfer from a worker to the main thread using the standard `postMessage` transfer list.
Use the transfer operation exactly as shown in the README:
// instantly transfer the index from a worker to the main thread
postMessage(index.data, [index.data]);The second array argument transfers ownership of the underlying buffer instead of cloning it. The receiving side reconstructs a Flatbush index from the received data.
// reconstruct the index from a raw array buffer
const index = Flatbush.from(e.data);The documented `from()` method accepts an `ArrayBuffer` or `SharedArrayBuffer` and an optional `byteOffset`. Flatbush exposes this representation for transferring or sharing an index between threads, or storing it in a file. The README does not document a file-format header or a file loading function, so `data` should not be described as a self-describing Flatbush file format.
Applications can pass `SharedArrayBuffer` as the `ArrayBufferType` when they want to share the index between `Worker`, `SharedWorker`, or `ServiceWorker` contexts. The README does not explain browser isolation requirements or a synchronization protocol for that configuration.
Flatbush constraints around updates, counts, and storage types
The clearest failure mode is attempting to treat Flatbush as a mutable index. Once the index is complete, items cannot be added or removed. An application receiving a continuous feed must either create another index for a new batch or choose an index designed for updates. The README does not provide an incremental update API, and it does not document the cost of rebuilding a large Flatbush index for every new batch.
A second constraint is constructor bookkeeping. The number of rectangles added must match the `numItems` passed to `new Flatbush()`. A mismatch violates the documented contract. Code that receives an uncertain batch size or omits an item needs to resolve that discrepancy before calling `finish()`.
Coordinate storage is another application decision. `Float64Array` is the default. The README says other types may be faster in certain cases and gives `Int32Array` as an example for integer data. It does not provide scaling helpers for converting arbitrary coordinate systems, so applications choosing another storage type remain responsible for coordinate compatibility.
Node size creates a separate tuning tradeoff. `nodeSize` defaults to 16. The README states that increasing it makes indexing faster and queries slower, while decreasing it has the reverse effect. Flatbush does not present one setting as universally best. Applications must decide whether construction speed or query speed matters more for their own workload.
Flatbush, RBush, and KDBush address different workloads
The README positions Flatbush as similar to RBush but distinguishes it through static operation, faster indexing and search, lower memory use, and a single-array-buffer representation. RBush is the relevant alternative in that comparison. Flatbush requires the full item count to be supplied and finalized before querying, whereas an application that edits indexed features after loading them needs an index that accepts those changes.
KDBush is described as a similar library for points. Flatbush accepts both points and rectangles, and its bounding-box search applies when items occupy an area rather than a single coordinate. A pure point workload may be better matched to KDBush. Flatbush still supports points because `add()` can omit maximum coordinates, but its rectangle query capability is broader.
The repository also lists ports for C++, Rust, C#, Python, and Dart. It links `geo-index` as a Rust port with Python bindings and states that it has ABI compatibility with the JavaScript library. The README does not document version parity, feature parity, or upgrade behavior for these implementations. Their existence does not establish that every method and storage option in the JavaScript package is available elsewhere.
For geographic coordinates, Flatbush points users to the `geoflatbush` extension. The README does not explain its API, so the base library should not be presented as including geographic-specific behavior.
Flatbush release activity and ISC licensing
The repository is not archived, and its last push was on 2026-10-16. Recent releases include v4.6.2 on 2026-06-09, v4.6.1 on 2026-06-09, and v4.6.0 on 2026-06-06. These dates document recent repository activity, but the README does not provide a release policy, compatibility matrix, migration guide, or long-term support schedule.
The package uses the ISC license. Its metadata marks the package as an ES module and publishes `index.js`, `index.d.ts`, and the generated `flatbush.js` browser bundle. The repository build script uses Rolldown to produce the UMD bundle, while the test script runs TypeScript checking and the Node test runner. These package scripts are repository details, not commands recommended for application deployment.
The runtime dependency list contains `flatqueue`. Development dependencies cover linting, TypeScript, and bundling. The README does not state a supported Node.js range or browser compatibility matrix. Upgrade planning therefore needs to account for API changes and package behavior without a documented compatibility table.
Editorial conclusion
Flatbush is a practical choice for applications that know all points or rectangles before building an index, especially when queries run in browsers or workers and compact storage matters. It is the wrong choice for datasets that require insertion, deletion, or replacement after indexing. Before adoption, verify that every item is supplied exactly once before `finish()`, confirm that `Float64Array` or `Int32Array` matches the coordinate domain, and measure the selected `nodeSize` against the application’s query pattern.
Frequently asked questions
Can Flatbush add or remove items after indexing?
No. Flatbush is a static spatial index, and the README explicitly says items cannot be added or removed after the initial indexing phase.
How do I add points or rectangles to Flatbush?
Call `index.add(minX, minY, maxX, maxY)` for rectangles. For points, call `index.add(minX, minY)`, which makes both maximum coordinates default to their corresponding minimum coordinates.
Can I transfer or store a Flatbush index as an ArrayBuffer?
Yes. After indexing, access `index.data`, transfer it with `postMessage`, store it as a compact binary representation, or reconstruct an instance with `Flatbush.from(data)`.
Does Flatbush support nearest-neighbor queries?
Yes. `index.neighbors(x, y, maxResults, maxDistance, filterFn)` returns item indices ordered by distance from the supplied point. `maxResults` and `maxDistance` default to `Infinity`.
What is the difference between Flatbush and KDBush?
The README describes KDBush as a similar library for points, while Flatbush indexes points and rectangles. Flatbush also provides bounding-box search and stores its static index in an `ArrayBuffer`.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mourner-flatbush)Star history chart for your README
[](https://hysenlabs.com/projects/mourner-flatbush)Was this useful?