As a shoal of fish moves it can seem to respond to its environment as a single entity, particularly when a predator approaches. This behaviour puzzled biologists for some time, but behavioural biologist Iain Couzin among others recently began to unlock some of the mechanics behind their movement. A model of it can be made by setting out the movement of individual members – no overarching mechanisms across the shoal are needed. Members of the shoal will roughly move as follows:

  1) Each member tends to keep a set distance from their neighbours.

  2) Each member keeps a similar orientation to that of their neighbours.

  3) Each member is only aware of their closest neighbours.

Of course there are other ways of moving depending on what is in their environment, for example if a predator approaches they’ll scram! The shoal’s basic movement can be described with the three principles above though, and a model following it will have movement similar to that of a shoal. So what do members need to do to stay part of the shoal? If they’re to keep a set distance from their neighbours then moving in a similar direction to them will help. They can look at how their neighbours are moving on average and adjust their own movement accordingly.


If a neighbour is too close they can also change their movement away from that neighbour.


And if a neighbour is too far away they can move closer to them.


An algorithm describing the movement of a shoal member might look something like the following.

neighbour_movement_vector = 0

for each close neighbour:
    add that neighbour's movement vector to neighbour_movement_vector

closest_neighbour = 0
closest_distance = -1

for each close neighbour:
    neighbour_distance = distance to neighbour
    if neighbour_distance < ideal neighbour distance:
        if neighbour_distance < closest_distance or closest_distance is -1:
            closest_neighbour = neighbour
            closest_distance = neighbour_distance

if closest_neighbour is not 0:
    collision_correction_vector = member's position - neighbour's position
    collision_correction_vector = 0

furthest_neighbour = 0
furthest_distance = -1

for each close neighbour:
    neighbour_distance = distance to neighbour
    if neighbour_distance > ideal neighbour distance:
        if neighbour_distance > furthest_distance or furthest_distance is -1:
            furthest_neighbour = neighbour
            furthest_distance = neighbour_distance

if furthest_neighbour is not 0:
    cohesion_correction_vector = neighbour's position - member's position
    cohesion_correction_vector = 0

normalise and scale neighbour_movement_vector
normalise and scale collision_correction_vector
normalise and scale cohesion_correction_vector

add neighbour_movement_vector to the member's movement vector
add collision_correction_vector to the member's movement vector
add cohesion_correction_vector to the member's movement vector

This algorithm was used to generate the movement in the video below.

Adding in predators and food and doing some general tinkering around, it’s possible to get more complex movement, as can be seen in the video below.

In the video below the shoal’s movement is reflected with sound. Each member makes a sound at a tone dependant on the closeness of their neighbours. The combination of all members doing this at once gives the sound depth.

The fact that shoal movement can be modelled using the actions of individual members in relation to a few close neighbours raises interesting questions about how we see the shoal. From the perspective of a shoal member it’s a fairly simple thing – all they have to do is keep track of how their immediate neighbours are moving and adjust their own movement accordingly. Yet we tend to see it as something broader and at times it can seem to react as a single entity. This disparity between the perspectives of an outside observer of the shoal and that of a member of it leads into an interesting area known as ’emergence’ which spans across several branches of science and philosophy.