The Concept
The two references for this assignment are really different from each other visually but I think they’re pointing at the same thing. Robert Hodgin’s Murmuration is basically a love letter to flocking as a visual phenomenon thousands of agents moving like one organism, the shape constantly shifting. Ryoichi Kurokawa is more abstract, but what I find interesting about his work is that it’s never just a system running. It’s always a system building toward something and then either resolving or falling apart. There’s always a direction to it.
What I wanted to do for this assignment was combine both of those ideas. The flocking is the base, but instead of just running it at fixed parameters and watching it loop forever, I wanted to design three distinct modes that the system moves through over time, each one pulling a different force to the foreground. The first mode is Scatter: cohesion and alignment are weak, the flock barely holds together, boids drift around loosely. The second is Order: alignment and cohesion spike up, the flock snaps into a tight murmuration and starts moving as one. The third is Break: flocking forces drop off and each boid gets its own slow random wander, so the flock fragments and individuals peel off in different directions.
The thing that made this interesting to work on is that the three modes aren’t really about telling a story, they’re just three different parameter states of the same flocking system. What surprised me is that even without trying to make it narrative, it ends up feeling like one. The flock coalesces and then falls apart and it just reads that way.
Code I’m Particularly Proud Of
The part I keep coming back to is the phase table + lerp structure:
const PHASES = [
{ sep: 1.8, ali: 0.4, coh: 0.15, wan: 0.0, hue: 210 }, // Scatter
{ sep: 1.1, ali: 2.4, coh: 2.0, wan: 0.0, hue: 160 }, // Order
{ sep: 0.5, ali: 0.12, coh: 0.06, wan: 0.6, hue: 30 }, // Break
];
curSep = lerp(curSep, tgt.sep, LERP_SPEED);
curAli = lerp(curAli, tgt.ali, LERP_SPEED);
curCoh = lerp(curCoh, tgt.coh, LERP_SPEED);
curWan = lerp(curWan, tgt.wan, LERP_SPEED);
curHue = lerp(curHue, tgt.hue, LERP_SPEED * 0.6);
All the artistic decisions about how each phase feels live in that one table. The five lerp calls handle every transition. What I really like is that the hue gets its own slower lerp multiplier, 0.6 of the normal speed, so the color shift lags slightly behind the behavior shift. The flock has already started tightening before the color fully reaches cyan, and the amber is still coming in as the boids are beginning to scatter. It makes the color feel reactive to the motion rather than synchronized with it.
Building It Up: Milestones & Challenges
Milestone 1: Getting the Three Rules Working
I started with the simplest possible thing, just to get the basic flocking running and understand what the three rules actually look like before doing anything else with them. No phases, no colors, no timer. Just separation, alignment, and cohesion on 120 boids against a black background.

This was more useful than I expected as a standalone step. Just watching the raw flocking with a plain white fill really helped me see what each rule contributes. I played with the weights a lot at this stage messing cohesion way up makes everything clump into a tight ball and stop moving. Messing separation makes them scatter and never reform. The balanced values I landed on here (sep: 1.8, ali: 1.0, coh: 0.8) ended up being the starting point for the Scatter mode in the final sketch.
One thing I noticed that I didn’t expect: the faint motion trails from the semi-transparent background actually read as a really clean visual even in pure black and white. I kept that in all three versions.
Milestone 2: Three Phases + Lerp Transitions
Once the base flocking felt right I added the three modes: Scatter, Order, Break, with a phase clock that advances every 660 frames and a set of target weights for each one. Still black and white at this point, but now with trails added and the lerp system in place.
The first version of the transitions used a hard switch, when the timer hit the phase boundary everything snapped to the new weights in a single frame. It looked bad. The flock would visibly lurch. Replacing the hard switch with lerp(cur, target, 0.018) means the weights drift toward the new values over about 50 frames, which smooths it out completely. You stop noticing the phase change and just feel the mood gradually shift.
Getting the Break phase right was the trickiest part of this milestone. Reducing cohesion and alignment alone wasn’t enough. The flock would just slow down and drift more randomly but stay roughly in the same area. I needed something that actively pulled individual boids away from the group, not just weakened the forces holding them together. That’s what the wander behavior is for. Each boid has its own wanderAngle that slowly drifts by a unique random amount each frame, so when the wander force ramps up during Break, every boid pulls off in its own direction. The flock fragments organically rather than just dispersing uniformly.
I also added a minimal phase label and a thin progress bar at the top left here — not for the final version necessarily, but useful to see while testing so you know which phase you’re actually in.
Challenge: Balancing the Color Shift
Moving from Milestone 2 to the final version was mostly about adding the color treatment, and the trickier part was making the color feel right rather than just technically correct.
My first attempt colored the boids directly by the current hue in HSB mode, which worked but looked flat. Every single boid was exactly the same color at any given moment. Adding a per-boid hueOffset of ±18 degrees fixed that immediately. The flock has a dominant color temperature but individual boids sit slightly warm or cool relative to it, which makes the whole thing look organic instead of painted.
The bigger issue was the timing of the color transitions. The hue lerps on the same LERP_SPEED as the weight changes, so originally the boids would turn amber at the exact same time as they started scattering. It felt too mechanical, like a mode switch rather than a natural shift. Slowing the hue lerp down to LERP_SPEED * 0.6 added enough lag that the color and the behavior feel like they’re influencing each other rather than switching together. That small change made a bigger difference to the feel of the piece than I expected.
The neighbor lines during Order were also something I wanted to get right visually. They connect boids within 48px of each other and fade based on distance, so as the flock compresses during Order the lines naturally get denser without me doing anything extra. I didn’t plan that, it just falls out of the density increasing.
The Final Result
Three modes cycling continuously: Scatter (cool blue, loose), Order (cyan-white, tight lattice), Break (amber, flock fragments and fades). Weights lerp smoothly between modes, color shifts on a slower delay. Grain overlay kills the flatness.
Controls:
- R — restart
- S — save frame
Reflection & Future Work
What I find most interesting looking back at this is how much work the parameter table does. The flocking code itself is basically unchanged from Milestone 1 — same three rules, same math. All the artistic decisions are in those five numbers per phase. Changing the coh weight during Order from 1.8 to 2.0 makes the flock noticeably tighter. Changing the wan weight during Break from 0.4 to 0.6 makes the collapse feel more violent. I spent most of my time in the final version just in that table, adjusting numbers and seeing what changed.
I also really like the neighbor lines as an emergent feature. I didn’t think “I want a lattice effect during Order”. I added the lines as a visual layer and the lattice emerged automatically because the boids happen to be close together during Order. That’s exactly the kind of thing I find satisfying about this kind of work, you set up the rules and the visuals kind of discover themselves.
What I’d explore next:
- User control over phase timing: being able to hold Order longer or trigger Break early with a keypress would make it interactive in an interesting way
- Multiple flocks running offset: two flocks on the same canvas at different points in the arc, occasionally interacting when their spaces overlap
- Predator boid: during Break, instead of just wander, have a single predator that chases the flock. The flock tries to reform against the predator, which adds conflict to the Break phase instead of just dissolution