Particle Effects: Adding Texture To Your Canvas Game
This is part 4, the conclusion to a series that details our team’s journey to creating disasteroids.com for NodeKnockout 2012, a 48 hour coding competition that showcases projects built on node.js. You can find part one here, part two here, and part three here.
We Need Eye Candy!
Once we got the basic physics and rendering in place it was time to figure out how to make the explosions and rocket trails. We initially had something like Scorched Earth in mind where the rockets draw a permanent trail, but we weren’t tied to the idea. After some quick hunting around we came across Javascript Particle System by @jsoverson and we were sold. It’s more of a demo than a library meant for reuse, so there are no docs. Luckily it was written pretty well and it wasn’t too hard to discern how to use it and pull out the functionality we needed.
There are six objects at the core of the particle system.
- Display - Wrapper for canvas element
- ParticleSystem - This runs the show and tells all the other element what to do
- Vector - A wrapper for x,y coords with some convenience methods
- Particle - represents an individual particle
- Field - Basically a source of gravity. negative gravity is repulsion.
- Emitter - Emits particles
Initialization
Basically how it works is you wrap a canvas element in a Display object which you then pass into ParticleSystem’s init like so:
// wrap canvas with Display and initialize var display = new Display(this.canvas); display.init(); // Initialize ParticleSystem and init with display var particleSystem = new ParticleSystem().init(display); // set an upper limit for total number of particles particleSystem.maxParticles = 10000; // start display.start();
Missile Thruster Particle Trail
Once this is done all you need to do is create an Emitter and push into the appropriate array and the particle system will do the rest. for example, here is a piece the render method for a missile:
render: function(canvas, particleSystem) {
var self = this;
// play shoot sound
app.playAudio('rocketShoot');
// create local handle for particle system
this.particleSystem = particleSystem;
// create new emitter called thruster
this.thruster = new Emitter();
// set parameters
this.thruster.position = new Vector(self.model.get('x'), self.model.get('y'));
this.thruster.velocity = Vector.fromAngle(self.model.get('angle')*180/Math.PI, 5);
this.thruster.size = 0;
this.thruster.particleLife = 100;
this.thruster.spread = .5;
this.thruster.emissionRate = 0.5;
this.thruster.jitter = 0;
this.thruster.particleColor = [255,130,0,1];
// push emitter into particle system
particleSystem.emitters.push(this.thruster);
}
This starts shooting particles out of the back of the missile. Since we already have all the math done to update the missiles position and angle, we can simple hook into that in the missiles reposition method.
reposition: function(model) {
var self = this;
// reposition the missile
this.object.set('left',self.model.get('x'));
this.object.set('top', self.model.get('y'));
this.object.set('angle', self.model.get('angle')*180/Math.PI);
// reposition the thruster emitter
var thrusterVector = Vector.fromAngle(self.model.get('angle') + Math.PI/2, self.model.get('height')/2);
this.thruster.position = new Vector(self.model.get('x') + thrusterVector.x, self.model.get('y') + thrusterVector.y);
// update the angle so it is always shooting out the tail of the missile
// velocity takes a vector that governs the angle as well as the speed
this.thruster.velocity = Vector.fromAngle(self.model.get('angle') + Math.PI/2, 2);
}
That gives us a missile with a particle trail!

Explosions and Blasts
Now we just need to handle what happens when the missile hits something. Basically what we want to do is:
- remove the truster emitter
- create an explosion emitter
- create a repulsion field so the particles don’t just emit in a circle but get blasted away
- wait a bit and remove the explosion and repulsion field.
exit: function() {
var self = this;
// play impact sound
app.playAudio('rocketImpact');
// remove thruster
this.particleSystem.removeEmitter(this.thruster);
// create explosion emitter and set params
this.explosion = new Emitter();
this.explosion.position = new Vector(this.model.get('x'), this.model.get('y'));
this.explosion.velocity = new Vector(0, 5);
this.explosion.size = 0;
this.explosion.particleColor = [255,50,0,1];
this.explosion.spread = 50;
this.explosion.emissionRate = 10;
this.explosion.particleLife = 7;
// create repulsion field and set params
// remember negative gravity will repel
this.repulsion = new Field(this.explosion.position, -1000);
this.repulsion.size = 0;
// push explosion and repulsion field into particleSystem
this.particleSystem.fields.push(this.repulsion);
this.particleSystem.emitters.push(this.explosion);
// wait 250ms and remove the explosion
setTimeout(function() {
self.particleSystem.removeEmitter(self.explosion);
}, 250);
// wait 1500ms and remove the repulsion field
// the wait is longer here so the particles can all get blown away
setTimeout(function() {
self.particleSystem.removeField(self.repulsion);
}, 1500);
}
That gives us a nice particle explosion that blasts away debris

The End
Thats the gist of how the particle physics implementation works. Thanks for taking time to read and we hope you have fun playing the game as well.
Note: We did have to customize the Javascript Particle System a bit to meet our needs. Primarily to allow for different sized particles and different colors. It also depended on require.js, which we removed as well. Here is the custom code on github. Thanks again to @jsoverson for making such a cool and extendable project.
VOTE!
If you enjoyed disasteroids.com or this post, please consider throwing us a vote on our team’s page. It requires Facebook authentication to ensure that there isn’t rampant cheating, but the awesome NodeKnockout organizers @visnup and @gerad would never use your information in a malicious way. They’ve been crazy enough to organize this event for the 3rd year now despite the operational headaches of putting together such a massive event.
