Framerate control system for JavaScript

By Javier Degirolmo

Table of contents

Introduction

This article discusses how to create a reliable framerate control system in JavaScript. It is basically a timer with a rate of several cycles per second. It would normally be used in games as a timer to keep the game logic running at a given speed — it is important that the timer is stable, otherwise the game will start running at different speeds and adversely affect gameplay.

You can download the example code, which includes both the code for the framerate control system object I’m about to explain as well as the example code I discuss later showing how to use it.

How it works

There are several ways to make a system that lets us keep up with the pace of a specific timer (which in our case would be running at the speed of the ideal framerate). One method would be to use a counter — the idea is that we call a function every time a timer tick happens, which increases the counter. Then the main loop would check for this counter later and compare it with the value it had the last time it was run. This lets us know how many frames have elapsed since the last iteration.

While this method works perfectly, it has an issue: it requires the function that increases the counter to be called constantly enough to keep up with the timer. Calling this function with methods such as setInterval() works in most browsers; if they can’t keep up with the pace of the timer they will start calling the function more often until they manage to reach the timer, which is accurate enough for our purposes. However, it seems that some browsers won’t call the function more often if they’re going too slowly, which breaks our timing system and becomes useless.

The approach used in this article is different. We obtain the current time in milliseconds using the getTime() function from the Date object and make a note of the current frame, then do the same thing again. We can then determine the amount of frames elapsed between those two times by looking at the two “current frame” values. This method isn’t affected by the speed of the browser. This method has only been tested on Opera, Firefox and Internet Explorer, but it should work on any browser that supports scripting and the Date object.

Implementing the Framerate object

Let’s have a look at how we can create this — we’ll store this functionality in a Framerate object. The first thing we’ll do is create an empty object that returns a pointer to itself. This is the object that will be doing all the timing duties.

function Framerate()
{
  // Return a pointer to itself
  return this;
}

Once we have the base object, we’ll start making the functions for it. The first function we’re going to make will tell the object the framerate. We’re going to call this function setRate(), and it will receive one parameter: the amount of cycles per second.

Framerate.prototype.setRate = function(rate)
{
  // Calculate new time difference between frames
  this.frameLen = 1000 / rate;
}

Notice that we’re declaring the function as part of the object prototype. Make sure to declare it outside the object code, or there will be a memory leak every time a new object of this type is created. The same applies to the rest of the functions for this object.

This function calculates the length of a frame in milliseconds. This value is stored in the object’s frameLen variable and will be used later to determine the amount of frames that have elapsed.

The next function — reset() — will reset the timer. It sets the time of the last frame that has been checked to the current time. We need to call this function when initializing the object in order to get proper results.

Framerate.prototype.reset = function()
{
  // Create Date object
  var d = new Date();
    
  // Reset time of the last time to the current one
  this.prevTime = d.getTime();
}

First, a new Date object is created. By default, Date objects contain the date and time of when they were created, which is what we’re looking for, so we just store the variable value as is. Next we retrieve the current time in milliseconds, calling the getTime() function on that object. This value is stored in the object’s prevTime variable, which holds the time of the last frame that was checked.

Now that the required functions to set up the timer have been created, it’s time to create the function to retrieve the amount of elapsed frames. This function will return how many frames have elapsed since the last time it was called or reset() was called. It achieves that by comparing the amount of time elapsed since the last time, using the value in frameLen to determine the duration in frames. It also updates the time for the last frame accordingly, getting it ready for the next call. We’re calling this function getFrames().

Framerate.prototype.getFrames = function()
{
  // Create Date object
  var d = new Date();
    
  // Get current time, in milliseconds
  var currTime = d.getTime();
    
  // Calculate amount of frames elapsed
  // Also update time of the last frame if needed
  var totalFrames = 0;
  while (this.prevTime + this.frameLen <= currTime)
  {
    this.prevTime += this.frameLen;
    totalFrames++;
  }
    
  // Return amount of frames elapsed
  return totalFrames;
}

First of all, it uses the same method as reset() to retrieve the current time. Next it sets up a counter to determine how many frames have passed (the initial value of this counter is 0 frames), then it enters a loop. If the time for what would be the next frame is smaller than the current frame, it increases the value of the last frame by one frame, and it also increases the amount of elapsed frames by 1. Once the situation arises that the next frame would be after the current time, we have achieved the value we want — the amount of elapsed frames, so the function stops iterating and returns the value.

These three functions give us enough to have a reliable timer that can be used to keep track of the framerate in a game. We'd like to encourage you to improve the object, and tell us about your results in the comments.

