kdbush freezes a point set into a single transferable buffer
A fast static index for 2D points
At a glance
- What is it?
- kdbush is a JavaScript KD-tree index for 2D points that is built once, queried by box or radius, and shipped between threads as a raw array buffer. It buys that speed by dropping rectangle support and refusing in-place updates.
- Who is it for?
- kdbush suits teams that load a large point set once and then run repeated box or radius queries, especially where the index must be moved to a worker thread. It does not suit workloads with changing geometry, rectangle queries, or a need for incremental deletion.
- 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 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The index is built once and then frozen
kdbush is a JavaScript library that indexes a fixed set of two-dimensional points and answers spatial questions about them. The lifecycle is deliberately narrow. You create the index with the number of points you expect, push coordinates in one call at a time, then trigger the build with a single finish call. After that the structure is closed for business. The README states that the index is static and that you cannot add or remove items after initial indexing, and the public API confirms it: there is an add method, a finish method, and no update, remove or delete counterpart.
That single constraint shapes everything else in the library, from the flat buffer layout down to the absence of convenience wrappers. If your coordinates change every frame, kdbush is the wrong shape of tool, and you would pay for a full rebuild on each batch of changes. If the point set is fixed at load time, the same constraint is what lets the tree be stored compactly and searched quickly. The design is a bet on immutability, and the whole library is built to win it.
Points only, so the rectangle case belongs to another library
Every entry in a kdbush tree is a point, never a rectangle. The README contrasts kdbush with RBush, which indexes arbitrary rectangles, and points readers who need a static index of rectangles to Flatbush instead. The trade is arithmetic rather than philosophical. Because a node stores a single coordinate pair, there is no stored rectangle to test against a query box, and the README states that when indexing points, KDBush takes roughly half the memory that Flatbush does.
The cost lands on the caller. A kdbush index cannot answer which items overlap a given rectangle, because it has no notion of extent. A rectangle query has to be approximated by querying the point's own location, and if you need everything inside a shape rather than near a location, this library cannot serve you directly.
The whole tree travels as one array buffer
The property that makes kdbush different from a typical in-memory tree is data. The index is stored as a single array buffer exposed on the instance as data, and the README highlights that it can be transferred between threads or stored as a compact file. Transferring a buffer is a cheap ownership move rather than a copy, so a worker can build the index and hand it to the main thread without a serialization step.
Reconstructing the index later goes through a static from method that accepts raw ArrayBuffer or SharedArrayBuffer data. If several workers need to read the same index at once, the constructor takes an ArrayBufferType argument, and the README suggests SharedArrayBuffer for that case across multiple Worker, SharedWorker or ServiceWorker instances. The buffer is the interface, not an implementation detail.
range and within are the entire query surface
Two query shapes are exposed, and they cover the common cases. The range method takes a bounding box as four coordinates and returns the indices of every item inside it. The within method takes a query point and a radius and returns the indices of every item within that distance. Both return indices rather than items, which keeps the library free of any assumption about what a point belongs to. You hold the original array and map the returned indices back to your own objects, exactly as the README example does.
There is also a withinInto method. It behaves like within but writes matches into a container you provide through indexed assignment, and it returns the number of matches rather than an array. The README describes it as accepting any indexed-writable container, including a typed array pre-sized to the expected upper bound for an allocation-free path, or a plain array that grows as needed. For tight loops that run the same radius query thousands of times, that variant avoids per-call array churn.
Installing kdbush and indexing a first point set
The README gives the install as npm install kdbush, followed by an ES module import. The package entry point is index.js and the package is marked as an ES module, so the import is the normal path in a modern build. The same import line appears in the usage example:
import KDBush from 'kdbush';A browser page can skip the package manager and load the ES module build straight from jsDelivr:
<script type="module">
import KDBush from 'https://cdn.jsdelivr.net/npm/kdbush/+esm';
</script>If a global variable is more convenient, the README also points at a browser bundle:
<script src="https://cdn.jsdelivr.net/npm/kdbush"></script>With the import in place, indexing a point set follows the pattern the README gives. Create the index with the expected item count, add each coordinate pair, and call finish to perform the indexing:
// initialize KDBush for 1000 items
const index = new KDBush(1000);
// fill it with 1000 points
for (const {x, y} of items) {
index.add(x, y);
}
// perform the indexing
index.finish();The add method returns a zero-based incremental number for each point, and that number is the identifier the queries hand back. The README example then maps identifiers to the original items with a lookup, and it performs a radius query and a transfer of the raw buffer:
const foundIds = index.range(minX, minY, maxX, maxY);const neighborIds = index.within(x, y, 5);postMessage(index.data, [index.data]);nodeSize and Int32Array decide how the tree behaves
Two constructor options have a direct effect on performance, and the README explains both. The first is nodeSize, the number of items in a KD-tree node, which defaults to 64. A larger node means faster indexing and slower search, and the reverse holds for a smaller node, so the default is a middle position rather than a fixed answer. Teams that index once and query heavily will feel the search side more, and teams that rebuild often will feel the indexing side.
The second is ArrayType, the array type used to store coordinate values, which defaults to Float64Array. The README notes that if your coordinates are integer values, passing Int32Array makes the index faster and smaller. A third argument, ArrayBufferType, controls the buffer class and defaults to ArrayBuffer, with SharedArrayBuffer available when the index is shared between threads.
Version history and the cost of upgrading
The release history is short and spaced out. The current version is v4.1.0, released on 2026-05-19. Before that, v4.0.2 shipped on 2023-04-27 and v4.0.1 on 2023-04-20, so the two most recent version bumps are separated by a gap of more than three years. The last push to the repository was on 2026-10-16, which is a recent change to the working tree even though the published version is several months old. The package does not carry a fast moving release cadence, and the 4.0.x pair landing two days apart in April 2023 suggests patch fixes rather than a schedule.
The licence is ISC, which is permissive and carries no copyleft obligations on code that links the library. The package publishes four files, including a type declaration at index.d.ts, so TypeScript consumers get declarations from the same install.
What the README leaves out
Several things a buyer would want to check are simply not written down. The README does not document rollback or a migration path between the v3 line and v4, so an upgrade from an older major version has no written procedure. It does not describe the internal buffer layout, which means the raw bytes in index.data are safe to move around but not something to parse yourself, and the only supported way back to a working index is the from method. No CLI, no server mode, and no framework integration are offered; the library is an import and nothing more. The repository does contain a bench.js script alongside test.js, and the package.json scripts expose a bench entry, so a benchmark harness is available to run locally, although no results are published in the README. The project is a data structure with a small, sharp surface, and it expects the caller to own everything around it.
Editorial conclusion
kdbush suits teams that load a large point set once and then run repeated box or radius queries, especially where the index must be moved to a worker thread. It does not suit workloads with changing geometry, rectangle queries, or a need for incremental deletion. Before adopting it, confirm that your coordinate values are stable after the finish call, that a rebuild is affordable whenever the data changes, and that your query shapes are limited to ranges and radii.
Frequently asked questions
Can I add or remove points in an existing kdbush index?
No. The index is static, so points are added with index.add before index.finish and the README states that you cannot add or remove items after initial indexing. Any change to the data means building a new index.
How do I install kdbush and use it in a browser?
The README gives the install as npm install kdbush followed by an ES module import of kdbush. In a browser you can instead import the ES module build from jsDelivr, or load the browser bundle with a script tag, which exposes a KDBush global variable.
What is the difference between kdbush and Flatbush?
KDBush indexes points only, while Flatbush is the option for a static index of rectangles. The README states that when indexing points, KDBush has the advantage of taking roughly twice less memory than Flatbush.
How do I share a kdbush index between threads?
Send the raw buffer with postMessage(index.data, [index.data]) and rebuild on the other side with KDBush.from. If several workers must read the same index, construct it with a SharedArrayBuffer buffer type.
Does kdbush work with integer coordinates?
Yes. The constructor accepts an ArrayType argument, and the README says that if your coordinates are integer values, Int32Array makes the index faster and smaller than the default Float64Array.
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-kdbush)Was this useful?