Home

Koi Pond

Instructions

Hold your pointer down to attract the fish.

Description

This visualization simulates a tranquil koi pond. The koi fish exhibit the behavior of schooling together. As you interact with the fish, they will be attracted to your pointer. Or, just sit back, relax, and watch the fish swim.

How it's made...

Schooling

This technique can be used for flocking birds, schooling fish, and even herds of buffalo. The algorithm was originally created by Craig Reynolds in 1986 who called it "Boids". ("Birds" with a Brooklyn accent.) This algorithm is characterized by three distinct rules:

This algorithm is detailed in many many many many places, so I won't reiterate it here.

Levers and Knobs

One part of the Flocking algorithm that I have not seen detailed very often is the tweaking required to acheive the intended behavior.

For example, the three components of the algorithm are calculated against "near by" entities. However, in my experience you can improve the results by using a different "near by" distance for each of the three components.

Also, instead of applying each of the three components equally, a component weight is applied to dial in the desired behavior. Since each of the three components is a normalized vector, the weights are just scale values.

Your reward for actually reading this far is that I will give you this link, which enables my debug options for the above simulation, allowing you to tweak the values yourself. Scroll up to see it under the instructions.

Steering

Another thing that is often only briefly mentioned, if at all, is what to do with this final vector once it has been calculated. The obvious thing to do is call that vector "velocity" and you are done. While this will produce good behavior in some circumstances, it's usually not quite what you would expect. This will produce movement that is absolute, and ignores some obvious physical properities you may wish to simulate. For example: when modeling a bird, and it tries to join a group of birds directly behind it, the bird will happily invert its direction mid-flight and head in the opposite direction. Unless you are modeling hummingbirds, this will look a bit odd.

The solution is to call the computed vector the "target velocity". Armed with this distinction, the implication is that the entity would like to travel in that direction/speed, but it gives us the chance to decide how it will get there over time. The first thing we want is a steering vector, which is simply the vector from our current velocity to the target velocity:

steering = targetVelocity - boid.velocity

In this case, I want to break the velocity component up into an acceleration/braking vector, and a left/right turning vector. This way I can control how much braking I am going to allow the fish to do. I certainly want to prefer turning over braking/reversing. Here's the simplified C++ code:

Vector2D calcSteering(const Boid &boid, const Vector2D &targetVelocity) {
    Vector2D steering = targetVelocity - boid.velocity;
    Vector2D acceleration = steering.project(boid.facing);
    Vector2D boidRight = Vector2D(boid.facing.y, -boid.facing.x);
    Vector2D turning = steering.project(boidRight);
    if (acceleration.dot(boid.facing) < 0.0f) {  // braking
        float braking = acceleration.magnitude();
        if (braking > BOID_MAX_BRAKING) {
            // redirect excess braking to turning.
            float excess = braking - BOID_MAX_BRAKING;
            acceleration = (acceleration / braking) * BOID_MAX_BRAKING;
            float turnMag = turning.magnitude();
            turning = (turning / turnMag) * (turnMag + excess);
        }
    }
    steering = turning + acceleration;
    steering.truncate(BOID_MAX_STEERING);
    return steering;
}
This kind of method would apply to any entity that prefers to maintain it's velocity rather than slam the breaks and reverse, like birds, fish, cars, spaceships, etc.

Lastly, this steering vector is applied to velocity as an acceleration, multiplied by delta time.

Webassembly

Like all visual simulations on this site, this project was programmed using C++, compiled with clang targeting Webassembly. Why C++ you ask? Because it allows me to manage and access memory in a more optimized way. It's challenging, as of this writing I am not aware of any proper debugging tools available... but I get by with console logging.