Example code

Now we will look at some example code showing how to use the Framerate object. It’ll merely show the amount of seconds elapsed on screen, with accuracy within a tenth of a second. We’ll set the framerate at 10 frames per second, and set up a function that will check the frames regularly and update the amount of seconds accordingly. Even if the browser doesn’t call this function fast enough, the amount of seconds will appear at the correct speed (albeit results will appear on screen at a slower rate, skipping some values).

First we’ll set up our main function to do the initialization. All variables will be stored here in order to prevent global namespace pollution.

// Main function
function TestCode()
{
}

// Call main function on load
TestCode();

Now we have to create the Framerate object. We’ll set it to run at a rate of 10 frames per second, as stated earlier. Then we’ll reset the timer so it knows when to start counting. We can achieve that by putting the following code inside the function. We’re storing the pointer to the Framerate object in a variable called f.

// Create a Framerate object
this.f = new Framerate();

// Initialize Framerate object
this.f.setRate(10);
this.f.reset();

Now we’ll create a <p> element to put in the document, which will show the amount of seconds elapsed. We’re storing the pointer to it in the p_obj variable, and appending the object to the document body.

// Create <p> object inside the page
this.p_obj = document.createElement("p");
this.p_obj.innerHTML = "0.0 seconds";
document.body.appendChild(this.p_obj);

We’ll start the counter at 0 seconds, so we have to set its initial value accordingly.

// Reset seconds counter
this.seconds = 0;

Now we need a function that will be used to update the seconds counter on screen. We’ll be using setInterval() so it is called regularly. Let’s start by making an empty function. We’re going to store a pointer to it in a variable called callback.

// This function will be called constantly to update the counter in the
// <p> object in the page.
this.callback = function()
{
}

Now we’ll insert the meat of the code into this function. The first thing we need in order to calculate the amount of seconds elapsed since the last time is to retrieve the amount of frames. In order to do that, we call the getFrames() function from our Framerate object.

// Get amount of frames elapsed
var numFrames = this.f.getFrames();

So far so good. Now we need to calculate the amount of seconds to show this time. In order to do that, we increase the seconds counter by the amount of seconds elapsed. Since the timer is running at 10 frames per second, that means that we have to divide the amount of frames by 10 in order to know how many seconds have elapsed. We add this value to the previous amount of seconds to get the new amount of seconds.

// Update amount of seconds
this.seconds += numFrames / 10;

Finally, we show the updated amount of seconds on screen by altering the innerHTML property of the <p> element we created before, thereby changing its contents.

// Show seconds on screen
this.p_obj.innerHTML = Math.floor(this.seconds) + "." +
                       Math.floor(this.seconds * 10) % 10 +
                       " seconds";

That is all we need in the updating function. The last thing we need now is to somehow cause this function to be called constantly. I’m going to use the setInterval() function for this. So, after having created our updating function, we only need to put the following inside the main function. Notice that I’m storing this in a variable and referencing the function from there; otherwise the updating function won’t receive a proper value for this and it won’t work.

// Call updating function
var myself = this;
setInterval(myself.callback, 150);

If you did everything correctly, you will see a seconds counter on screen. You can find the completed code in the code download for this article. I'm calling the updating function every 150 milliseconds in order to make the effects of going too slow more obvious. Feel free to modify this value in order to see how the Framerate object behaves. The seconds counter should be counting at the right speed regardless of how often the function is called.

What’s next?

In this article we’ve described a barebones framerate control system. It can certainly be improved on — here are some suggestions that you may want to look into:

  • Function parameters are not validated. You may want to make sure that the passed parameters are valid, otherwise the code may crash if invalid parameters are passed in.
  • Related to the above point, you may want to make the Framerate object have default values. Trying to use it without explicitly calling setRate() and reset() will yield invalid results.
  • Make a function to retrieve the current rate.

And of course, feel free to experiment and try new ideas to improve the Framerate object.

This article is licensed under a Creative Commons Attribution, Non Commercial - Share Alike 2.5 license.

Comments

The forum archive of this article is still available on My Opera.

  • photo

    jayarjo

    Saturday, September 1, 2012

    Great article! But I have a side question. When you say: "Notice that we’re declaring the function as part of the object prototype. Make sure to declare it outside the object code, or there will be a memory leak every time a new object of this type is created." - what kind of memory leak do you mean? I've never encountered any mentioning of this being a potential memory leak. Could you provide any resource that has a deeper writeup on the matter?
No new comments accepted.