See the full article for more.
The separation and cohesion rules come for "free" by attaching every particle to every other particle with a negative Attraction and a Spring. It's quite feasible that this could have been done with two Attractions, but the Spring has the useful method currentLength() which I used to detect which other particles are nearby in order to implement the alignment rule.
I made the simulation pseudo-3D by displaying the current Z-axis position of a particle as the colour of its stroke - more distant particles are lighter, closer ones are darker. Imagine looking down from above on a tank full of murky water which contains forty tadpoles*. The schools seem to emerge from the translucent water and then sink back into the depths.
The simulation is very sensitive to initial conditions and is therefore unstable when tinkered with. If you play with the values in the source code (change the number of tadpoles, change the strength of the attractions or the springs, etc) then the simulation may fall apart completely, or start to look much less realistic and natural. I arrived at the values through trial and error - they seemed to produce the best-looking effect. I'm sure a more stable simulation could be arrived at with further work.
- Disclaimer - I have no idea whether tadpoles actually move in schools ;-)