<canvas id="canvas"></canvas>
<h1>Happy Halloween!</h1>
@import url(https://fonts.googleapis.com/css?family=Euphoria+Script);

html, body{
  width: 100%;
  height: 100%;
  background: #ff8833;
  overflow: hidden;
  text-rendering: optimizeLegibility;
}

canvas{
  display: block;
  background: #ff8833;
}

h1 {
  position: absolute;
  z-index: 1;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  height: 80px;
  text-align: center;
  margin: auto;
  font-family: 'Euphoria Script';
  font-size: 80px;
  color: rgba(104, 54, 0, 0.6);
}
// A simple experiment regarding birds flocking.

var Vector = (function () {
    function Vector(x, y) {
        if (typeof x === "undefined") { x = 0; }
        if (typeof y === "undefined") { y = 0; }
        this.x = x;
        this.y = y;
    }
    Vector.prototype.clone = function () {
        return new Vector(this.x, this.y);
    };
    Vector.prototype.zero = function () {
        this.x = this.y = 0;
        return this;
    };
    Object.defineProperty(Vector.prototype, "zeroed", {
        get: function () {
            return (this.x === 0 && this.y === 0);
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Vector.prototype, "length", {
        get: function () {
            return Math.sqrt(this.lengthSq);
        },
        set: function (value) {
            var a = this.angle;
            this.x = Math.cos(a) * value;
            this.y = Math.sin(a) * value;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Vector.prototype, "lengthSq", {
        get: function () {
            return this.x * this.x + this.y * this.y;
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Vector.prototype, "angle", {
        get: function () {
            return Math.atan2(this.y, this.x);
        },
        set: function (value) {
            var length = this.length;
            this.x = Math.cos(value) * length;
            this.y = Math.sin(value) * length;
        },
        enumerable: true,
        configurable: true
    });
    Vector.prototype.normalize = function () {
        if(this.length === 0) {
            this.x = 1;
            return this;
        }
        var length = this.length;
        this.x /= length;
        this.y /= length;
        return this;
    };
    Vector.prototype.truncate = function (max) {
        this.length = Math.min(max, this.length);
        return this;
    };
    Vector.prototype.reverse = function () {
        this.x = -this.x;
        this.y = -this.y;
        return this;
    };
    Object.defineProperty(Vector.prototype, "normalized", {
        get: function () {
            return this.length == 1;
        },
        enumerable: true,
        configurable: true
    });
    Vector.prototype.dotProduct = function (vector) {
        return this.x * vector.x + this.y * vector.y;
    };
    Vector.angleBetween = function angleBetween(vector1, vector2) {
        if(!vector1.normalized) {
            vector1 = vector1.clone().normalize();
        }
        if(!vector2.normalized) {
            vector2 = vector2.clone().normalize();
        }
        return Math.acos(vector1.dotProduct(vector2));
    };
    Object.defineProperty(Vector.prototype, "perpendicular", {
        get: function () {
            return new Vector(-this.y, this.x);
        },
        enumerable: true,
        configurable: true
    });
    Vector.prototype.sign = function (vector) {
        return this.perpendicular.dotProduct(vector) < 0 ? -1 : 1;
    };
    Vector.prototype.dist = function (vector) {
        return Math.sqrt(this.distSq(vector));
    };
    Vector.prototype.distSq = function (vector) {
        var dx = vector.x - this.x;
        var dy = vector.y - this.y;
        return dx * dx + dy * dy;
    };
    Vector.prototype.add = function (vector) {
        return new Vector(this.x + vector.x, this.y + vector.y);
    };
    Vector.prototype.subtract = function (vector) {
        return new Vector(this.x - vector.x, this.y - vector.y);
    };
    Vector.prototype.multiply = function (value) {
        return new Vector(this.x * value, this.y * value);
    };
    Vector.prototype.divide = function (value) {
        return new Vector(this.x / value, this.y / value);
    };
    Vector.prototype.equals = function (vector) {
        return this.x === vector.x && this.y === vector.y;
    };
    return Vector;
})();
var Vehicle = (function () {
    function Vehicle() {
        this.mass = 1.0;
        this.maxSpeed = 10;
        this.position = new Vector();
        this.velocity = new Vector();
    }
    Vehicle.WRAP = "wrap";
    Vehicle.BOUNCE = "bounce";
    Vehicle.prototype.draw = function (context) {
        context.save();
        context.translate(this.position.x, this.position.y);
        context.rotate(this.velocity.angle);
      
        //This is where I draw them as candy corn :)
        context.fillStyle = "#ff4814";
        context.beginPath();
        context.moveTo(this.size, 0);
        context.lineTo(-this.size, this.size / 2);
        context.lineTo(-this.size, -this.size / 2);
        context.lineTo(this.size, 0);
        context.fill();
      
        context.fillStyle = "#ffcd2d";
        context.beginPath();
        context.moveTo(this.size, 0);
        context.lineTo(-this.size * 0.5, this.size / 2 * 0.8);
        context.lineTo(-this.size * 0.5, -this.size / 2 * 0.8);
        context.lineTo(this.size, 0);
        context.fill();

        context.fillStyle = "#eeeeee";
        context.beginPath();
        context.moveTo(this.size, 0);
        context.lineTo(Math.floor(-this.size * 0.1) + 7, this.size / 2 * 0.3);
        context.lineTo(Math.floor(-this.size * 0.1) + 7, -this.size / 2 * 0.3);
        context.lineTo(this.size, 0);
        context.fill();

        context.restore();
    };
    Vehicle.prototype.update = function (width, height) {
        this.velocity = this.velocity.truncate(this.maxSpeed);
        this.position = this.position.add(this.velocity);
        if(this.edgeBehavior == Vehicle.WRAP) {
            this.wrap(width, height);
        } else if(this.edgeBehavior == Vehicle.BOUNCE) {
            this.bounce(width, height);
        }
    };
    Vehicle.prototype.bounce = function (width, height) {
        if(this.position.x > width) {
            this.position.x = width;
            this.velocity.x *= -1;
        } else if(this.position.x < 0) {
            this.position.x = 0;
            this.velocity.x *= -1;
        } else if(this.position.y > height) {
            this.position.y = height;
            this.velocity.y *= -1;
        } else if(this.position.y < 0) {
            this.position.y = 0;
            this.velocity.y *= -1;
        }
    };
    Vehicle.prototype.wrap = function (width, height) {
        if(this.position.x > width) {
            this.position.x = 0;
        }
        if(this.position.x < 0) {
            this.position.x = width;
        }
        if(this.position.y > height) {
            this.position.y = 0;
        }
        if(this.position.y < 0) {
            this.position.y = height;
        }
    };
    return Vehicle;
})();
var __extends = this.__extends || function (d, b) {
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var SteeringVehicle = (function (_super) {
    __extends(SteeringVehicle, _super);
    function SteeringVehicle() {
        _super.call(this);
        this.maxForce = 1;
        this.arrivalThreshold = 100;
        this.wanderRadius = 5;
        this.wanderAngle = 0;
        this.wanderRange = 1;
        this.wanderDistance = 10;
        this.avoidDistance = 300;
        this.avoidBuffer = 20;
        this.pathIndex = 0;
        this.pathThreshold = 20;
        this.tooCloseDist = 60;
        this.inSightDist = 200;
        this.steeringForce = new Vector();
    }
    SteeringVehicle.prototype.seek = function (target) {
        var desiredVelocity = target.subtract(this.position);
        desiredVelocity.normalize();
        desiredVelocity = desiredVelocity.multiply(this.maxSpeed);
        var force = desiredVelocity.subtract(this.velocity);
        this.steeringForce = this.steeringForce.add(force);
    };
    SteeringVehicle.prototype.flee = function (target) {
        var desiredVelocity = target.subtract(this.position);
        desiredVelocity.normalize();
        desiredVelocity = desiredVelocity.multiply(this.maxSpeed);
        var force = desiredVelocity.subtract(this.velocity);
        this.steeringForce = this.steeringForce.subtract(force);
    };
    SteeringVehicle.prototype.arrive = function (target) {
        var desiredVelocity = target.subtract(this.position);
        desiredVelocity.normalize();
        var dist = this.position.dist(target);
        if(dist > this.arrivalThreshold) {
            desiredVelocity = desiredVelocity.multiply(this.maxSpeed);
        } else {
            desiredVelocity = desiredVelocity.multiply(this.maxSpeed * dist / this.arrivalThreshold);
        }
        var force = desiredVelocity.subtract(this.velocity);
        this.steeringForce = this.steeringForce.add(force);
    };
    SteeringVehicle.prototype.pursue = function (target) {
        var lookAheadTime = this.position.dist(target.position) / this.maxSpeed;
        var predictedTarget = target.position.add(target.velocity.multiply(lookAheadTime));
        this.seek(predictedTarget);
    };
    SteeringVehicle.prototype.evade = function (target) {
        var lookAheadTime = this.position.dist(target.position) / this.maxSpeed;
        var predictedTarget = target.position.add(target.velocity.multiply(lookAheadTime));
        this.flee(predictedTarget);
    };
    SteeringVehicle.prototype.wander = function () {
        var center = this.velocity.clone().normalize().multiply(this.wanderDistance);
        var offset = new Vector();
        offset.length = this.wanderRadius;
        offset.angle = this.wanderAngle;
        this.wanderAngle += Math.random() * this.wanderRange - this.wanderRange * 0.5;
        var force = center.add(offset);
        this.steeringForce = this.steeringForce.add(force);
    };
    SteeringVehicle.prototype.avoid = function (targets) {
        for(var i = 0, l = targets.length; i < l; i++) {
            var target = targets[i];
            var heading = this.velocity.clone().normalize();
            var difference = target.position.subtract(this.position);
            var dotProd = difference.dotProduct(heading);
            if(dotProd > 0) {
                var feeler = heading.multiply(this.avoidDistance);
                var projection = heading.multiply(dotProd);
                var dist = projection.subtract(difference).length;
                if(dist < target.size + this.avoidBuffer && projection.length < feeler.length) {
                    var force = heading.multiply(this.maxSpeed);
                    force.angle += difference.sign(this.velocity) * Math.PI / 2;
                    force = force.multiply(1 - projection.length / feeler.length);
                    this.steeringForce = this.steeringForce.add(force);
                    this.velocity = this.velocity.multiply(projection.length / feeler.length);
                }
            }
        }
    };
    SteeringVehicle.prototype.path = function (path, loop) {
        if (typeof loop === "undefined") { loop = false; }
        var point = path[this.pathIndex];
        if(point == null) {
            return;
        }
        if(this.position.dist(point) < this.pathThreshold) {
            if(this.pathIndex >= path.length - 1) {
                if(loop) {
                    this.pathIndex = 0;
                }
            } else {
                this.pathIndex++;
            }
        }
        if(this.pathIndex >= path.length - 1 && !loop) {
            this.arrive(point);
        } else {
            this.seek(point);
        }
    };
    SteeringVehicle.prototype.flock = function (flock) {
        var averageVelocity = this.velocity.clone();
        var averagePosition = new Vector();
        var inSightCount = 0;
        for(var i = 0, l = flock.length; i < l; i++) {
            var vehicle = flock[i];
            if(vehicle != this && this.inSight(vehicle)) {
                averageVelocity = averageVelocity.add(vehicle.velocity);
                averagePosition = averagePosition.add(vehicle.position);
                if(this.tooClose(vehicle)) {
                    this.flee(vehicle.position);
                }
                inSightCount++;
            }
        }
        if(inSightCount > 0) {
            averageVelocity = averageVelocity.divide(inSightCount);
            averagePosition = averagePosition.divide(inSightCount);
            this.seek(averagePosition);
            this.steeringForce.add(averageVelocity.subtract(this.velocity));
        }
    };
    SteeringVehicle.prototype.inSight = function (vehicle) {
        if(this.position.dist(vehicle.position) > this.inSightDist) {
            return false;
        }
        var heading = this.velocity.clone().normalize();
        var difference = vehicle.position.subtract(this.position);
        var dotProd = difference.dotProduct(heading);
        if(dotProd < 0) {
            return false;
        }
        return true;
    };
    SteeringVehicle.prototype.tooClose = function (vehicle) {
        return this.position.dist(vehicle.position) < this.tooCloseDist;
    };
    SteeringVehicle.prototype.update = function (width, height) {
        this.steeringForce = this.steeringForce.truncate(this.maxForce);
        this.steeringForce = this.steeringForce.divide(this.mass);
        this.velocity = this.velocity.add(this.steeringForce);
        this.steeringForce = new Vector();
        _super.prototype.update.call(this, width, height);
    };
    return SteeringVehicle;
})(Vehicle);
var Playground = (function () {
    function Playground() {
        this.mouseX = 0;
        this.mouseY = 0;
        this.pinned = true;
        this.color = "#ffffff";
        var canvas = document.querySelector("#canvas");
        this.context = canvas.getContext("2d");
        this.context.lineWidth = 1;
        this.resize();
        this.init();
        window.addEventListener('resize', this.resize.bind(this));
        this.animate();
    }
    Playground.prototype.init = function () {
        this.birds = [];
        this.flockQuantity = 25; //Number of boids
        for(var i = 0; i < this.flockQuantity; i++) {
            this.birds[i] = new SteeringVehicle();
            this.birds[i].size = 20; //Size of boids
            this.birds[i].position = new Vector(Math.random() * this.width, Math.random() * this.height);
            this.birds[i].mass = 2; //Boid mass
            this.birds[i].velocity.length = 3;
            this.birds[i].velocity.angle = Math.PI / 5;
            this.birds[i].edgeBehavior = Vehicle.WRAP;
        }
    };
    Playground.prototype.onMouseMove = function (e) {
        this.mouseX = e.clientX;
        this.mouseY = e.clientY;
    };
    Playground.prototype.resize = function () {
        this.width = window.innerWidth;
        this.height = window.innerHeight;
        this.context.canvas.width = this.width;
        this.context.canvas.height = this.height;
    };
    Playground.prototype.animate = function () {
        this.context.clearRect(0, 0, this.width, this.height);
        this.context.fillStyle = this.context.strokeStyle = this.color;
        this.context.globalCompositeOperation = "source-over";
        for(var i = 0; i < this.flockQuantity; i++) {
            this.birds[i].flock(this.birds);
            this.birds[i].update(this.width, this.height);
            this.birds[i].draw(this.context);
        }
        requestAnimationFrame(this.animate.bind(this));
    };
    return Playground;
})();
var Run = (function () {
    function Run() {
        var playground = new Playground();
    }
    return Run;
})();
var run = new Run();

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://raw.github.com/GoodBoyDigital/pixi.js/master/bin/pixi.js