Tutorial: Particle Basics with HTML5 and Javascript

With the holidays coming up and the weather turning cooler, I thought it would be fitting to do a basic, winter-themed tutorial on particles with HTML5 and Javascript. In this tutorial you will learn how to use the HTML5 canvas element and Javascript to create simple particles for a nice, soothing snowfall effect. If you want to see the finished product, you can do so here. Now let's get started!

First we'll set up our basic HTML document. This really is pretty simple; the body tag will contain just a single div element which will itself contain the canvas element that we'll use to draw our scene.

<!DOCTYPE HTML>

<html>

<head>

    <title>HTML5 Particles</title>

    <script type="text/javascript">

    </script>

</head>

<body onLoad="init()" >

    <div style="margin:0 auto; width:640px; height:480px; background-color:#ffffff; border: 3px solid black;">
        
       <canvas id="stage" width="640" height="480" >
       </canvas>
    
    </div>

</body>

</html>

If you'll notice, we added an onLoad function, init, to our body tag. However, we haven't yet defined the init method; we'll get to that a bit later. The basic HTML structure is in place, but before we jump right into code, go ahead and find a winter-themed image that you'd like to use. I recommend using a 640x480 image simply because that's what I used when creating this tutorial. Once you've found an image that you like, save it in the same directory as your HTML document. I saved my image with the name: snowscene.jpg.

Now that we have our image ready, let's get into the code. The first thing we're going to do is add a few variables in our script block. So under the opening script tag add the following:

var NUM_FLAKES = 100;
var canvas;
var context;
var intervalID;
var bgImage;
var flakes;

All of these variables will be used at one point or another in our program. The NUM_FLAKES variable is in all caps because we are setting it to 100 here and we don't want to change it. The capital letters are not necessary, but they will serve as a reminder that this variable shouldn't be changed. Next up, let's define that init method that gets called when our html body loads.

function init()
{
    canvas = document.getElementById("stage");
    context = canvas.getContext('2d');
	
    if(context != undefined)
    {
        bgImage = new Image();
        bgImage.onload = onImageLoaded;
        bgImage.src = "./snowscene.jpg";
    }
    else
    {
        alert('no rendering context found');
    }
}

This function is pretty simple. First we grab a reference to our canvas element. Next, we attempt to grab a 2D rendering context from the canvas. If successful, we'll load in our background image; in my case: snowscene.jpg. If we fail to get a rendering context, then we show an alert to let the user know. Notice that we've set the bgImage's onload function to another function that we haven't created yet: onImageLoaded. Let's take care of that now.

function onImageLoaded()
{
    context.drawImage(bgImage, 0, 0);
    createFlakes();
    intervalID = setInterval(update, 16);
}

Again we have a short, simple function. First it draws the background image to the canvas and then calls a function createFlakes. The createFlakes function is a helper method that we'll define in just a bit. Lastly, this function sets up an interval that will execute another as-of-yet nonexistent function, update, every 16 milliseconds. This will aim to give us a framerate of 60 FPS.

Before we define the createFlakes and update methods we will first define a flake object and its properties. In Javascript, functions are objects. In order to create our flake object, we'll define it as a function. This is probably my largest gripe about Javascript, hopefully a future implementation will allow for proper class support; but for now, we'll work with what we've got. Here's our flake definition.

function flake()
{
    this.x = 0;
    this.y = 0;
    this.radius = 4;
    this.alpha = 0.7;
    this.velocityX = -7;
    this.velocityY = 8;
    this.fillStyle = "rgba(255, 255, 255, 0.7)";
	
    this.initialize = function()	
    {
        this.x = Math.random() * 640;
        this.y = Math.random() * -100;
        this.radius = Math.random() * 4 + 2;
        this.alpha = Math.random();
        this.velocityX = Math.random() * 4;
        this.velocityY = Math.random() * 2 + 2;
		
        // make the minimum alpha 0.25
        // if less the flake is hard to see
        if(this.alpha < 0.25)
            this.alpha = 0.25;
		
        this.fillStyle = "rgba(255, 255, 255, " + this.alpha.toString() + ")";
		
        // we will randomly flip the velocity x
        // because all flakes going the same direction is boring!
        if(Math.random() > 0.5)
            this.velocityX *= -1;
    };
}

Our flake contains several properties: an x position, a y position, a radius, an alpha value, a velocity for both the x and y directions and also a string that describes how to draw this particular flake. It also contains an initialize function which gives these properties randomized values. This method also ensures that no flake has an alpha value lower than 0.25 which keeps them more visible and it will randomly reverse the flake's movement along the x axis. This means some flakes will move right to left while others move from left to right. This is not necessary, but does help create a more pleasing effect.

With the flake object defined, let's build the createFlakes function.

function createFlakes()
{
    flakes = new Array();
    var f;
    for(var i = 0; i < NUM_FLAKES; i++)
    {
        f = new flake();
        f.initialize();		
        flakes.push(f);
    }
}

This function should be pretty self-explanatory, but we'll go over it anyway to be on the safe side. First, we create a new array to hold our flakes. Then we loop through and create a new flake until we reach the value of NUM_FLAKES (in this case, 100). Inside the loop we create a new flake, initialize it and then add it to our flakes array. Now we're ready to write the final function for this tutorial: update.

function update()
{
    context.clearRect(0, 0, 640, 480);
    context.drawImage(bgImage, 0, 0);
	
    var curFlake;
    for(var i = 0; i < NUM_FLAKES; i++)
    {
        curFlake = flakes[i];
        curFlake.x += curFlake.velocityX;
        curFlake.y += curFlake.velocityY;
		
        // draw a circular flake
        context.beginPath();
        context.arc(curFlake.x, curFlake.y, curFlake.radius, Math.PI * 2, false);
        context.fillStyle = curFlake.fillStyle;
        context.fill();
		
        // if a flake goes out of bounds, recycle it and use it again
        if(curFlake.y > 480 || curFlake.x > 640 || curFlake.x < 0)
        {
            curFlake.initialize();
        }
    }
}

This function is a little bit bigger than most of the others we've seen, but still isn't too bad. First, it clears the canvas so that we can draw a fresh image to it. Then it draws the background image. Now things get more interesting. We loop over all the flakes in our array and update them by adding their x and y velocities to their x and y positions. Once the position is updated, we draw the flake. We use the rendering context's arc method to define our circle and then set the drawing fill style to that of the current flake. Then we call the context's fill method to actually draw the flake.

The last thing we do in the loop is check to see if the current flake is no longer in the display area. If it is not, we reinitialize it and it is reused. This allows us to give the illusion of having an infinite amount of flakes, but in reality, we're just reusing the same 100 flakes over and over again!

That's all there is to it! Easy stuff, huh? In this tutorial you learned how to use basic particles to create a soothing snowfall effect with Javascript and HTML5. I hope you found it both entertaining and helpful! Thanks for reading!

Bookmark and Share

2 Responses to “Tutorial: Particle Basics with HTML5 and Javascript”

  1. Chris says:

    In what program do you code, notepad?

  2. Rhuno says:

    I use Notepad++ or Sublime Text when working with HTML/JS stuff, but there are plenty of other options out there as well. Adobe’s Brackets is something I’ve been meaning to look into.

Leave a Reply

Subscribe to RSS feed FGS5 Badge