Skip to content
Sample of Boids Flock (Hodgin Murmuration)

hodgin_iquilezles · BoidsFlock

Boids Flock (Hodgin Murmuration)

A 2D flock of boids steered by Craig Reynolds' three classic rules (separation, alignment, cohesion) over a coarse spatial-hash neighbor query. A weak long-range goal force tugs the flock across the canvas. Particles trace a low-alpha stroke buffer, producing Hodgin's swooping, ever-coalescing silhouettes.

Original: https://roberthodgin.com/project/murmuration

▶ Try the live demo (JS port) ▶ Run reference.py via Pyodide 10 formulas have instant JS ports. The Pyodide path runs the actual Python in your browser — first load is ~6 MB, then instant.

Family
hodgin_iquilezles
Source
hodgin × iquilezles
Evolution
particle
Render
path_tracing
Complexity
medium
Agents
250
Year
2008
Author
Robert Hodgin (flight404)

Math essence

For each boid i, accumulate weighted sums of separation (1/r^2 push inside separation_radius), alignment (mean velocity inside neighbor_radius), and cohesion (mean position inside neighbor_radius), using a uniform spatial hash for O(num) total work. A weak goal-toward-vector adds a small acceleration. Total acceleration is clamped to max_force, integrated into velocity (clamped to max_speed) and position. The previous-to-current position segment is stroked onto a decaying canvas.

Parameters

Name Type Default Range Description
dim int 500 200–1000 Square canvas side length in pixels.
num int 250 60–1500 Number of boids.
neighbor_radius float 32 10–80 Radius (px) inside which a boid considers neighbours for align/coh/sep.
separation_radius float 14 4–40 Inner radius (px) within which separation dominates.
w_sep float 1.6 0–4 Separation weight.
w_align float 1 0–3 Alignment weight.
w_coh float 0.9 0–3 Cohesion weight.
w_goal float 0.04 0–0.3 Migration-goal weight.
max_speed float 3.2 0.5–8 Speed clamp (px/frame).
max_force float 0.18 0.02–0.6 Acceleration clamp (px/frame^2).
trail_decay float 0.965 0.85–0.999 Per-frame multiplicative fade of the canvas.

Reference

First 60 lines of reference.py. Full source: library_external/hodgin_iquilezles/BoidsFlock/reference.py

from __future__ import annotations

from typing import ClassVar

import numpy as np


class BoidsFlock:
    """Boids Flock — Robert Hodgin (flight404), circa 2003–2024.

    A 2D Reynolds-style boids simulation in the spirit of Hodgin's
    *Murmuration* / "Bird Migration" series (roberthodgin.com/project/murmuration).
    Each boid obeys three classic Reynolds rules — *separation*, *alignment*,
    *cohesion* — applied to all neighbours inside ``neighbor_radius``.
    A weak long-range "migration" force tugs the flock toward ``goal``,
    yielding the swooping, ever-coalescing silhouettes Hodgin is known for.

    Particles trace a low-alpha stroke from their previous to their current
    position; the accumulated frame buffer is the rendered image.  The
    algorithm uses a coarse spatial hash so that ~200–1000 boids run in
    near-realtime even on pure NumPy.
    """

    family: ClassVar[str] = "hodgin_iquilezles"
    sketch: ClassVar[str] = "boidsflock"

    def __init__(
        self,
        dim: int = 500,
        num: int = 250,
        neighbor_radius: float = 32.0,
        separation_radius: float = 14.0,
        w_sep: float = 1.6,
        w_align: float = 1.0,
        w_coh: float = 0.9,
        w_goal: float = 0.04,
        max_speed: float = 3.2,
        max_force: float = 0.18,
        trail_decay: float = 0.965,
        ink_color: tuple[int, int, int] = (240, 245, 255),
        background: tuple[int, int, int] = (8, 8, 14),
        **_,
    ) -> None:
        self.dim = dim
        self.num = num
        self.neighbor_radius = neighbor_radius
        self.separation_radius = separation_radius
        self.w_sep = w_sep
        self.w_align = w_align
        self.w_coh = w_coh
        self.w_goal = w_goal
        self.max_speed = max_speed
        self.max_force = max_force
        self.trail_decay = trail_decay
        self.ink_color = np.array(ink_color, dtype=np.float32)
        self.bg_color = np.array(background, dtype=np.float32)
        self.reset()

    def reset(self, seed: int | None = None) -> None:
        self.rng = np.random.default_rng(seed)

Try running it locally

The reference implementation is self-contained Python with numpy. Install the package and run:

python library_external/hodgin_iquilezles/BoidsFlock/reference.py

Need help? See Learn for the standard environment setup, or Playground for an in-browser sketchbook.

More from hodgin × iquilezles

Sample of Fractional Brownian Motion noise

Fractional Brownian Motion noise

hodgin_iquilezles · FBMNoise

Quilez's canonical fBM: a 2D value-noise base summed over N octaves whose frequency doubles and amplitude is multiplied by G = exp2(-H) per octave.

View →
Sample of Magneto Particles (Hodgin Magnetosphere)

Magneto Particles (Hodgin Magnetosphere)

hodgin_iquilezles · MagnetoParticles

Particles pulled and pushed by N orbiting magnetic anchors whose charges flip sign over time.

View →
Sample of Mulberry32 PRNG (stratified scatter)

Mulberry32 PRNG (stratified scatter)

hodgin_iquilezles · Mulberry32RNG

Mulberry32 is a tiny 32-bit-state pseudo-random number generator popularised in the demoscene and used pervasively by Quilez and other graphics programmers as the RNG inside procedural sketches.

View →
Sample of Saturn Rings (orbital resonance)

Saturn Rings (orbital resonance)

hodgin_iquilezles · SaturnRings

Thousands of particles are initialized in an annulus between inner_radius and outer_radius around a central mass, with circular orbital velocities v = sqrt(M/r).

View →
Sample of Smooth-Minimum SDF field

Smooth-Minimum SDF field

hodgin_iquilezles · SmoothMinSDF

Two animated 2D SDF primitives (a moving circle and a translating slab) are blended with Quilez's quadratic-polynomial smooth-minimum and rendered as a scalar field.

View →
Sample of Worley / Cellular Noise

Worley / Cellular Noise

hodgin_iquilezles · WorleyNoise

Quilez's classic 2D Worley noise: a unit grid of cells, each containing one pseudo-random feature point placed by a 2D integer hash.

View →

See all hodgin_iquilezles artworks →