About the Simulation
This is a real-time 3D flocking simulation built with Three.js and WebGL. It demonstrates emergence — how complex, organic group behavior arises from a few simple rules followed by each individual agent.
The simulation is based on Craig Reynolds' Boids algorithm (1986), which models the coordinated movement of flocks of birds, schools of fish, or swarms of insects. Each agent independently follows three core rules, and the interplay between them produces lifelike motion without any centralized control.
How It Works
Separation — each agent steers away from nearby neighbors to avoid crowding and collisions. The closer a neighbor is, the stronger the repulsion. This prevents agents from overlapping and maintains personal space within the flock.
Alignment — each agent steers toward the average direction (velocity) of its neighbors. This causes the group to move in a coordinated fashion, with individuals matching the heading and speed of those around them.
Cohesion — each agent steers toward the average position of its neighbors. This keeps the group together, preventing individuals from straying too far and maintaining the integrity of the flock.
The balance between these three forces is controlled by the Separation, Alignment, and Cohesion sliders in the settings panel. Additional forces include boundary avoidance (agents are pushed back when they reach the edge of the world), turbulence (random noise for organic movement), and predator fear (agents flee from red predator agents).
Interacting with the Simulation
🖱 Click & Drag — attract agents toward any point in 3D space. A glowing ring marks the attraction point. This is useful for directing the swarm, creating interesting patterns, or simply playing with the flock.
🔄 Drag (right button) — orbit the camera around the scene to view the swarm from any angle. The camera auto-rotates by default, but you can toggle this off.
🔍 Scroll — zoom in for a close-up view of individual agents or zoom out to see the entire swarm in its spherical boundary.
⚙ Settings Panel — click the gear icon in the top-left corner to open the full control panel. Adjust flocking parameters, visual settings, color modes, and apply presets in real-time.
Predators
The simulation includes predator agents (shown in red) that actively hunt the regular agents. Predators are larger, faster, and have a simple AI: they seek out the nearest agent within their detection radius and chase it. Regular agents, in turn, flee from predators with a strength determined by the "Predator Fear" slider.
This creates a dynamic hunt-and-evade ecosystem within the simulation. When fear is high, agents scatter chaotically; when fear is low, they barely react to predators. The number of predators can be adjusted via presets (the "Hunt" preset has 8 predators, while "Calm" has only 1).
Performance & Technology
The simulation is optimized to run smoothly in your browser using WebGL. Key performance techniques include:
InstancedMesh — all agents are rendered as a single draw call using Three.js InstancedMesh, which is far more efficient than creating individual mesh objects for each agent. This allows thousands of agents to be rendered at 60 FPS.
Spatial Grid — instead of checking every agent against every other agent (which would be O(n²) and impossible at scale), the world is divided into a 3D grid. Each agent only checks for neighbors in nearby grid cells, reducing the complexity to O(n) in practice.
Bloom Post-Processing — the UnrealBloomPass adds a subtle glow effect to the agents, creating a more visually striking appearance. The bloom strength can be adjusted or disabled entirely via the settings panel.
Frequently Asked Questions
Boid is a term coined by computer scientist Craig Reynolds in 1986. It's a portmanteau of "bird" and "android" — essentially a "bird-like object." Reynolds developed the Boids algorithm to simulate the coordinated movement of flocks of birds, schools of fish, and swarms of insects.
The algorithm is a classic example of emergence: complex global behavior (the flock) emerges from simple local rules followed by each individual. Each boid only considers its immediate neighbors, yet the group as a whole moves in a coherent, organic way. This concept has been widely used in computer graphics, animation (including Hollywood films like The Lion King and Batman Returns), robotics, and artificial life research.
The original paper, "Flocks, Herds, and Schools: A Distributed Behavioral Model," was published at SIGGRAPH in 1987 and remains one of the most cited works in computer graphics and artificial life.
In a naive implementation, each agent would need to check its distance to every
other agent to find its neighbors. With N agents, this is
O(N²) — for 3000 agents, that's about 9 million distance calculations
per frame. This quickly becomes a bottleneck.
The spatial grid (also called a uniform grid or hash grid) divides the 3D world into cubic cells. Each agent is assigned to the cell it currently occupies. When an agent needs to find its neighbors, it only checks the 27 cells (3×3×3) surrounding its position — the cell it's in plus the adjacent cells in each direction.
This reduces the neighbor search from O(N²) to approximately
O(N × k), where k is the average number of agents in
the nearby cells. Since k is typically small (bounded by the
neighbor radius and agent density), the overall complexity is effectively
O(N). This is what makes simulations with thousands of agents
possible in real-time.
In Three.js, every Mesh object you add to the scene requires a
separate draw call to the GPU. With thousands of agents, creating individual
meshes would result in thousands of draw calls, which would severely impact
performance — even if the geometry and material are identical.
InstancedMesh solves this by rendering many copies of the same geometry in a single draw call. Each copy (called an "instance") can have its own position, scale, rotation, and color, but they all share the same underlying geometry and material. This is extremely efficient because the GPU processes all instances in parallel.
In this simulation, both the main agent spheres and the glow spheres use
InstancedMesh. This allows us to render 3000+ agents with minimal performance
overhead. The instance data (position, scale, color) is updated every frame
using a Float32Array and uploaded to the GPU as a single buffer.
Predators use a different AI than regular agents. While regular agents follow the three Boids rules (separation, alignment, cohesion), predators have a simple chase behavior:
Each predator scans for the nearest regular agent within a detection radius (30 units). If it finds one, it steers directly toward that agent at a higher speed than regular agents. If no agent is nearby, the predator wanders randomly. Predators also respect the boundary — they turn back if they reach the edge of the world.
Regular agents, in turn, have a flee response: when a predator is within 20 units, the agent accelerates away from it. The strength of this response is controlled by the "Predator Fear" slider. At high fear values, agents scatter explosively; at low values, they barely react.
This creates an asymmetric relationship: predators are faster and stronger individually, but regular agents have the advantage of numbers and coordinated flocking. The result is a dynamic chase scene where the flock constantly reshapes itself in response to the threat.
The color mode changes how agent colors are calculated each frame. There are four modes:
Rainbow — each agent cycles through the full color spectrum over time, with speed influencing brightness. This creates a constantly shifting, vibrant display where the flock appears to ripple with color.
Speed — agents are colored based on their current velocity. Slow agents appear blue, medium-speed agents appear green/yellow, and fast agents appear red. This gives you a visual heatmap of the flock's internal dynamics.
Teams — agents are divided into four color groups (teams) based on their initial hue. This makes it easy to see how different subgroups mix and interact within the larger flock.
Heat — similar to speed mode but uses a hot color palette (black → red → orange → yellow → white). Slow agents are dark, fast agents are bright yellow/white. This creates a dramatic thermal-imaging effect.
The Connection Distance slider (in the Visuals section of the settings panel) draws lines between agents that are within a certain distance of each other. When set to 0, no lines are drawn.
As you increase the distance, you'll see a web of connections forming between nearby agents. This visualizes the neighborhood relationships that drive the flocking algorithm. At low values, only very close agents are connected, revealing tight clusters. At higher values, the entire flock becomes a interconnected network, showing how information (in the form of positional and velocity data) propagates through the group.
This is particularly interesting to watch with the "Teams" color mode, as you can see whether agents of the same team tend to cluster together or mix freely.
Even with optimizations like InstancedMesh and spatial grids, there are limits to what can be achieved in real-time on consumer hardware. The main bottlenecks are:
CPU: neighbor search — while the spatial grid reduces complexity, each agent still needs to iterate over its neighbors and compute forces. With 6000 agents and a large neighbor radius, each agent may have dozens of neighbors, resulting in hundreds of thousands of distance calculations per frame.
CPU: instance matrix updates — every frame, we update the position, scale, and color of every agent in the instance buffer. For 6000 agents, that's 6000 matrix calculations and 18000 color channel updates, all on the CPU.
GPU: fill rate — with bloom post-processing enabled, the GPU needs to render the scene multiple times (once for the main pass, once for bloom extraction, once for blur, and once for compositing). At high agent counts and high resolutions, this can become fill-rate limited.
If you experience slowdowns, try reducing the Agent Count, decreasing the Neighbor Radius, or lowering the Bloom Strength. The simulation is designed to scale gracefully — you can run it with 100 agents on a low-end device or 6000 on a high-end gaming machine.
Absolutely! This simulation is a single HTML file with no external dependencies (everything is loaded via CDN). You can freely use, modify, and adapt it for your own projects — whether for education, research, game development, or creative coding.
The code is structured with clear sections and comments. Key areas to explore:
The Agent class contains the core flocking logic in the
flock() method — this is where you'd modify the Boids rules or
add new behaviors. The CONFIG object at the top of the script
controls all adjustable parameters. The SpatialGrid class handles
neighbor lookups and can be replaced with a different spatial partitioning
scheme (like an octree) if needed.
The simulation is built with Three.js, which is MIT-licensed. The code itself is provided as-is — feel free to learn from it, remix it, and build upon it. If you create something interesting, I'd love to see it!
The Time Scale slider controls the speed at which the simulation runs. At 1.0 (default), the simulation runs in real-time — one second of simulation time equals one second of real time.
Increasing the Time Scale (up to 3.0) makes the simulation run faster. Agents move further each frame, and the flocking dynamics play out at high speed. This can create interesting visual effects and is useful for quickly observing how parameter changes affect the overall behavior.
Decreasing the Time Scale (down to 0.1) slows everything down, allowing you to observe the fine details of agent interactions. At very low speeds, you can see exactly how individual agents respond to their neighbors, how predators track their prey, and how the flock reorganizes after being disturbed.
Note that Time Scale is independent of frame rate — the simulation uses delta-time (the actual time elapsed between frames) multiplied by the Time Scale to compute movement. This ensures smooth behavior regardless of your monitor's refresh rate or performance fluctuations.
When the simulation starts (or when you click "Reset"), agents are placed
randomly within a sphere of radius worldSize × 0.6 (about 72 units
out of a maximum of 120). The distribution uses uniform spherical
sampling: each agent gets a random azimuth angle (0 to 2π), a random
inclination angle (using acos(2 × random - 1) for uniform
distribution on the sphere), and a random radius.
Initial velocities are also random — each agent gets a random direction vector scaled to half the maximum speed. This ensures the flock starts in a disorganized state and quickly self-organizes into coherent groups as the flocking rules take effect.
Predators are placed using the same method but start with higher initial speeds. The separation between agents at startup is not guaranteed — agents may overlap initially, but the separation force quickly pushes them apart within the first few frames.
Scroll back up to interact with the simulation