HomeAlgorithms & AIQuadtree

🌲 Quadtree

Interactive quadtree simulation: scatter points, watch recursive 2D subdivision, run range and nearest-neighbour queries, and compare collision broadphase vs O(n²).

Algorithms & AI3DModerate60 FPS
quadtree ↗ Open standalone

About Quadtree Spatial Index

A quadtree is a tree data structure in which each internal node subdivides its 2D region into exactly four equal quadrants (NW, NE, SW, SE), recursively until each leaf region contains at most a threshold number of points (commonly one). Invented by Raphael Finkel and J.L. Bentley in 1974 and popularised in computational geometry throughout the 1980s, quadtrees reduce spatial queries from O(n) linear scan time to O(log n + k) where k is the number of results returned. They are the standard acceleration structure for collision broadphase detection in 2D game engines, geographic information systems (GIS), and image compression (quadtree-coded images replace uniform regions with a single colour node).

This simulator lets you scatter random points or click to place them individually, then watches the tree subdivide in real time as the node-capacity threshold is exceeded. You can drag a range-query rectangle to see exactly which quadrants are entered and which are pruned, count nodes visited versus a naive linear scan, and observe how clustered versus uniform point distributions affect tree depth and query efficiency.

Frequently Asked Questions

What is the time complexity of a quadtree range query?

For a uniformly random point set of n points in a unit square, a range query returning k points visits O(√n + k) nodes in expectation — far better than the O(n) linear scan. In the worst case (highly degenerate point distributions or a query that covers many partially-overlapping nodes) the bound rises to O(n), but this is rare in practice. The O(√n) term comes from the number of quadtree cells that intersect the query boundary without being fully contained in it.

How does a quadtree speed up collision detection in games?

In a 2D physics engine, checking all pairs of objects for collision is O(n²) — infeasible for hundreds of objects. A quadtree broadphase works by inserting each object's bounding box into the tree, then for each object querying only the objects in the same or adjacent leaf cells. If objects are spread across the space, the average number of candidates per object drops to O(log n) or lower, reducing the total broadphase cost to O(n log n). Engines such as Box2D, Unity 2D, and LibGDX all use spatial trees (quadtrees or AABB trees) for this purpose.

What is the difference between a point quadtree and a PR (point-region) quadtree?

A point quadtree splits at the coordinates of the inserted point — the four children represent quadrants centred on that point. A PR (point-region) quadtree splits the space at its geometric midpoint regardless of where points are, giving a fixed hierarchical grid structure. PR quadtrees are more predictable in depth (always ⌈log₂(D/ε)⌉ for resolution ε in domain D) and are easier to implement without balancing. This simulator uses the PR variant because the fixed-midpoint subdivision makes the visual animation cleaner.

How deep can a quadtree grow?

The maximum depth of a PR quadtree is bounded by the resolution of the coordinate system. For 32-bit floating-point coordinates in a unit square, the minimum distinguishable distance is about 10⁻⁷, so the tree can reach at most ~23 levels before two "distinct" points occupy the same leaf. For integer coordinates in a 1024×1024 grid, the maximum depth is 10 (since 2¹⁰ = 1024). In practice, very deep trees only form when many points cluster in a tiny area; the average depth for a uniform point set is O(log n).

Can a quadtree handle dynamic point insertion and deletion?

Yes. Insertion traverses from the root to the appropriate leaf in O(depth) time, splitting the leaf if it exceeds capacity. Deletion removes the point and, if the parent's total point count falls below the merge threshold, collapses the four child nodes back into the parent leaf. Both operations are O(log n) average for uniformly distributed points. Frequent insertions and deletions in a clustered point set may require periodic rebuilding to prevent severe imbalance.

What is an octree and how is it related to a quadtree?

An octree is the 3D generalisation: each node subdivides its cube into eight equal sub-cubes (octants). Octrees are widely used in 3D game engines, ray tracing acceleration, and LiDAR point-cloud processing. The same algorithmic principles apply — range queries, nearest-neighbour search, and collision broadphase all benefit from the spatial hierarchy. In practice, 3D BVH (Bounding Volume Hierarchy) trees often outperform octrees for dynamic scenes because they adapt to point distribution rather than using fixed midpoint splits.

How is a quadtree used in image compression?

Quadtree image coding recursively divides an image into quadrants. If all pixels in a quadrant fall within a threshold of a single colour value, the quadrant is stored as a single leaf node with that colour — no per-pixel storage needed. Otherwise the quadrant is subdivided again. This produces a lossless (at threshold=0) or lossy (threshold>0) compression scheme. Fractal image compression (used in some early CD-ROM games) is a related technique. Modern codecs (HEVC, AV1) use quadtree-like coding unit (CU) hierarchies to partition frames into variable-size blocks for entropy coding.

What is the nearest-neighbour query algorithm on a quadtree?

Nearest-neighbour search starts at the root and descends into the child quadrant containing the query point, maintaining a "current best" candidate. On the way back up the recursion, each sibling quadrant is checked: if the minimum possible distance from the query to the quadrant (its closest corner distance) is less than the current best, the quadrant must be searched — otherwise it is pruned. In practice this visits O(log n) nodes for uniformly random data, though worst-case (adversarial point placement) is O(n).

How does a quadtree relate to a k-d tree?

