Simulating thousands of birds without feathers flocking together

Reviewing the bird-physics literature and building a tunable simulation of the collective motion of a flock

Preparing the flock…
Change the bird equation
Ai=wiJ(v‾Ni−vi)⏟alignment +\displaystyle \mathbf A_i=\underbrace{w_iJ(\overline{\mathbf v}_{\mathcal N_i}-\mathbf v_i)}_{\text{alignment}}\,+wiC(x‾Ni−xi)⏟cohesion +\displaystyle \underbrace{w_iC(\overline{\mathbf x}_{\mathcal N_i}-\mathbf x_i)}_{\text{cohesion}}\,+S∑j∈Ri(1−rij/r0)xi−xjrij⏟separation +\displaystyle \underbrace{S\sum_{j\in\mathcal R_i}(1-r_{ij}/r_0)\frac{\mathbf x_i-\mathbf x_j}{r_{ij}}}_{\text{separation}}\,+Ei⏟disturbance +\displaystyle \underbrace{\mathbf E_i}_{\text{disturbance}}\,+Ri⏟roost +\displaystyle \underbrace{\mathbf R_i}_{\text{roost}}\,+Bi⏟altitude +\displaystyle \underbrace{\mathbf B_i}_{\text{altitude}}\,+ηi⏟variation\displaystyle \underbrace{\boldsymbol\eta_i}_{\text{variation}}
Alignment of the flock 0.00Mean speed 0.0 m/sWhat do these controls mean? ↓

A few thousand birds can turn together, stretch into a ribbon, or divide around an obstacle without anyone appearing to direct traffic.

One way to animate a flock is to decide where every bird should go and draw the paths. Craig Reynolds took a different approach in his 1987 paper on boids. He gave simulated individuals local behaviors: avoid crowding, align with nearby individuals, and stay near the group. The flock’s motion followed from repeatedly applying those rules. As his abstract puts it, the birds (or equivalently boids for New Yorkers) “choose their own course” (Reynolds, 1987).

That is a useful start, but it leaves a lot unspecified. What counts as nearby? Do birds align their directions or their full velocities? How do those local interactions let a flock change direction and stay together?

Birds are not a gas at equilibrium (and flocks cannot be approximated as a sphere for that matter)

The connection to statistical physics is fairly direct. We have many interacting degrees of freedom, local fluctuations, and a collective state. But birds continually consume energy to move. Their speed does not simply decay by drag until everything comes to rest. They are examples of active matter: the constituents take in energy and convert it into motion or mechanical work (Ramaswamy, 2010).

These systems, as you can imagine, don't gradually settle to equilibrium. For birds, dissipation includes energy transferred to the air and heat. Flocks of birds form these familiar collective swarms. Walk under a flock and you may find out what else they dissipate on your freshly washed clothes.

In the model introduced by Vicsek and collaborators in 1995, particles move at a fixed speed and update their headings toward the local average, with added directional noise. There is no leader supplying the direction of travel. Nevertheless, coordinated motion can emerge (Vicsek et al., 1995). This simplified mechanic lets us ask about ordering without first determining the biomechanics of a wing, much as we can predict the orbit of a planet without worrying so much about how matter is distributed within it.

A convenient measure of directional order is the polarization,

P=∣1N∑i=1Nu^i∣,P=\left|\frac{1}{N}\sum_{i=1}^{N}\hat{\mathbf u}_i\right|,

where u^i\hat{\mathbf u}_i is the unit vector along bird ii’s velocity and NN is the number of birds. If all headings agree, P=1P=1. If headings largely cancel, PP is small. However, this does not tell us everything. An organized flock moving around a circle can have a small total polarization.

Toner and Tu instead described slowly varying density and velocity fields. Their 1995 continuum equations are

