Spicy Yoghurt | Last updated: 9 March 2019 | HTML5 game tutorial

Create a game loop in JavaScript

Create a proper game loop in JavaScript and learn about frame rates. Measure and display fps to see your loop in action. Request animation frames and have your own loop running by the end of this HTML5 game tutorial.

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

Why do you need a game loop?

In the previous tutorial, you've created an application that draw's a rectangle on an HTML5 canvas. This is super nice and all, but that drawing operation is only performed once. If you want make it appear as if the rectangle is moving, and eventually create an actual game, you need to draw more. A lot more.

To achieve this, you'll need a loop to repeatedly execute your game logic. In the world of games, this loop is called a game loop. It'll be the core of your game and trigger the drawing operations. You'll no longer be drawing a single static image, but many images will be drawn after each other, creating motion.

Here's an example with a moving circle. It shows five frames in a row, each with a slightly different position of the circle. This creates the effect of motion when shown quickly after each other. Multiple frames, creating a motion animation

The benefits of a high frame rate

As you might know, with games, people talk about how many fps they can reach with their gaming rig. Fps stands for frames per second. Each time you draw your game objects on the screen, counts as a frame.

So, fps is the amount of times per second you can draw your game on the screen. In general, the better your hardware the higher your fps will be. And the higher your fps, the smoother your game will play. Also, the delay between frames will become smaller, making the state of the game more up to date. This gives you a possible advantage in gameplay. That's why professional gamers want to squeeze out every frame they can get and spend a lot on the best possible hardware. A professional gamer

The ideal frame rate for your game loop

Humans can process 10 to 12 frames per second individually. Any more frames than that and you will perceive it as being a motion and that is what you want for your game. Of course, a game running at 12 fps would be extremely choppy. You might see it as a motion, but you need more frames if you want to make it look smooth. Movies generally use 24 fps, but most games run even more frames than that. If more is better, why not just aim to run your game at 120 fps and make it ultra smooth?

Well, there is also the refresh rate of your display to take into account. Running a game at 120 fps on a display that's 60Hz (that's a refresh rate of 60 times per second) won't do much good. You'll have more frames to draw than your display can handle. Modern displays support a higher refresh rate, so you'll need one of those if you want to handle 120 fps. But an HTML5 game is not just for high-end desktops rigs with great displays, it has to run and perform well on mobile devices too.

An fps that is equal to that of the refresh rate of your display would be ideal. It will offer the smoothest experience and the game state will be up to date the most. And at the same time you want to limit the maximum fps in some amount, to prevent your game from using too much system resources and be a battery drainer on mobile devices.

So, to sum it all up, this is what you'll be looking for with your game loop:
  • A frame rate that is as high as possible for smoothness
  • A frame rate that isn't higher than the screen refresh rate
  • A frame rate that doesn't uses too much system resources
Let's check out some options and see how well they fit these requirements. You are going to find the perfect loop for your game.

Set-up a game loop

Ok, so you need a game loop? Why not just use this simple while loop in your JavaScript code?

                
                    // A bad game loop
                    while (running) {
                        draw();
                    }
                
            
The loop just starts up and draws indefinitely, until something tells it to stop running, like when pausing the game. So, you will get the maximum potential out of your system and reach as much fps as your hardware allows.

But there is a small problem. JavaScript runs on a single thread per browser tab. With the while loop, every bit of available system resources will go into performing the drawing operations, again and again. This will render your browser unable to perform other tasks, like managing user input or other important events. When you let it run for a while, it will hang your browser and eventually give the famous 'this page is not responding' warning.

To fix this, you'll need to give the browser some air while performing the game loop. You could use something like setInterval() to loop with a set amount of time for each frame. This will put some time between each drawing operation, giving the system space to perform other tasks than drawing.

                
                    // Another bad game loop
                    setInterval(gameLoop, 16);

                    function gameLoop() {
                        draw();
                    }
                
            
With an interval of 16ms, this game loop will reach about 60fps. This will perform better than the while loop but there is no guarantee that the browser is ready to perform a repaint when the gameLoop() function is triggered. You might calculate frames that are never put on your display. How to fix this?

The proper JavaScript game loop

You need a way to give the browser some air while looping, and time your game loop to be in-sync with the browser repaint. Luckily there is a solution. You can use window.requestAnimationFrame() to tell the browser you want to request a repaint for an animation, or in this case a game.

                
                    // The proper game loop
                    window.requestAnimationFrame(gameLoop);

                    function gameLoop() {
                        draw();
                        window.requestAnimationFrame(gameLoop);
                    }
                
            
