HTML5 Canvas Particles Fountain Explosion with Gravity

One of my previous posts talked about implementing gravity in our canvas experiments. Eventually, we made a realistic bouncing ball. We’ll kind of extend that experiment to make a quick fountain explosion on canvas obeying gravity.

Demo

I have made a demo as a codecast.

What's the one thing every developer wants? More screens! Enhance your coding experience with an external monitor to increase screen real estate.


Understanding the Logic

First, there’s all those basic code where we get the 2d context of canvas and set our gravity value along with particles count. We wrote a neat little Particle constructor that we instantiate inside a for loop as many times as the particle_count. The constructor holds properties for the x axis, y axis, radius, color, x velocity, y velocity and the method to draw the particle using the arc method on the canvas context.

	var canvas = document.querySelector('canvas');
	var ctx = canvas.getContext('2d');
	
	var W = canvas.width = window.innerWidth;
	var H = canvas.height = window.innerHeight;
	
	// Let's set our gravity
	var gravity = 0.5;
	
	// Time to write a neat constructor for our
	// particles.
	// Lets initialize a random color to use for
	// our particles and also define the particle
	// count.
	var particle_count = 20;
	var particles = [];
	
	var random_color = 'rgb(' +
			parseInt(Math.random() * 255) + ',' +
			parseInt(Math.random() * 255) + ',' +
			parseInt(Math.random() * 255) + ')';
	
	function Particle() {
		this.radius = 5;
		this.x = W / 2;
		this.y = H - this.radius;
		
		this.color = random_color;
		
		// Random Initial Velocities
		this.vx = Math.random() * 4 - 2;
		// vy should be negative initially
		// then only will it move upwards first
		// and then later come downwards when our
		// gravity is added to it.
		this.vy = Math.random() * -14 - 7;
		
		// Finally, the function to draw
		// our particle
		this.draw = function() {
			ctx.fillStyle = this.color;
			
			ctx.beginPath();
			
			ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
			ctx.fill();
			
			ctx.closePath();
		};
	}
	
	// Now lets quickly create our particle
	// objects and store them in particles array
	for (var i = 0; i < particle_count; i++) {
		var particle = new Particle();
		particles.push(particle);
	}

The initial velocities on both axis are randomly generated.

// Random Initial Velocities
this.vx = Math.random() * 4 - 2;
this.vy = Math.random() * -14 - 7;

It is important to note that vy must be negative initially. Then only will the particles move upwards when you add it to y coordinate, i.e., particle.y.

Finally, let’s talk a bit about the main part of the code that does the animation along with other tasks like applying gravity, repositioning the particles when they move off the canvas and clearing off the canvas to prevent the particle trails from showing up.

	// Finally, writing down the code to animate!
	(function renderFrame() {
		requestAnimationFrame(renderFrame);
		
		// Clearing screen to prevent trails
		ctx.clearRect(0, 0, W, H);
		
		particles.forEach(function(particle) {
			
			// The particles simply go upwards
			// It MUST come down, so lets apply gravity
			particle.vy += gravity;
			
			// Adding velocity to x and y axis
			particle.x += particle.vx;
			particle.y += particle.vy;
			
			// We're almost done! All we need to do now
			// is to reposition the particles as soon
			// as they move off the canvas.
			// We'll also need to re-set the velocities
			
			if (
				// off the right side
				particle.x + particle.radius > W ||
				// off the left side
				particle.x - particle.radius < 0 ||
				// off the bottom
				particle.y + particle.radius > H
			) {
				
				// If any of the above conditions are met
				// then we need to re-position the particles
				// on the base
				particle.x = W / 2;
				particle.y = H - particle.radius;
				
				// If we do not re-set the velocities then
				// the particles will stick to base
				
				// Velocity X
				particle.vx = Math.random() * 4 - 2;
				particle.vy = Math.random() * -14 - 7;
			}
			
			particle.draw();
		
		});
	}());

Initially, we clear the entire canvas in renderFrame so that the particles do not leave trails when moving. Then we forEach over our particles array and do some work on each particle.

Firstly, we add gravity to the y velocity turning it positive from negative, hence moving downwards when added to particle.y. The change to particle.x occurs at it’s constant velocity, i.e., particle.vx.

Now, as soon as the particle is off the left, right or bottom edges, it is brought back to it’s original position by re-setting the particle.x and particle.y.

particle.x = W / 2;
particle.y = H - particle.radius;

Just re-setting the x and y positions is not enough. The velocities also need to be reset else the if condition will keep on evaluating to true, and the particles are going to stick at their initial position, i.e., at bottom.

// Velocity X
particle.vx = Math.random() * 4 - 2;
// Velocity Y
particle.vy = Math.random() * -14 - 7;

Note, that the 4, 2, -14, 7 are all random integers to generate random negative and positive velocities. We could have also used Math.sin or Math.cos and multiplied the result by a similar integer to get a proper random negative/positive velocity. Just make sure that the Y velocity is always negative initially.

You can also think of these equations like, if you want a random number between Z and -Z, then you can multiple 2Z to Math.random() and subtract Z from the product.

It is extremely important to understand the conditions that we’ve placed inside our if statement, especially the reason behind why we add/subtract the radius to the x/y positions.

if (
	// off the right side
	particle.x + particle.radius > W ||
	// off the left side
	particle.x - particle.radius < 0 ||
	// off the bottom
	particle.y + particle.radius > H
) {

Consider this condition – particle.x + particle.radius > W. Why did we add particle.radius ? Because the x position is in the center of the ball, which means that if we just check for particle.x > W then there will be times where the ball has already protruded beyond the right edge. Similarly if we check for particle.x < 0 instead of particle.x - particle.radius < 0, then it'll hold true when the ball has already popped out off the left surface by more than half of it's width/diameter.

Still confused ? I am sure this figure will be helpful:
Particle Position on Canvas Edges

Final Words

We have a good idea regarding implementing gravity and also re-positioning objects in our experiments by now. Using these basic concepts, we can build proper realistic interactions in our demos and games.

You can expand on this idea to add more features like positioning the fountain origin at mouse or touch positions or even create more amazing stuffs like fireworks, etc.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Author: Rishabh

Rishabh is a full stack web and mobile developer from India. Follow me on Twitter.

Leave a Reply

Your email address will not be published. Required fields are marked *