∂tv+(v⋅∇)v=αv−β∣v∣2v−∇p+DL∇(∇⋅v)+D1∇2v+D2(v⋅∇)2v+f,∂tρ+∇⋅(ρv)=0.\begin{aligned} \partial_t\mathbf v+(\mathbf v\cdot\nabla)\mathbf v &=\alpha\mathbf v-\beta|\mathbf v|^2\mathbf v-\nabla p +D_L\nabla(\nabla\cdot\mathbf v)\\ &\quad+D_1\nabla^2\mathbf v+D_2(\mathbf v\cdot\nabla)^2\mathbf v+\mathbf f,\\ \partial_t\rho+\nabla\cdot(\rho\mathbf v)&=0. \end{aligned}

Here ρ\rho is number density, v\mathbf v is the coarse-grained velocity, and f\mathbf f is random forcing. The density-dependent pressure is written pp here to distinguish it from polarization. The α,β\alpha,\beta terms select a nonzero speed when α>0\alpha>0, the DD terms smooth spatial variations, and the second equation conserves bird number (Toner and Tu, 1995).

The convective term (v⋅∇)v(\mathbf v\cdot\nabla)\mathbf v carries orientation as the individuals move and exchange neighbors. Their analysis finds stable orientational order even in two dimensions. A moving state selects a direction although the equations select none: spontaneous symmetry breaking away from equilibrium. The equilibrium Mermin–Wagner restriction therefore does not decide this problem. The conclusion depends on the nonlinear dynamics, not just the uniform solution ∣v∣=α/β|\mathbf v|=\sqrt{\alpha/\beta} (Toner and Tu, 1995).

Seven neighbors ≠\neq seven meters

Suppose every bird responds to all other birds within a fixed radius. Compress the flock and the number of interaction partners increases. Let it spread out and that number decreases; eventually some birds lose contact altogether. This is a metric neighborhood,

Nimetric={j≠i:∣xj−xi∣<r}.\mathcal N_i^{\rm metric}=\{j\ne i:|\mathbf x_j-\mathbf x_i|<r\}.

It selects the other birds whose positions lie inside an open ball in the language of metric spaces. In flocking, a topological neighborhood usually means a fixed number of nearest individuals. Their distances change as the flock expands, but their number does not. This specialized usage is about neighbor rank. “The seventh closest bird” is a different instruction from “every bird within seven meters.”

Ballerini and company used three-dimensional reconstructions of starling flocks to investigate this distinction. They inferred an interaction range from the anisotropy of neighbor positions: nearby birds were not distributed uniformly around a reference bird, and that directional structure weakened with increasing neighbor rank. Across flocks with different densities, their results supported roughly six or seven* neighbors rather than a constant distance (Ballerini et al., 2008).

* A notable pair of numbers in these modern times

Neighbor-position anisotropy approaches its isotropic value of one third near the sixth or seventh neighbor in two measured flocks.
Figure 1. “For both flocks, the structure becomes approximately isotropic between the sixth and the seventh nearest neighbor.” The labels 32-06 and 25-11 identify two reconstructed flock events, containing 781 and 1,168 birds, respectively. The horizontal line marks the isotropic value γ=1/3\gamma=1/3. Panel reproduced from Ballerini et al. (2008), supplied as a crop.

The inference of seven comes from spatial structure and is experimentally motivated as a starting point; it does not directly record which birds each individual was watching. Let's bring that into our working model.

There is also no reason to make every interaction topological. Two bodies can collide whether they are each other’s first or twentieth neighbor. The model for this article uses nearest-neighbor selection for social coordination and a separate distance-dependent rule for close encounters.

Try changing the neighbor count while leaving the other settings alone. Increasing it changes which information each bird averages. That doesn't guarantee a more responsive flock, as averaging over more individuals may also dilute a local changes.

What starts a turnaround?

The original version of this animation had a straightforward problem: strong alignment, weak individual fluctuations, and no horizontal environmental preference. Adding a bird leading the pack on a prescribed route would create turns, but would also supply much of the answer in advance.

In a study of spontaneous turns, Attanasi and collaborators found initiators near the flock’s outer tips (Attanasi et al., 2015).

For the scene here, I have added a preference to remain near a roost. Hildenbrandt, Carere, and Hemelrijk’s StarDisplay combines local coordination with simplified flight mechanics and attraction toward a roosting region. Its horizontal return response depends on the bird’s heading: stronger when flying outward, weaker when already returning (Hildenbrandt et al., 2010).