The browser will perform a callback function on its own time. Meaning it won't hang the system. Also, when a browser tab is no longer focused, the browser might reduce the number of callbacks, to reduce system load. This will improve the battery life of the device it is run on, for example.

Do you remember the talk about frame rate? Well, the browser takes care of picking a suitable fps for the device it is running on. This usually means your game loop will run at 60fps, but will generally match your display refresh rate. The requestAnimationFrame() function takes care of that.

This method meets all requirements. The loop runs at a frame rate that will make the game look smooth, takes in account the refresh rate of the screen and reduces the use of system resources. It's the perfect match.

How to use requestAnimationFrame()?

The function window.requestAnimationFrame() accepts a function as argument. You can pass your gameLoop() function in here. When the browser is ready it will call that function.

You will have to start the game loop by calling window.requestAnimationFrame() once, and then keep on calling it inside the loop. It's easier to understand in the next example:

                    
                        <script>
                            "use strict";
                            var canvas;
                            var context;
                            
                            window.onload = init;

                            function init(){
                                canvas = document.getElementById('canvas');
                                context = canvas.getContext('2d');

                                // Start the first frame request
                                window.requestAnimationFrame(gameLoop);
                            }

                            function gameLoop(timeStamp){
                                draw();

                                // Keep requesting new frames
                                window.requestAnimationFrame(gameLoop);
                            }

                            function draw(){
                                var randomColor = Math.random() > 0.5? '#ff8080' : '#0099b0';
                                context.fillStyle = randomColor;
                                context.fillRect(100, 50, 200, 175);
                            }
                        </script>
                    
                    
The callback from requestAnimationFrame() is given one argument, and that's a time stamp containing the current time. Its value is the same as you would get from calling performance.now(), but it's given for free this way.

You won't be doing anything with the time stamp right now. You'll learn more about using time as a factor in your game loop in the next tutorial about canvas animations. It's easier to explain things there, when you have moving objects. Just remember you can use it later on.

Check out the running game loop

Let's try out your new game loop. When running this code, you see a big difference compared to the previous tutorial with only one call to the draw() function. draw() is now called many times in a row. The rectangle is redrawn roughly 60 times per second, depending on the device you run the loop on.

Here's a schematic view of your game loop. We will expand it with more tasks in the next tutorials.
A simple game loop with only a draw function
The color of the rectangle is still random. So, a new random color is picked, every frame. That's why you now see a flashy rectangle that is switching very fast between red and blue. This makes it easier to see the game loop is actually working.

Measure and display fps

Well done, you've made a game loop and it's working. But wouldn't it be nice if you could measure its performance and check out how much frames per second it produces? Let's try to calculate the current fps by using the timestamp parameter provided in the game loop.

First start by calculation the time that has passed since the previous frame. You can do that by subtracting the timestamp of the previous frame from the current timestamp. The time that's left is the number of milliseconds since the last frame.

Divide this number by 1000 to go from milliseconds to seconds. It takes this amount of seconds to produce one frame. So, divide 1 by this number to get the number of frames per second.

Here is an example of a very simple implementation that uses a fillText() drawing operation, like handled in the canvas drawing tutorial, to draw the fps as text to the screen:

         var oldTimeStamp;

        function gameLoop(timeStamp) {

            //Calculate the number of seconds passed
            //since the last frame
            var secondsPassed = (timeStamp - oldTimeStamp) / 1000;
            oldTimeStamp = timeStamp;

            //Calculate fps
            var fps = Math.round(1 / secondsPassed);

            //Draw number to the screen
            context.fillStyle = 'white';
            context.fillRect(0, 0, 200, 100);
            context.font = '25px Arial';
            context.fillStyle = 'black';
            context.fillText("FPS: " + fps, 10, 30);

            // Perform the drawing operation
            draw();

            // The loop function has reached it's end
            // Keep requesting new frames
            window.requestAnimationFrame(gameLoop);
        }
        
You can run the code to check the fps meter and see if your game loop is functioning properly. If you view this page on a desktop device, it should hit a nice fps of 60. The fps is updated every frame, so it might get a bit hard to read, but it's fine for this demonstration. You could put more time into it and think of ways to make the fps get updated less often. You could buffer the fps and only update it once every second for example.

Final code

That's all on game loops for now. Your JavaScript game loop is in place and the rectangle gets redrawn many times per second now. You understand the relation between fps and a game loop. Also, you can now measure the fps of your own game.

You can download the final code for this step of the tutorial here. Feel free to ask questions in the comments.

In the next step of the tutorial, the rectangle will come more alive. You will be learning how to add motion and make animations on the HTML5 canvas. Also you'll learn about the effects of frame rate on animations.

Leave a comment