coderholic

Javascript Boids

Boids are a way to simulate swarm behaviour using 3 simple rules. I'd previously implemented Boids in Python, and was really impressed with the results. When I heard about the javascript vector graphics library Raphaël I thought it would be a good opportunity to port my original code to javascript. If you just want to see the boids in action you can jump right to the boids demonstration.

At the heart of the boids implementation are the boid objects. The constructor function is shown below, and basically just sets up a starting position, size, and velocity, and creates an Raphaël circle:

var Boid = function(x, y, size) {
    this.x = x;
    this.y = y;

    this.xVelocity = 1;
    this.yVelocity = -1;

    this.circle = paper.circle(x, y, size).attr({fill: '#FF0000'});
}

Here's the boids move away function, which makes sure that the boids don't crowd each other:

Boid.prototype.moveAway = function(boids, minDistance) {
    var distanceX = 0;
    var distanceY = 0;
    var numClose = 0;

    for(var i = 0; i < boids.length; i++) {
        var boid = boids[i];
        
        if(boid.x == this.x && boid.y == this.y) continue;
        
        var distance = this.distance(boid)
        if(distance < minDistance) {
            numClose++;
            var xdiff = (this.x - boid.x);
            var ydiff = (this.y - boid.y);

            if(xdiff >= 0) xdiff = Math.sqrt(minDistance) - xdiff;
            else if(xdiff < 0) xdiff = -Math.sqrt(minDistance) - xdiff;

            if(ydiff >= 0) ydiff = Math.sqrt(minDistance) - ydiff;
            else if(ydiff < 0) ydiff = -Math.sqrt(minDistance) - ydiff;

            distanceX += xdiff;
            distanceY += ydiff;
        }
    }
    
    if(numClose == 0) return;
    
    this.xVelocity -= distanceX / 5;
    this.yVelocity -= distanceY / 5;
}

The move closer function, which causes the boids to attract one another:

Boid.prototype.moveCloser = function(boids, distance) {
    if(boids.length < 1) return             

    var avgX = 0;

    var avgY = 0;

    for(var i = 0; i < boids.length; i++) {

        var boid = boids[i];
        if(boid.x == this.x && boid.y == this.y) continue;
        if(this.distance(boid) > distance) continue;
        
        avgX += (this.x - boid.x);

        avgY += (this.y - boid.y);
    }
        

    avgX /= boids.length;
    avgY /= boids.length;

    distance = Math.sqrt((avgX * avgX) + (avgY * avgY)) * -1.0
    if(distance == 0) return;
    
    this.xVelocity= Math.min(this.xVelocity + (avgX / distance) * 0.15, maxVelocity)

    this.yVelocity = Math.min(this.yVelocity + (avgY / distance) * 0.15, maxVelocity)
}

And the move with function, which causes all the boids to generally move in the same direction:

Boid.prototype.moveWith = function(boids, distance) {
    if(boids.length < 1) return
    // calculate the average velocity of the other boids
    var avgX = 0;
    var avgY = 0;
    for(var i = 0; i < boids.length; i++) {
        var boid = boids[i];
        if(boid.x == this.x && boid.y == this.y) continue;
        if(this.distance(boid) > distance) continue;
        
        avgX += boid.xVelocity;
        avgY += boid.yVelocity;
    }

    avgX /= boids.length;
    avgY /= boids.length;

    distance = Math.sqrt((avgX * avgX) + (avgY * avgY)) * 1.0
    if(distance == 0) return;

    this.xVelocity= Math.min(this.xVelocity + (avgX / distance) * 0.05, maxVelocity);
    this.yVelocity = Math.min(this.yVelocity + (avgY / distance) * 0.05, maxVelocity);

}

With just those simple three rules some quite complex and realistic looking swarm behaviour emerges!

View the boids in action (tested on Firefox 3).

Download the source code: boids.js.

Posted on 17 Oct 2008
If you enjoyed reading this post you might want to follow @coderholic on twitter or browse though the full blog archive.