Boids
GPU flocking simulation
Quick Start
Loading...
Source
Loading...
Documentation
Boids
GPU-accelerated flocking simulation using Reynolds' boids algorithm. A compute shader updates positions and velocities each frame — you handle rendering however you want.
Usage
import { createBoids } from './boids';
const flock = createBoids(device, {
count: 2048,
bounds: { width: 100, height: 100 }
});
// Per frame
flock.update(deltaTime);
// Read positions/velocities for rendering
const positionBuffer = flock.positions; // GPUBuffer of vec2<f32>
const velocityBuffer = flock.velocities; // GPUBuffer of vec2<f32>
// Clean up when done
flock.destroy();
Rendering
The position and velocity buffers have VERTEX usage, so you can bind them directly in a render pipeline:
passEncoder.setVertexBuffer(0, flock.positions);
passEncoder.draw(6, flock.count); // instanced quads, triangles, etc.
Or use them as storage buffers in a custom compute/render shader.
API
createBoids(device, options?)
Returns a Boids instance. Boids are initialized with random positions within bounds and random velocity directions.
| Option | Type | Default | Description |
|---|---|---|---|
count |
number |
2048 |
Number of boids |
bounds |
{ width: number, height: number } |
100x100 |
Simulation area (centered at origin) |
separationWeight |
number |
1.5 |
Separation force multiplier |
alignmentWeight |
number |
1.0 |
Alignment force multiplier |
cohesionWeight |
number |
1.0 |
Cohesion force multiplier |
separationRadius |
number |
3.0 |
Distance threshold for separation |
neighborRadius |
number |
6.0 |
Distance threshold for alignment/cohesion |
maxSpeed |
number |
10.0 |
Maximum boid speed |
maxForce |
number |
5.0 |
Maximum steering force per frame |
flock.update(deltaTime)
Dispatches the compute shader to advance the simulation by deltaTime seconds.
flock.positions
GPUBuffer containing count vec2<f32> values (current positions). Usages: STORAGE | COPY_DST | VERTEX.
flock.velocities
GPUBuffer containing count vec2<f32> values (current velocities). Usages: STORAGE | COPY_DST | VERTEX.
flock.count
Number of boids in the simulation.
flock.destroy()
Releases all GPU buffers (both ping-pong pairs and uniforms).
Algorithm
The simulation implements Craig Reynolds' three classic flocking rules, evaluated per-boid on the GPU:
Separation — Steer away from nearby boids to avoid crowding. Each boid accumulates repulsion vectors from all boids within separationRadius, weighted inversely by distance (closer boids push harder). The result is normalized and scaled to produce a steering force.
Alignment — Match the heading of nearby boids. Each boid averages the velocity of all neighbors within neighborRadius, then steers toward that average direction.
Cohesion — Steer toward the center of mass of nearby boids. Each boid averages the position of all neighbors within neighborRadius, then steers toward that centroid.
The three forces are weighted, summed, and applied as acceleration. Velocity is clamped to maxSpeed. Boundaries wrap toroidally (boids exiting one side appear on the opposite side).
Neighbor search is brute-force O(n^2) — each boid reads all other boids. This is simple and works well for up to ~4K boids on modern GPUs. For larger counts, you'd add a spatial hash (see Modifying section below).
WGSL loading
The default import uses Vite's ?raw suffix:
import shaderSource from './boids.wgsl?raw';
If you're not using a bundler, load via fetch:
const shaderSource = await fetch(new URL('./boids.wgsl', import.meta.url)).then((r) => r.text());
Modifying
Extend to 3D
Change vec2f to vec3f throughout the WGSL shader and TypeScript. Update buffer sizes from count * 2 * 4 to count * 4 * 4 (vec3f is 16-byte aligned in WGSL storage buffers). Add a bounds.depth parameter and update the wrapping logic.
Add obstacle avoidance
Add a storage buffer of obstacle positions and radii. In the main loop, after the three flocking forces, add a repulsion force from any obstacles within a detection range:
for (var i = 0u; i < obstacle_count; i++) {
let diff = pos - obstacles[i].xy;
let dist = length(diff);
if (dist < obstacles[i].z) { // z = radius
force += normalize(diff) * avoidance_weight / max(dist, 0.001);
}
}
Add spatial hashing for large flocks
Replace the O(n^2) neighbor search with a spatial hash grid. The approach:
- Add a compute pass that assigns each boid to a grid cell (hash position to cell index)
- Sort boids by cell index using a radix sort (see the
radix-sortmodule) - In the main flocking pass, only search boids in the 3x3 neighborhood of cells
This reduces the search from O(n^2) to O(n * k) where k is the average number of neighbors.
Tune flocking behavior
The balance of separationWeight, alignmentWeight, and cohesionWeight controls the overall character:
- High separation, low cohesion: scattered, avoidant movement
- High cohesion, low separation: tight clusters that clump together
- High alignment: synchronized, parallel movement (schooling fish)
- Equal weights: classic balanced flocking
The separationRadius and neighborRadius control the spatial scale. A larger neighborRadius means boids coordinate over longer distances; a larger separationRadius means they maintain more personal space.
Further Reading
Resources on flocking algorithms, boids, and GPU particle simulation.
Original Research
Craig Reynolds, "Flocks, Herds, and Schools: A Distributed Behavioral Model" (SIGGRAPH 1987) The foundational paper introducing boids. Defines the three rules (separation, alignment, cohesion) and demonstrates emergent flocking behavior from simple local interactions. Reynolds won an Academy Award for Technical Achievement for this work. https://dl.acm.org/doi/10.1145/37402.37406
Craig Reynolds, "Steering Behaviors for Autonomous Characters" (GDC 1999) Extends the original boids model with practical steering behaviors for games: seek, flee, pursue, evade, wander, path following, obstacle avoidance, and leader following. https://www.red3d.com/cwr/steer/gdc99/
Craig Reynolds' Boids page The author's own reference page with links to papers, implementations, and related work. https://www.red3d.com/cwr/boids/
GPU Implementation
WebGPU Samples — Compute Boids The official WebGPU samples include a boids implementation. A useful reference for WebGPU-specific patterns like ping-pong buffers and compute dispatch. https://webgpu.github.io/webgpu-samples/?sample=computeBoids
Simon Green, "Particle Simulation using CUDA" (2010) NVIDIA technical report covering GPU particle systems with spatial hashing for neighbor search. The spatial hash approach described here can be adapted to optimize this module's brute-force neighbor search. https://developer.download.nvidia.com/assets/cuda/files/particles.pdf
Joselli et al., "A Flocking Boids Simulation and Optimization Structure for Mobile Multiprocessor Systems" (2009) Explores optimization strategies for GPU flocking simulations, including spatial partitioning and workload balancing.
Spatial Hashing
Matthias Teschner et al., "Optimized Spatial Hashing for Collision Detection of Deformable Objects" (2003) The spatial hashing technique that can replace brute-force neighbor search for large boid counts. Maps 3D positions to a 1D hash table using a simple modular hash. https://matthias-research.github.io/pages/publications/tetraederCollision.pdf
Green, "GPU Gems 3: Chapter 32 — Broad-Phase Collision Detection with CUDA" GPU-friendly spatial hashing with sort-based construction, directly applicable to optimizing boid neighbor search. https://developer.nvidia.com/gpugems/gpugems3/part-v-physics-simulation/chapter-32-broad-phase-collision-detection-cuda
Emergent Behavior
Iain Couzin et al., "Collective Memory and Spatial Sorting in Animal Groups" (2002) Studies how simple local rules produce complex group-level phenomena in animal collectives. Provides biological context for why the boids model works. https://doi.org/10.1006/jtbi.2001.2443
Vicsek et al., "Novel Type of Phase Transition in a System of Self-Driven Particles" (1995) Physics perspective on flocking as a phase transition. The Vicsek model is a simplified variant of boids that's easier to analyze mathematically. https://doi.org/10.1103/PhysRevLett.75.1226
General References
Daniel Shiffman, "The Nature of Code" — Autonomous Agents Accessible introduction to steering behaviors and flocking, with step-by-step code examples. Good for understanding the math behind the Reynolds model. https://natureofcode.com/autonomous-agents/
Sebastian Lague, "Coding Adventure: Boids" (YouTube) Visual walkthrough of implementing boids with GPU compute shaders, including spatial partitioning optimization. https://www.youtube.com/watch?v=bqtqltqcQhw