What a flock doesn't know

Mar 4, 2026 · Microblog #10
simulation emergent behavior artificial life

120 boids, three rules, no flock-level state. Click to reset; move mouse to pull the swarm.

Craig Reynolds conceived the boids algorithm around 1975, sat on it for a decade, then built it in a month at Symbolics Inc. while preparing SIGGRAPH slides. He published it in 1987. The paper's central claim was provocation: you don't need a conductor. Flocking is what happens when every bird runs the same three cheap comparisons against its neighbors — stay separated, match their heading, drift toward their center — and no bird ever looks at the flock as a whole.

The key move in the implementation is the Reynolds steering formula. For each of the three forces, you compute a desired velocity (normalized, scaled to maxSpeed), subtract the boid's current velocity to get a steering delta, then clamp the magnitude to maxForce. This caps how hard any single rule can yank the agent in one frame. The three clamped deltas sum into one force vector, which gets added to velocity. Velocity then gets clamped to maxSpeed:

// Separation — neighbors within r=18, push away
if (sa) {
  sx /= sa; sy /= sa;
  const m = Math.hypot(sx, sy) || 1;
  sx = (sx / m) * maxSpeed - b.vx;
  sy = (sy / m) * maxSpeed - b.vy;
  const sm = Math.hypot(sx, sy) || 1;
  if (sm > maxForce) { sx = (sx / sm) * maxForce; sy = (sy / sm) * maxForce; }
  fx += sx * 1.35; fy += sy * 1.35;
}
// Alignment — average velocity of neighbors within r=42
if (aa) {
  ax /= aa; ay /= aa;
  const m = Math.hypot(ax, ay) || 1;
  ax = (ax / m) * maxSpeed - b.vx;
  ay = (ay / m) * maxSpeed - b.vy;
  const am = Math.hypot(ax, ay) || 1;
  if (am > maxForce) { ax = (ax / am) * maxForce; ay = (ay / am) * maxForce; }
  fx += ax * 0.85; fy += ay * 0.85;
}
// Cohesion — center of mass of neighbors within r=52
if (ca) {
  cx = cx / ca - b.x; cy = cy / ca - b.y;
  const m = Math.hypot(cx, cy) || 1;
  cx = (cx / m) * maxSpeed - b.vx;
  cy = (cy / m) * maxSpeed - b.vy;
  const cm = Math.hypot(cx, cy) || 1;
  if (cm > maxForce) { cx = (cx / cm) * maxForce; cy = (cy / cm) * maxForce; }
  fx += cx * 0.65; fy += cy * 0.65;
}

The radii matter more than the weights. Separation runs at 18 pixels, alignment at 42, cohesion at 52. So a boid first checks: is anyone dangerously close? Then: who's steering nearby? Then: where's the local crowd center? Each question answers at a different spatial scale, and the nesting of those scales is what produces the stable sub-flocks and the fluid merges between them.

Reynolds' first animation ran on a Symbolics Lisp Machine. By 1992 the algorithm was animating bat swarms and penguin armies in Batman Returns. The flock still doesn't know it's a flock. That's the whole point.

← Back to showcase