Our birds similarly turn back after leaving a region, then continue inward before releasing that preference. They respond individually and influence their neighbors through the social terms discussed in What the simulation does.

How far does a turn travel?

A flock can have high polarization and still respond badly when something happens. Everyone might be traveling in the same direction, yet a turn by a few individuals could be lost before it reaches the rest. Photos of starling flocks alone don't settle this question.

In 2010, Cavagna and collaborators measured correlations between velocity fluctuations in starling flocks. The range of those correlations grew with the flock’s linear size. This is the sense in which the correlations were called scale free, meaning they did not reveal a fixed correlation length independent of the size of the group (Cavagna et al., 2010).

Speed correlation length rises approximately linearly with the measured flock's linear size.
Figure 2. The speed fluctuations correlation length ξsp\xi_{\rm sp} grows approximately as ξsp=aL\xi_{\rm sp}=aL, where LL is the flock’s linear size and aa is the fitted slope. Reproduced from Cavagna et al. (2010), speed-correlation panel; supplied crop.

A bird can interact directly with a few neighbors while sharing correlated fluctuations with birds much farther away. You might interact regularly with a small group of friends and share tastes with a much wider circle. But by the time you get to Kevin Bacon (see Six Degrees of Kevin Bacon), there likely isn't much in common. Interaction range ≠\neq correlation length.

The distinction is made clear during a turn. Attanasi and collaborators reconstructed turning starling flocks and found directional information propagating with a “linear dispersion law and negligible attenuation.” Their theory introduced behavioral inertia: resistance to changing the curvature of a flight path. In the nearly planar, ordered limit, their equations (6) and (8) read (Attanasi et al., 2014)

∂tφ=szχ,∂tsz=a2J∇2φ⟹∂t2φ=cs2∇2φ,cs2=a2Jχ.\partial_t\varphi=\frac{s_z}{\chi},\qquad \partial_t s_z=a^2J\nabla^2\varphi \quad\Longrightarrow\quad \partial_t^2\varphi=c_s^2\nabla^2\varphi,\qquad c_s^2=\frac{a^2J}{\chi}.

Here φ\varphi is the heading angle, szs_z is its conjugate spin variable, χ\chi is behavioral inertia, aa is the average nearest-neighbor spacing, and JJ is the theory’s alignment stiffness. These are the paper’s variables, not numerical values taken from our sliders. The resulting wave equation carries a turn with speed csc_s.

In our simulation, matching velocities and copying a turn after a delay can produce a collective turn. To compare it with these experiments, we would also need to measure how quickly the turn spreads and how much it weakens.

Why a flock darkens and divides

A bird presents a different silhouette when it banks. If nearby birds copy that maneuver, a dark band can travel through the flock without an equally large change in density. Hemelrijk and collaborators reproduced this effect with rolling maneuvers in a model of agitation waves (Hemelrijk et al., 2015). A two-dimensional view can also make birds at different heights appear to pass through one another.

Banking changes more than the view. Tilting the lift force makes some of it available for turning and leaves less to oppose gravity. Unless the bird increases lift, it loses height. In StarDisplay, coupling turns to flight mechanics helped produce changing flock shapes (Hemelrijk and Hildenbrandt, 2011). Our birds now have a bank angle and a lift response that affect their trajectories, rather than only tilting a mesh after the turn has happened.

The 50,000- and 100,000-point modes retain the movement rules but cannot display that wing-area effect. Wingbeats are just an animation and do not contribute in any aerodynamic way.

A split doesn't have to divide the flock in half. Papadopoulou and collaborators studied flocks pursued by a robotic falcon: different birds turned or dived at different times, and some groups separated while others remained connected (Papadopoulou et al., 2026). Those observations concern escape behavior.

Occasional disturbances arise near the flock’s edge. Affected birds briefly prioritize a turn or dive, and neighbors can copy it after a delay. The response weakens as it spreads. Neither the number of resulting groups nor their sizes is specified. Once the maneuver ends, normal coordination returns; groups can join again when their paths bring them close. The disturbance’s cause is not simulated.

What the simulation does

