Spicy Yoghurt | Last updated: 2 Februari 2019 | HTML5 game

Create a smooth canvas animation

Following this HTML5 game development tutorial, you will learn how to create the perception of motion by drawing dynamically on the canvas. You'll be taught the basics for creating canvas animations. You will also learn how to make this animation smooth, even with a variable frame rate.


This tutorial requires the HTML5 file with the game loop, you have created in the previous tutorial. If you skipped that step, you can download the HTML5 file with game loop here.

Draw dynamically on the canvas

The draw() function in the previous tutorial is very static. It only draws one rectangle on the same place, over and over again. Let's make it a bit more interesting. Change the old draw() function to this new one:

                
                    function draw() {
                        // Fill with red
                        context.fillStyle = '#ff8080';

                        // Draw a rectangle on the canvas
                        context.fillRect(rectX, rectY, 50, 75);
                    }
                
            
As you can see, the draw operation is now using variables as input. If you change rectX and rectY, you should get a moving rectangle. With the following code you can update the position of the rectangle:

                            
                //Rectangle coordinates
                var rectX = 0;
                var rectY = 0;

                function gameLoop() {
                    // Update game objects
                    update();

                    // Perform the drawing operation
                    draw();

                    // The loop function has reached it's end
                    // Keep requesting new frames
                    window.requestAnimationFrame(gameLoop);
                }

                function update() {
                    rectX += 1;
                    rectY += 1;
                }
            
            
A simple update() function adds 1 to the coordinates of the rectangle. Every frame the update() function is called in the gameLoop(). It is important to perform the update() before the draw(), so that you'll always draw the latest state of your game to the canvas.

A blur of graphics

You should expect to see the rectangle move in a slanted line, from the upper left corner to the lower right. Here is what you get: Uhm what is happening here? The rectangle is drawn on the canvas, but the previous rectangle is never removed. Why is that?

How does the canvas work?

Well, the canvas acts as a drawing board. You can draw on it, change position of the rectangle, and draw it again. Do this quick enough and you will get the perception of motion. But if you forget to clear the canvas, you will draw each drawing on top of the previous. Creating a blur of drawings.

Clearing the canvas

To solve this problem, you'll have to clear the canvas before every new drawing operation. Add clearRect() to the draw() function:

                
                    function draw() {
                        // Clear the canvas
                        context.clearRect(0, 0, canvas.width, canvas.height);

                        // Fill with red
                        context.fillStyle = '#ff8080';

                        // Draw a rectangle on the canvas
                        context.fillRect(rectX, rectY, 50, 75);
                    }
                
            
Now it's looking much better! The clearRect() function clears a part of the canvas. In this case, it is set to clear an area covering the entire canvas. Starting from the upper left corner at 0, 0, continuing for canvas.width and canvas.height.

Time based motion

Right now, the animation runs smooth. But what if your computer somehow delays some frames or speeds up? The motion will be held back or speed up too. You don't want that. You want to move you object with speed x, no matter the framerate.

That's why you want to include time as a factor in your animation. See the following code:


                var oldTimeStamp = 0;

                function gameLoop(timeStamp) {
                    // Calculate how much time has passed
                    var secondsPassed = (timeStamp - oldTimeStamp) / 1000;
                    oldTimeStamp = timeStamp;

                    // Update game objects
                    update(secondsPassed);

                    // Perform the drawing operation
                    draw();

                    // The loop function has reached it's end
                    // Keep requesting new frames
                    window.requestAnimationFrame(gameLoop);
                }

                function update(secondsPassed) {
                    // Set the speed of the objects. It will move x pixels per second.
                    var movingSpeed = 50;

                    // Move the objects
                    rectX += (movingSpeed * secondsPassed);
                    rectY += (movingSpeed * secondsPassed);
                }
            
At the start of the gameLoop() the number of passed seconds gets calculated. This value is passed to the update() function.

When your game runs at 60fps, it's roughly 0.0167 seconds per frame. This means that when you want to move an object with, let say, 50 pixels per second, you have to multiply 50 with the number of seconds that have passed. That's what's happening in the update() function.

So, no matter how much time has passed, your objects will always move at the desired speed. This makes the animation more suitable for different kinds of hardware, with different frame-rates.

Final code

You managed to move objects in a smooth manner. Let's take a look at the whole code now.

                    
                <!DOCTYPE html>
                <html lang="en">

                <head>
                    <meta charset="UTF-8">

                    <title>HTML5 Game Development Tutorial | Spicy Yoghurt</title>
                    <meta name="description" content="HTML5 game development tutorial">
                    <meta name="author" content="Spicy Yoghurt">
                </head>

                <body>
                    <canvas id="canvas" style="border:1px solid lightgrey;">
                        Your browser does not support the HTML5 canvas tag.
                    </canvas>
                    <script>
                        "use strict";

                        // Declare as variable
                        var canvas;
                        var context;
                        var rectX = 0;
                        var rectY = 0;
                        var oldTimeStamp = 0;

                        // Listen to the onLoad event
                        window.onload = init;

                        // Trigger init function when the page has loaded
                        function init() {
                            canvas = document.getElementById('canvas');
                            context = canvas.getContext('2d');

                            // Request an animation frame for the first time
                            // The gameLoop() function will be called as a callback of this request
                            window.requestAnimationFrame(gameLoop);
                        }

                        function gameLoop(timeStamp) {
                            // Calculate how much time has passed
                            var secondsPassed = (timeStamp - oldTimeStamp) / 1000;
                            oldTimeStamp = timeStamp;

                            // Update game objects
                            update(secondsPassed);

                            // Perform the drawing operation
                            draw();

                            // The loop function has reached it's end
                            // Keep requesting new frames
                            window.requestAnimationFrame(gameLoop);
                        }

                        function update(secondsPassed) {
                            // Set the speed of the objects. It will move x pixels per second.
                            var movingSpeed = 50;

                            // Move the objects
                            rectX += (movingSpeed * secondsPassed);
                            rectY += (movingSpeed * secondsPassed);
                        }
                        
                        function draw() {
                            // Clear the canvas
                            context.clearRect(0, 0, canvas.width, canvas.height);

                            // Fill with red
                            context.fillStyle = '#ff8080';

                            // Draw a rectangle on the canvas
                            context.fillRect(rectX, rectY, 50, 75);
                        }
                    </script>
                </body>
                </html>



            
            
You can copy and store this code in a file, or download the complete file here. Feel free to ask questions in the comments.

In the next step of the tutorial, you'll be learning how to create multiple objects and detect collisions!

Leave a comment