The Wandering Birds System
On Friday last week, the wind was doing something grand to the sky over Seaford and the South of England coastline. Storms Austen and Boleo were setting a dramatic scene over the sea, and a loose scatter of dark shapes were heading somewhere, none of them really agreeing where. Swifts, skylarks, I couldn’t tell you.
Capture a moment
It inspired me to think about capturing the balance of randomness and control in their movement. So dusting off my p5.js knowledge (it’s a limited tome), I got to thinking about how I might recreate what I saw.
I started with a grid. Bit of a falsity, birds don’t exactly adhere to grids (watch a pigeon traverse its way across paving slabs with equal levels of neuroticism and nonchalance). But I needed to start my birds somewhere. Twenty five columns, twenty rows, one bird per cell.
const COLS = 25; const ROWS = 20;
A grid is a lovely way to think about placement, kind of art in itself. But it’s a terrible way to think about birds. Five hundred of them in tidy rows and the page looked like a weird magic eye puzzle.
So I staggered every other row like brickwork, then nudged each bird a little off its centre point.
const ROW_STAGGER = 0.5; const JITTER = 0.35;
Better still, bu still a puzzle, just a messier one. And every cell was occupied, which isn’t how a flock sits. So I threw away five in every hundred at random. Small change. Instantly less machined.
Capture the direction
Now I needed the flock to actually fly somewhere rather than spread itself over the whole canvas.
So I started simple with a line from bottom left to top right. But in the words of William Kent, “Nature abhors a straight line,” and he was right, because the birds I’d seen weren’t travelling in one. So I gave it a gentle curve instead. Enough to feel like it was going somewhere on purpose.
The curve on its own only tells each bird which way to face, though. They’d still be sat in every cell, politely pointing the same way. So the last bit of the grid went: instead of a bird in every cell, each cell got a chance of one. Near the curve, almost certain. Out at the edges, a coin toss. Far from it, nothing.
Capture the movement
The interesting thing is that in a gale there isn’t a single collision. They’re not following a leader and they haven’t all read the same memo. Each one is just watching the few nearest it and matching them.
I basically cheated a bit with Perlin noise. Ask it what’s happening at two spots a short distance apart and the value comes back nearly the same. Further away and the answer drifts. So I use it as wind. Each bird asks the noise what the gust is doing right here, and turns by that much. Neighbours get almost the same shove. A bird over on the other side of the page gets its own weather entirely.
You can see it in the output once you know to look. Pockets of birds leaning left and right together. Movement that feels considered rather than scattered.
Which brings up the thing I hadn’t thought about. Two birds overlapping on the page doesn’t read as two birds. It reads as a crash. And birds don’t crash. They’re reading each other constantly, adjusting, giving way, and the one thing they never do is occupy the same bit of sky. One pair of overlapping outlines is enough to tell the eye that these aren’t birds, they’re shapes.
My birds have no idea what’s already on the page. They get put down in their cells blind. So after everything else is decided, every bird gets checked against every other one, and any two whose outlines touch both get deleted.
function removeOverlaps(birds) { const gone = new Set(); for (let i = 0; i < birds.length; i++) { for (let j = i + 1; j < birds.length; j++) { // ...do these two touch? } } return birds.filter((_, i) => !gone.has(i)); }
Capture the variation
I tried many - but in the end I only needed three poses. Interestingly variety in movement and direction did the rest visually. Banking, one wing long and the other swept back. Winging to one side, riding a bit of air. And seen from below, nearly symmetrical. I weighted them so banking and winging turn up often and the underneath view is a treat.
const POSES = { bank: 2, wingsup: 3, below: 1 };
Each wing is its own shape, with its own angle, length and curve. That’s what lets a bird look like it’s doing something rather than being a copy of the one next to it.
So finally, I needed enough variation to please the eye, but not feel contrived.
Still couldn’t tell you what the birds over Seaford were. Swifts, skylarks, something else. Storms Austen and Boleo have cleared off now. The sky over Seaford is doing nothing much at all, which feels like a wasted opportunity.
I spent the rest of the weekend trying to pin it down with code and maths. Couldn’t do it, obviously... but I think I got close enough to frame it. :)
Watch this space, as we free these birds on to paper next week....