The equations below specify an effective steering model. Its constants, response times and limits are implementation choices, not fitted measurements.

Bird ii has position xi\mathbf x_i, velocity vi\mathbf v_i, and heading u^i=vi/∣vi∣\hat{\mathbf u}_i=\mathbf v_i/|\mathbf v_i|. Its target acceleration, Ai\mathbf A_i, is the sum of the responses below. Distances are in meters and time is in seconds.

The small diagrams isolate individual terms and have their own controls.

1. Choose the neighbors

Ni\mathcal N_i is the set of the nearest kk birds. A bar is their average: for example, v‾Ni=k−1∑j∈Nivj\overline{\mathbf v}_{\mathcal N_i}=k^{-1}\sum_{j\in\mathcal N_i}\mathbf v_j. If fewer than kk candidates are available, the average uses that smaller number. An empty set contributes no social attraction or alignment.

Below 20,000 birds, a spatial tree organizes the birds by their positions in three-dimensional space. It finds the nearest 24 birds without comparing every possible pair; the first kk contribute to the social coordination term.

7 nearest neighbors form the social set N_i, numbered by distance from bird i. The dashed circle reaches the farthest selected neighbor.
Bird iiSocial neighbors Ni\mathcal N_iOther birds
7  birds7\;\text{birds}
52%52\%

The neighbor count stays fixed, and the circle reaches the kkth neighbor.

This flat example has enough candidates for all kk neighbors.

2. Match velocity and stay together

Aialign=J(v‾Ni−vi),Aicohere=C(x‾Ni−xi).\mathbf A_i^{\rm align}=J(\overline{\mathbf v}_{\mathcal N_i}-\mathbf v_i),\qquad \mathbf A_i^{\rm cohere}=C(\overline{\mathbf x}_{\mathcal N_i}-\mathbf x_i).

The first term reduces differences in speed and direction. Its strength JJ has units s−1\mathrm{s^{-1}}. The second pulls toward the neighbors’ average position, with CC in s−2\mathrm{s^{-2}}. Increasing the neighbor count changes the information available to the bird. Increasing these strengths makes it respond more strongly to that information.

Velocity-space diagram. The bird approaches its neighbors' fixed average velocity. The remaining speed-and-direction difference is 5.76 meters per second.Velocity spacemean velocityThe neighbors
Bird’s velocityNeighbor averageVelocity difference
Target magnitude: 8.64 m s−28.64\,\mathrm{m\,s^{-2}}
1.5 s−11.5\,\mathrm{s^{-1}}
0.0 s0.0\,\mathrm s

Neighbor velocities are held fixed; only the alignment term acts here.

3. Leave room for wings

Aiseparate=S∑j∈Ri(1−rijr0)xi−xjrij,rij=∣xi−xj∣.\mathbf A_i^{\rm separate}=S\sum_{j\in\mathcal R_i}\left(1-\frac{r_{ij}}{r_0}\right)\frac{\mathbf x_i-\mathbf x_j}{r_{ij}}, \qquad r_{ij}=|\mathbf x_i-\mathbf x_j|.

Ri\mathcal R_i contains nearby candidates within the personal-space radius r0r_0. Repulsion acts along the line between the two birds and decreases linearly to zero at r0r_0. Here S=10 m s−2S=10\,\mathrm{m\,s^{-2}}. Repulsion is evaluated for up to 24 candidates.

Separation is 0.60 meters, with personal-space radius 0.85 meters. Repulsion is 2.94 meters per second squared.Repulsiondistance
This birdNeighborRepulsion
0.60 m0.60\,\mathrm m
0.85 m0.85\,\mathrm m
Target magnitude: 2.94 m s−22.94\,\mathrm{m\,s^{-2}}

One pair shown. The full term sums nearby candidates within r0r_0.

4. Return toward the roost

Local cohesion keeps neighbors together; the roost response Ri\mathbf R_i gives them a place to return to. A bird starts returning after leaving radius RR and continues until it reaches 0.45R0.45R.

