JavaScript Particle Engine

Introduction

Just a little while back, I stumbled across a few different posts about particle engines and the math behind them. I study Mechatronics Engineering at McMaster University and I have the math background require (which isn't really all that much) to build something similar to many of these posts I read. So I decided to give it a shot and write a short post about it.

This implementation will be a simple particle engine using the canvas object. The engine will implement a vector object that will handle the basic movement of everything, a particle object, emitters which emit particles, fields which cause a force to act on the particles, and a "player" object which allows you to move around with your mouse and interact with the particles.

In this particular case, the player object can be customized to have a mass (positive to attract and negative to repel) and will be shrunk by the blue particles and grow when coming in contact with the yellow ones.

The module dependencies were managed using require.js.

The Physics

The vector object is really the core of the engine itself. The vector requires an x and y position, some work with angles, and the ability to get the vector magnitude.


define(function(require, exports, module) {

    var Vector = function Vector(x, y) {
        this.x = x || 0;
        this.y = y || 0;
    }

    Vector.prototype.add = function(vector) {
        this.x += vector.x;
        this.y += vector.y;
    }

    Vector.prototype.getMagnitude = function () {
        return Math.sqrt(this.x * this.x + this.y * this.y);
    };

    Vector.prototype.getAngle = function () {
        return Math.atan2(this.y, this.x);
    };

    Vector.fromAngle = function (angle, magnitude) {
        return new Vector(magnitude * Math.cos(angle), magnitude * Math.sin(angle));
    };

    module.exports = Vector;

});

The movement of everything is done by adding or subtracting the vector objects of the object in question.

Since each player and field has a mass associated to it, a particle should be submitted to the field that would be associated with that mass. So each particle has a method that allows its acceleration to be subjected to the force of a player or field.


    Particle.prototype.submitToFields = function (fields) {

        var totalAccelerationX = 0;
        var totalAccelerationY = 0;

        for (var i = 0; i < fields.length; i++) {
            var field = fields[i];

            // find the distance between the particle and the field
            var vectorX = field.position.x - this.position.x;
            var vectorY = field.position.y - this.position.y;

            // calculate the force.
            var force = field.mass / Math.pow(vectorX*vectorX+vectorY*vectorY, 1.5);

            // add to the total acceleration the force adjusted by distance
            totalAccelerationX += vectorX * force;
            totalAccelerationY += vectorY * force;
        }

        // update our particle's acceleration
        this.acceleration = new Vector(totalAccelerationX, totalAccelerationY);
    };

Finally, the engine is started using a file which require.js will use as a starting point named main.js. This file has a game options object that is also exposed (to some extent) as the header of the html testing project so that you can change the options.


function GameOptions(numOfParticles, particleSize, emissionRate, objectSize, playerSize, playerMass, numberOfFields, numberOfEmitters) {
    this.maxParticles = numOfParticles || 20;
    this.particleSize = particleSize || 5;
    this.emissionRate = emissionRate || 1;
    this.objectSize = objectSize || 15;
    this.playerSize = playerSize || 15;
    this.playerMass = playerMass || 0;
    this.numberOfFields = numberOfFields || 1;
    this.numberOfEmitters = numberOfEmitters || 1;
}

var GameOptions = new GameOptions();

var game = function(GameOptions) {
    require(["Vector", "Particle", "Emitter", "Field", "Player", "Engine"], function(Vector, Particle, Emitter, Field, Player, Engine) {

        var newGame = new Engine(GameOptions);
        newGame.startGame();
    });
};

game(GameOptions);

Testing and Demo

The engine was sort of refactored to be a sudo small game engine since the player object would allow you to write some simple logic to have a fun game. Engine.js has most of the refactor logic and a lot of the general logic for the demo. In the future this should all be abstracted better to be re-used in an easier fashion. However, I was not setting out to write a game engine, just to have some fun!

Check out the demo here and get the source on my github.