Algorithms
Boids: How Three Rules Make a Flock
Boids is a 1986 agent-based model by Craig Reynolds in which each simulated bird follows three simple local steering rules — separation, alignment, and cohesion — and coordinated flocking emerges with no leader, no global plan, and no central controller. It is the canonical example of emergence and the ancestor of nearly every crowd, swarm, and flocking system in games and film.- Invented1986, Craig Reynolds
- RulesSeparation, Alignment, Cohesion
- Naive timeΘ(n²) per frame
- With spatial index≈ Θ(n·k) per frame
- SpaceΘ(n)
- ControlFully decentralized (no leader)
Interactive visualization
Press play, or step through manually. The visualization is yours to drive — try it before reading on.
Watch the 60-second explainer
A condensed visual walkthrough — narrated, captioned, under a minute.
The three rules — and why local is enough
Each boid is an autonomous agent with a position and a velocity. On every frame it looks only at its neighbors — the boids within a perception radius (and, in Reynolds' original, within a viewing angle, so a boid ignores what is directly behind it). From those neighbors it computes three steering vectors:
- Separation: steer away from neighbors that are too close, to avoid collisions. The steering vector points along the sum of directions away from each crowding neighbor, typically weighted by inverse distance so nearer neighbors push harder.
- Alignment: steer toward the average heading (mean velocity) of neighbors, so the boid matches the local flow.
- Cohesion: steer toward the average position (centroid) of neighbors, so the group stays together.
The three vectors are scaled by weights and summed into an acceleration. Crucially, no boid ever consults a global variable: the flock's shape, direction, and splits/merges are never programmed — they are a consequence of thousands of purely local decisions. That is the whole point of the model and the reason it is the textbook illustration of emergence.
The update loop, precisely
Boids is a discrete-time simulation. Each frame integrates every agent forward by one timestep dt. The canonical per-boid update is:
for each boid b: N = neighbors(b, radius) // boids within perception range sep = -Σ(pos[j]-pos[b]) / |pos[j]-pos[b]|² // push away, j∈N ali = avg(vel[j] for j in N) - vel[b] coh = avg(pos[j] for j in N) - pos[b] acc = w_s·norm(sep) + w_a·norm(ali) + w_c·norm(coh) vel[b] = clampSpeed(vel[b] + acc·dt, vmax) pos[b] = pos[b] + vel[b]·dt
A vital implementation detail is the double buffer invariant: all three steering vectors for frame t must be computed from the same snapshot of positions/velocities at t, then written to a separate buffer for t+1. If you update boids in place, early boids in the loop influence later ones within the same frame, breaking the symmetry and injecting a scan-order bias that visibly warps the flock.
Why it costs Θ(n²) — and how to fix it
The expensive part is neighbors(b, radius). Naively, each boid tests distance against every other boid, so a frame does n×(n−1) distance checks: Θ(n²) time, Θ(n) space. This is fine for a few hundred boids but quadratic growth bites hard — going from 1,000 to 10,000 boids is a 100× jump in work per frame, dropping a real-time simulation well below 60 FPS.
The standard remedy is a spatial index. Because the perception radius is small relative to the world, almost every distance test fails; you only need boids in nearby space:
- Uniform grid: bucket boids into cells of side ≈ perception radius; query the 3×3 (2D) or 3×3×3 (3D) neighborhood of cells. Build is Θ(n), query is Θ(k) where k is the average neighbor count. Best when boids are roughly uniformly dense.
- k-d tree or quadtree/octree: adapts to clumpy distributions; radius/kNN queries run in ≈ Θ(log n + k) each.
Either way the frame drops to ≈ Θ(n·k). Since k is bounded by the perception radius and not by n, this is effectively linear in the flock size — the difference between simulating hundreds and simulating tens of thousands of agents in real time.
A worked micro-example
Take three 2D boids with weights w_s = w_a = w_c = 1 and a large radius so all are neighbors. Focus on boid A at position (0, 0) with velocity (1, 0):
- B at (2, 0), velocity (0, 1); C at (0, 2), velocity (0, −1).
- Cohesion: neighbor centroid = ((2+0)/2, (0+2)/2) = (1, 1); steer = (1, 1) − (0,0) =
(1, 1)— A is pulled toward the middle of B and C. - Alignment: avg neighbor velocity = ((0+0)/2, (1−1)/2) = (0, 0); steer = (0,0) − (1,0) =
(−1, 0)— the opposing headings cancel, gently slowing A's rightward drift. - Separation: both neighbors are 2 units away; inverse-square pushes A back toward (−0.25, −0.25)-ish — small because they aren't crowding yet.
Summed and clamped, A's next velocity turns up-and-right toward the group while easing off. Repeat for B and C from the same snapshot, integrate, and the trio converges into a coherent unit within a few frames — flocking from arithmetic no single agent could have planned.
Variants, tuning, and trade-offs
Boids is really a framework of steering behaviors, and real systems layer more forces onto the core three: goal-seeking (fly toward a target), obstacle avoidance, wind, predator-fleeing, and world-boundary wrapping or turning. Reynolds later generalized this into a whole vocabulary of steering behaviors (pursue, evade, wander, path-follow, arrival).
- Weight tuning is the entire art. Too much cohesion collapses the flock into a jittering ball; too much separation shatters it into a diffuse gas; too much alignment freezes everyone onto one heading like a marching grid. The relative weights and the perception radius (often separation uses a smaller radius than cohesion/alignment) control whether you get a starling murmuration or a school of fish.
- Vicsek model is the physicists' minimal cousin: alignment only, plus noise. It exhibits a sharp order/disorder phase transition and is used to study collective motion analytically, but it does not aim to look like real animals.
- Versus centralized clustering (e.g. k-means): boids never assigns agents to groups; groups form, split, and merge dynamically. If you want animation, use boids; if you want a one-shot partition of static data, use clustering.
Reynolds' 1987 SIGGRAPH paper is one of the most cited in computer graphics, and Reynolds won a 1998 Scientific and Engineering Academy Award for his contributions to computer animation for film. Boids first appeared on screen in Reynolds' own short Stanley and Stella in: Breaking the Ice (1987), and reached wide audiences a few years later in the computer-generated bat swarms of Batman Returns (1992).
Pitfalls and common misconceptions
- "There's a leader." There is not. Watching a flock bank in unison, people assume a lead bird or a shared target. Every boid runs the identical local rule; leadership is an illusion produced by fast local propagation of turns.
- In-place update bug. Updating positions during the same loop that reads them (single buffer) is the most common correctness error. It usually still "looks like flocking," which is why it survives review — but it introduces a subtle directional bias tied to iteration order.
- Divide-by-zero / self-inclusion. Separation weighted by inverse distance explodes when two boids coincide; clamp the minimum distance. And a boid must exclude itself from its neighbor set, or cohesion and alignment are silently pulled toward its own state.
- Speed clamping is not optional. Without a max-speed clamp (and often a min speed so birds don't stall), accelerations compound and boids rocket off to infinity. The clamp is what keeps the system in a stable, visually plausible regime rather than blowing up numerically.
- Radius vs. angle. Omitting the field-of-view constraint is a fine simplification, but it changes the aesthetics: real animals don't react to what's directly behind them, and including the blind spot yields more natural turning waves.
| Approach | Coordination | Per-step cost | Produces |
|---|---|---|---|
| Boids (naive) | Local, decentralized | Θ(n²) | Emergent flocking |
| Boids + k-d tree / grid | Local, decentralized | ≈ Θ(n·k) | Same flocking, scalable |
| Cellular automaton (Game of Life) | Fixed local rules on a grid | Θ(cells) | Emergent patterns, no continuous motion |
| K-means clustering | Global, centralized | Θ(n·k·i) | Static cluster assignment |
| Vicsek model (physics) | Local, decentralized | Θ(n²) or Θ(n·k) | Alignment-only phase transition |
Frequently asked questions
What are the three rules of boids?
Separation (steer away from crowding neighbors to avoid collisions), alignment (steer toward the average heading of nearby boids), and cohesion (steer toward the average position of nearby boids). Each is a purely local vector; their weighted sum is the boid's steering, and coordinated flocking emerges from applying all three every frame.
Who invented boids and when?
Craig Reynolds created boids in 1986 and published it in the 1987 SIGGRAPH paper "Flocks, Herds, and Schools: A Distributed Behavioral Model." The name is a contraction of "bird-oid object." It became one of the most cited papers in computer graphics and earned Reynolds a 1998 Academy technical award.
Why does flocking emerge without a leader?
Because a change in one boid's heading (e.g. avoiding an obstacle) alters its neighbors' alignment and cohesion targets, which alters their neighbors', and so on. Turns ripple outward faster than the flock moves. No global coordination exists — the collective shape is an emergent property of many identical local rules interacting.
What is the time complexity of boids?
The naive neighbor search is Θ(n²) per frame because each boid checks distance to every other. Adding a spatial index — a uniform grid, k-d tree, quadtree, or octree — drops this to roughly Θ(n·k), where k is the average neighbor count bounded by the perception radius, making it effectively linear in flock size.
How do you keep a flock from collapsing or exploding?
Tune the relative weights and radii of the three rules, and always clamp velocity to a maximum (and often a minimum) speed. Too much cohesion collapses the flock into a jittering point; too much separation disperses it; without a speed clamp, accumulating acceleration sends boids off to infinity.
How is boids different from k-means clustering?
K-means is a centralized, one-shot algorithm that partitions static points into k fixed clusters by minimizing distance to centroids. Boids is a decentralized, continuous simulation where groups form, split, and merge over time with no assignment step. Use k-means to label data; use boids to animate collective motion.