Ri=qiKR1+h^i⋅n^i2tanh⁡ ⁣[8(−n^i)⋅l^i]l^i.\mathbf R_i=q_iK_R\frac{1+\hat{\mathbf h}_i\cdot\hat{\mathbf n}_i}{2} \tanh\!\left[8(-\hat{\mathbf n}_i)\cdot\hat{\mathbf l}_i\right]\hat{\mathbf l}_i.

qiq_i is 1 during a return and 0 otherwise. h^i\hat{\mathbf h}_i is the horizontal heading, n^i\hat{\mathbf n}_i points horizontally from the roost to the bird, and l^i=(hiz,0,−hix)\hat{\mathbf l}_i=(h_{iz},0,-h_{ix}) points sideways. The dot products set the turn’s direction and strength; the hyperbolic tangent limits the command. The roost slider sets KRK_R in acceleration units. At zero, horizontal flight is unrestricted.

Outside R. Distance from the roost is 1.18 times R. Return state q_i is 1. The roost acceleration is perpendicular to the heading, with no distance-dependent strength.roostcenterReturn stateactivedistance from roost:
HeadingRoost accelerationRelease boundary
25∘25^\circ
Ri⋅h^i=0\mathbf R_i\cdot\hat{\mathbf h}_i=0Target magnitude: 8.56 m s−28.56\,\mathrm{m\,s^{-2}}

Snapshots with KR=9 m s−2K_R=9\,\mathrm{m\,s^{-2}}. The full flight controller converts this target into banking and motion.

5. Choose altitude and individual variation

Bi\mathbf B_i has only a vertical component. It strengthens when the predicted height two seconds ahead falls below 11 meters or above 34 meters, with a weaker preference for 20 meters. The remaining term, ηi\boldsymbol\eta_i, gives each bird a smoothly changing individual preference.

Altitude and noise equationsBiy=1.6 s−2max⁡(11 m−yi−2 s viy,0)−1.2 s−2max⁡(yi+2 s viy−34 m,0)+0.015 s−2(20 m−yi)−0.025 s−1viy.B_{iy}=1.6\,\mathrm{s^{-2}}\max(11\,\mathrm m-y_i-2\,\mathrm s\,v_{iy},0) -1.2\,\mathrm{s^{-2}}\max(y_i+2\,\mathrm s\,v_{iy}-34\,\mathrm m,0) +0.015\,\mathrm{s^{-2}}(20\,\mathrm m-y_i)-0.025\,\mathrm{s^{-1}}v_{iy}.η′=e−h/tcη+σ1−e−2h/tc ξ,tc=1.8 s.\eta'=e^{-h/t_c}\eta+\sigma\sqrt{1-e^{-2h/t_c}}\,\xi,\qquad t_c=1.8\,\mathrm s.

Here η\eta is one component of the current individual acceleration preference, and η′\eta' is its value after the next perception update. Each component follows this update independently. hh is the elapsed time between perception updates, ξ\xi is a seeded standard normal sample, and σ\sigma sets the long-time standard deviation of each component. The exponential factor retains some of the preceding preference over correlation time tct_c.

Current height 15 meters, vertical speed -4 meters per second. Predicted height after two seconds is 7 meters. Vertical acceleration target is 6.58 meters per second squared.nowahead
Current heightPredicted heightAltitude acceleration
15.0 m15.0\,\mathrm m
−4.00 m s−1-4.00\,\mathrm{m\,s^{-1}}
yahead=yi+(2 s)viyy_{\rm ahead}=y_i+(2\,\mathrm s)v_{iy}Biy=6.58 m s−2B_{iy}=6.58\,\mathrm{m\,s^{-2}} (up)

The open circle extrapolates the current vertical velocity. The full trajectory also depends on the other terms.

6. Respond to a local disturbance

The disturbance response is a temporary preference of direction. A patch near the flock’s edge receives a shared turn command, sometimes accompanied by a dive.