A k-d tree (k-dimensional tree, Bentley 1975) is a binary space-partitioning tree that cycles through coordinate axes for its splits, choosing the median point along the current axis as the split value. For 2D data a k-d tree alternates between x-splits and y-splits. Unlike a PR quadtree, a k-d tree always balances perfectly (O(log n) depth for n points), but it has worse cache performance and is harder to update dynamically. Empirically, k-d trees outperform quadtrees for static point sets and lower dimensions; quadtrees are preferred for dynamic 2D data and collision broadphase.

What is the "Z-order curve" and how does it relate to quadtrees?

The Z-order (Morton) curve maps 2D coordinates to a 1D index by interleaving the bits of the x and y coordinates: for x = b₁b₂b₃ and y = c₁c₂c₃, the Morton code is b₁c₁b₂c₂b₃c₃. This linearisation preserves spatial locality: points close on the Z-order curve are spatially nearby in 2D. The Morton code of a point is exactly the path from root to leaf in a PR quadtree encoded as a binary string. Database systems (e.g., DynamoDB, Google S2) use Morton or Hilbert curves to index spatial data in 1D B-trees, achieving quadtree-equivalent query performance with standard index structures.

Can quadtrees represent non-point spatial data such as polygons?

Yes — the region quadtree stores which cells (pixels) are "inside" a polygon by subdividing until cells are entirely inside, entirely outside, or at the resolution limit (then stored as partially-covered). Vector quadtrees insert line segments or polygons by testing each level of subdivision for intersection. Storing large polygons naïvely causes duplication across many nodes; R-trees (Guttman, 1984) are generally preferred for rectangle and polygon indexing in GIS because they bound objects tightly and avoid redundant multi-node storage.

About this simulation

This simulation grows a point-region (PR) quadtree live as you scatter, drag or paint points onto a 2D canvas. Whenever a leaf cell holds more points than the current capacity, it splits into four equal quadrants — NW, NE, SW and SE — around its own midpoint, recursing until every cell is under capacity or has reached a fixed depth limit. Switching mode lets you drag a range-query rectangle, probe for a nearest neighbour, or set every point moving to watch the same tree drive collision broadphase, with live counters comparing nodes visited against a naive linear scan.

🔬 What it shows

Subdivision lines are drawn as cells split, points inside an active range-query box turn cyan, and the current nearest-neighbour match is highlighted in yellow with its search radius drawn as a circle. In Moving/collision mode, each point's neighbourhood is checked only against nearby cells rather than every other point, and the stats compare the candidate pairs the tree found against the pair count a naive all-pairs check would need.

🎮 How to use

The Capacity per leaf slider (1–16, default 4) sets how many points a cell holds before it splits; Animation speed (0.1×–3×) scales motion in Moving/collision mode. Six mode radios switch between add/drag, paint swarm, erase, range query, nearest neighbour and moving/collision; Scatter 200, Clear and Reset fill or empty the canvas, and the Show subdivision / Show points / Show query window checkboxes toggle what's drawn.

💡 Did you know?

PR quadtrees were introduced by Raphael Finkel and J. L. Bentley in 1974. Because this variant always splits at a cell's geometric midpoint rather than at a point's coordinates, its maximum depth is fixed by resolution alone — here capped at 10 levels in the code, so no cell can subdivide forever even if many points cluster into a tiny area.

Frequently asked questions

How does this quadtree decide when to split a cell?

Each cell starts as a single leaf holding all of its points. As soon as a leaf's point count exceeds the Capacity per leaf slider value, it splits into four equal-sized child quadrants at its own midpoint, redistributes its points into whichever child now contains them, and becomes an internal node. Splitting stops once a cell's depth passes 10, even if it still holds more points than its capacity.

What does raising or lowering the capacity per leaf slider change?

A low capacity forces cells to split much sooner, so the tree grows deeper with more leaves and internal nodes for the same point set — visible in the Leaves, Internal nodes and Max depth stats. A high capacity lets each leaf hold more points before splitting, producing a shallower tree that visits fewer nodes per query but scans more points inside each leaf.

How does range query mode compare to a linear scan?

Dragging a rectangle in Range query mode walks the tree from the root, only descending into child quadrants that intersect the rectangle and skipping the rest entirely. The Nodes visited stat counts exactly how many cells were checked this way, while Linear scan always shows the total point count — the gap between the two numbers is the saving the tree provides over checking every point individually.

How does nearest-neighbour search avoid checking every point?

Picking a target in Nearest neighbour mode searches the child quadrant containing the point first to get an initial current-best distance, then only re-checks sibling quadrants whose closest possible corner is nearer than that best distance — everything else is pruned. The yellow circle drawn around the query point marks the current best distance, and Nodes visited reports how many cells were actually examined.

What is happening in Moving/collision mode?

Every point is given a velocity and bounces off the canvas edges, and on each frame the quadtree is rebuilt and used to run a small range query around every point to find nearby collision candidates, rather than comparing it against every other point. The Query result and Nodes visited stats show the unique candidate pairs the quadtree found, while Linear scan shows the total pairs a naive all-pairs check would require.

⚙ Under the hood

A quadtree recursively subdivides the plane into four children. Watch it adapt to your point swarm and accelerate range, nearest-neighbour and collision queries — far fewer nodes visited than O(n).

Canvas 2DData StructureQuadtreeSpatial IndexRange Query

3D · Three.js / WebGL renderer · 60 FPS target · runs fully client-side, no install

What did you find?

Add reproduction steps (optional)