ei(t)={aisin⁡[π(t−ti)/Ti],ti≤t<ti+Ti,0,otherwise,wi=1−0.95ei.e_i(t)=\begin{cases}a_i\sin[\pi(t-t_i)/T_i],&t_i\le t<t_i+T_i,\\0,&\text{otherwise},\end{cases}\qquad w_i=1-0.95e_i.Ei=ei[(12 m s−2)sil^i−(7 m s−2)diy^].\mathbf E_i=e_i\left[(12\,\mathrm{m\,s^{-2}})s_i\hat{\mathbf l}_i-(7\,\mathrm{m\,s^{-2}})d_i\hat{\mathbf y}\right].

Each pulse starts at tit_i, has amplitude aia_i, and lasts TiT_i. Here si=±1s_i=\pm1 chooses the turn direction and did_i sets the downward component. Alignment and cohesion are multiplied by wiw_i during the maneuver; separation and environmental responses remain active.

An eligible bird copies the strongest active maneuver among its social neighbors after a delay, with reduced amplitude. The response weakens as it spreads.

A seven-bird chain with 0.20 seconds between copies and 96 percent amplitude retention. Current time 0.0 seconds. Curves compare the first, middle and late turners A, D and G.Response strengthtime since the first response
First turner (AA)Middle turner (DD)Late turner (GG)
0.20 s0.20\,\mathrm s
0.0 s0.0\,\mathrm s
an+1=0.96 ana_{n+1}=0.96\,a_nGG starts 1.20 s1.20\,\mathrm s after AA; peak 78%78\% of AA’s.

Stationary birds with a shared 3.2 s3.2\,\mathrm s pulse duration and delay. The flock samples both. These illustrate the model, not experimental data.

Putting those responses together gives the bird equation shown in the controls:

Ai=wiJ(v‾Ni−vi)⏟alignment+wiC(x‾Ni−xi)⏟cohesion+S∑j∈Ri(1−rij/r0)xi−xjrij⏟separation+Ei⏟disturbance+Ri⏟roost+Bi⏟altitude+ηi⏟variation\mathbf A_i=\underbrace{w_iJ(\overline{\mathbf v}_{\mathcal N_i}-\mathbf v_i)}_{\text{alignment}}+\underbrace{w_iC(\overline{\mathbf x}_{\mathcal N_i}-\mathbf x_i)}_{\text{cohesion}}+\underbrace{S\sum_{j\in\mathcal R_i}(1-r_{ij}/r_0)\frac{\mathbf x_i-\mathbf x_j}{r_{ij}}}_{\text{separation}}+\underbrace{\mathbf E_i}_{\text{disturbance}}+\underbrace{\mathbf R_i}_{\text{roost}}+\underbrace{\mathbf B_i}_{\text{altitude}}+\underbrace{\boldsymbol\eta_i}_{\text{variation}}
Details about implementation

A bird cannot instantly adopt the entire requested acceleration. First separate the turning part from the part that changes speed. The matrix Pi=I−u^iu^iT\mathsf P_i=\mathsf I-\hat{\mathbf u}_i\hat{\mathbf u}_i^{\mathsf T} removes the component along the heading; I\mathsf I is the identity matrix. Define cap⁡a(w)=wmin⁡(1,a/∣w∣)\operatorname{cap}_a(\mathbf w)=\mathbf w\min(1,a/|\mathbf w|) for a nonzero vector, and zero for a zero vector. This limits its length without changing its direction.

Ti=cap⁡amax⁡(PiAi),bin+1=bin+(1−e−Δt/τ)(Ti−bin).\mathbf T_i=\operatorname{cap}_{a_{\max}}(\mathsf P_i\mathbf A_i),\qquad \mathbf b_i^{n+1}=\mathbf b_i^n+(1-e^{-\Delta t/\tau})(\mathbf T_i-\mathbf b_i^n).

Ti\mathbf T_i is the limited turning target, bi\mathbf b_i is the stored steering acceleration, and superscript nn labels the current step. Over one step Δt=1/30 s\Delta t=1/30\,\mathrm s, steering moves partway toward the target. The slider τ\tau controls how gradually it responds.

Banking changes the flight path

Let l^i\hat{\mathbf l}_i point sideways and e^u,i=u^i×l^i\hat{\mathbf e}_{u,i}=\hat{\mathbf u}_i\times\hat{\mathbf l}_i point upward perpendicular to the heading. The stored steering projected onto these directions gives requests blb_l and bub_u. A reduced pitch controller limits the upward request to keep ordinary flight away from vertical aerobatics:

θi=arcsin⁡(uiy),bu′=clip⁡[κ(−θ0−θi), κ(θ0−θi)](bu),θ0=0.55,κ=20 m s−2.\theta_i=\arcsin(u_{iy}),\qquad b_u'=\operatorname{clip}_{[\kappa(-\theta_0-\theta_i),\,\kappa(\theta_0-\theta_i)]}(b_u),\quad \theta_0=0.55,\quad \kappa=20\,\mathrm{m\,s^{-2}}.

Angles are in radians. With g=9.81 m s−2g=9.81\,\mathrm{m\,s^{-2}} and Vi=bu′+geu,iyV_i=b_u'+g e_{u,iy}, the requested bank and lift per unit mass are

ϕi∗=clip⁡[−1.03,1.03]atan2⁡[bl,max⁡(0.1g,Vi)],ℓi∗=clip⁡[0,2.2g](Vicos⁡ϕi∗).\phi_i^*=\operatorname{clip}_{[-1.03,1.03]}\operatorname{atan2}[b_l,\max(0.1g,V_i)],\qquad \ell_i^*=\operatorname{clip}_{[0,2.2g]}\left(\frac{V_i}{\cos\phi_i^*}\right).

The actual bank ϕi\phi_i and specific lift ℓi\ell_i relax toward these requests by the same exponential update used above. The response time is 0.22 seconds when rolling farther into a bank, 0.44 seconds when rolling out, and 0.65 seconds for lift. Because lift lags behind banking, a turn can initially lose height. The resulting turning acceleration is

f⊥,i=ℓisin⁡ϕi l^i+(ℓicos⁡ϕi−geu,iy)e^u,i.\mathbf f_{\perp,i}=\ell_i\sin\phi_i\,\hat{\mathbf l}_i+ (\ell_i\cos\phi_i-g e_{u,iy})\hat{\mathbf e}_{u,i}.

This resolves lift and the perpendicular component of gravity. The speed controller below stands in for propulsion and drag, including compensation for gravity along the heading. This is not a complete aerodynamic model.

a∥,i=clip⁡[−as,as][v0−∣vi∣τv+u^i⋅Ai],τv=1.25 s,as=2.5 m s−2.a_{\parallel,i}=\operatorname{clip}_{[-a_s,a_s]}\left[\frac{v_0-|\mathbf v_i|}{\tau_v}+\hat{\mathbf u}_i\cdot\mathbf A_i\right], \qquad \tau_v=1.25\,\mathrm s,\quad a_s=2.5\,\mathrm{m\,s^{-2}}.

a∥,ia_{\parallel,i} changes speed along the heading. The first contribution relaxes speed toward the preferred value v0v_0; the second preserves the forward social response, so a bird can slow behind another or catch up. The clip operation limits this acceleration to the interval shown.

vi∗=vin+Δt[f⊥,in+1+a∥,iu^i],xin+1=xin+Δt vin+1.\mathbf v_i^*=\mathbf v_i^n+\Delta t[\mathbf f_{\perp,i}^{n+1}+a_{\parallel,i}\hat{\mathbf u}_i], \qquad \mathbf x_i^{n+1}=\mathbf x_i^n+\Delta t\,\mathbf v_i^{n+1}.

Before advancing position, the candidate velocity vi∗\mathbf v_i^* is rescaled to the intended speed clip⁡[0.64v0,1.36v0](∣vin∣+Δt a∥,i)\operatorname{clip}_{[0.64v_0,1.36v_0]}(|\mathbf v_i^n|+\Delta t\,a_{\parallel,i}). Its total change from the preceding velocity is then limited to amax⁡Δta_{\max}\Delta t, with amax⁡=12 m s−2a_{\max}=12\,\mathrm{m\,s^{-2}}. That final limit also applies after a slider change. This discrete rule defines the browser model.

Roost and initial conditions

The default roost radius is

R=45 m+1.5E,E=10 m [max⁡(N,80)/3000]1/3R=45\,\mathrm m+1.5E,\quad E=10\,\mathrm m\,[\max(N,80)/3000]^{1/3}

Here EE sets the initial cloud’s size. The roost center is (−25,0,−30) m(-25,0,-30)\,\mathrm m; the cloud begins near (0,20,0) m(0,20,0)\,\mathrm m, avoiding a symmetric launch directly away from the center. An exactly radial heading uses a small seeded left-or-right choice to break the tie. The return memory and smooth turn command are inspired by StarDisplay’s heading-dependent roost attraction.

Disturbance timing

The first disturbance occurs after five simulated seconds; subsequent intervals are chosen uniformly between 40 and 60 seconds. A randomly oriented edge of the flock receives the disturbance within radius max⁡(2 m,0.42E)\max(2\,\mathrm m,0.42E).

Initial pulse amplitudes range from 0.75 to 1, and durations from 2.8 to 3.6 seconds. Some events include a dive whose strength varies with position within the affected patch. Copying introduces a delay of 0.12–0.28 seconds and retains 96% of the donor’s amplitude, stopping below amplitude 0.25. A bird responds at most once to the same disturbance, then waits five seconds after its maneuver before responding to another. Pending responses cannot propagate until their start time, so updating birds in an array does not transmit a disturbance instantly.

The shortcut for large flocks

At 20,000, 50,000, and 100,000 individuals, three-meter grid cells replace the tree. The search examines at most 64 candidates from 27 adjacent cells, with at most 12 from any one cell. The nearest 24 of those candidates supply the same force terms. They don't need to be the globally nearest birds. This is my spherical cow for your computer’s sake.

Perception updates every two physics steps below 10,000 birds, every four at 10,000 and 20,000, and every ten at 50,000 and 100,000. From 10,000 birds upward, this work is staggered across birds, while all positions and velocities advance at 30 Hz in simulation time. Noise uses the corresponding elapsed perception time. Because the search, sampling interval, and roost size vary, these modes are not all-else-controlled-for finite-size comparisons.

Playback runs at 1.5× speed: one second on screen advances the model by 1.5 seconds. The fixed timestep and all physical parameters stay the same; the worker performs more steps per second of wall-clock time.

The update loop

Every target uses the same old positions and velocities; a disturbance copied in this pass starts in the future. Otherwise, birds later in the loop would effectively see into the future.

every 1/30 second of simulation time (1.5x playback):
    update local disturbance pulses and any new disturbance
    on a perception step:
        organize current positions for the neighbor search
        for each bird due to update its perception:
            find up to 24 nearby candidates
            average the nearest k positions and velocities
            schedule delayed copying of a nearby maneuver, if eligible
            reduce social response during escape; add its turn/dive command
            add repulsion from candidates within personal space
            update its return-to-roost state
            add roost turning, altitude response, and correlated noise
            store the target acceleration

    for each bird, using the same previous state:
        separate target acceleration into sideways and forward parts
        limit the sideways target and gradually adjust stored steering
        update bank angle and lift toward the steering request
        resolve lift and gravity to obtain turning acceleration
        combine the forward target with preferred-speed regulation
        update velocity, limiting speed and total velocity change

    for each bird:
        advance position with its new velocity
    send a snapshot to the display

while the panel is visible and playing, up to 120 frames per second:
    interpolate between completed simulation states on the GPU
    follow the flock with the camera
    draw shared bird meshes, or small points for 50,000 and above

Read the movement rules · Read the timing loop

Both the drawing and physics pause while the simulation is offscreen, so reading about birds gives the computer a rest.

And, because there are technically no feathers on our bird…

Behold! Plato’s Man!

This here sparrow has a 24-centimeter wingspan and is made of 882 triangles. It was made with OpenAI’s GPT-6 Astra using the Computer Use tool in Blender, before I learned of the obsession the bird-watching physicists have with the starling.

Preparing the sparrow…
House sparrow · 24 cm wingspan

As you can see by starting its wingbeat, this poor featherless bird's skull deforms with every never-ending flap of its wings. And with a few thousand more, we have our flock.

References & further reading