Modernize your HTML5 Canvas Game

Share this article

Modern browsers like Internet Explorer 10 are implementing stable versions of some interesting HTML5 features, including offline application programming interfaces (API), drag and drop, and file API. These features are bringing us to a new era of web applications and fresh, quickly emerging gaming scenarios. In this two-part article, I’ll show how I’ve used these new features to modernize my last HTML5 game, HTML5 Platformer. Hopefully you’ll get some great new ideas for your own games! Part 1: Hardware scaling and CSS3 Part 2: Offline , file, and drag-and-drop API (next article) Note: The URL demo is at the end of this article. Feel free to play using your favorite browser, and check out the IE10 gameplay video. The source code will be available for download in Part 2.

Scaling across devices

If you’re building an HTML5 game, you’re probably interested in the cross-platform nature of this standard programming language. But compatibility with a broad variety of devices means you have to take into account a huge number of resolutions. Compared to SVG, Canvas—at first—seems ill-prepared to handle this. However, with a casual game based on sprites, there is a simple solution to implement. David Catuhe has done a great job of describing this on his blog, Unleash the power of HTML 5 Canvas for gaming – Part 1 (see the section called “Using the hardware scaling feature” for specifics). The idea is as simple as it is smart. You’re working inside a canvas at a fixed, predictable resolution, and you’re stretching it to the current displayed resolution using the canvas.style properties.

Step 1: Stretch

In the case of my little HTML5 Platformer game, the assets and level logic have been set to 800×480. So if I want to fill a 1080p screen or a 1366×768 tablet, I need to build higher resolution assets to match those specs. Before building all those assets, I can try a scaling operation—along with proper anti-aliasing—to increase image quality. Let’s give it a try. The scaling operation simply requires this code:
window.addEventListener("resize", OnResizeCalled, false);

function OnResizeCalled() {
    canvas.style.width = window.innerWidth + 'px';
    canvas.style.height = window.innerHeight + 'px';
}
And that’s it! With hardware-accelerated browsers, this operation will be done by your GPU at no cost. Anti-aliasing can even be enabled. That’s why David Catuhe thought it was worth mentioning it in his Canvas performance article. This trick is not specific to HTML5, by the way. Most modern console games are not internally computed in 720p or 1080p; almost all of them render in lower resolutions (like 1024×600) and let the GPU handle the scaling/anti-aliasing process. In most cases, the method described here can help you boost the number of frames per second (FPS). But this action, by itself, raises a ratio problem. Indeed, when the canvas had a fixed size of 800×480, the ratio was controlled. Now, I get this strange result when resizing the browser window: The game is still playable, but also clearly far from optimal.

Step 2: Control your ratio

The idea here is to control how the screen is filled when the browser window is resized. Rather than stretching anything further, I’m going to add some empty space on the right if the window is too large, or at the bottom if the window is too high. Here’s that code:
var gameWidth = window.innerWidth;
var gameHeight = window.innerHeight;
var scaleToFitX = gameWidth / 800;
var scaleToFitY = gameHeight / 480;

var currentScreenRatio = gameWidth / gameHeight;
var optimalRatio = Math.min(scaleToFitX, scaleToFitY);

if (currentScreenRatio >= 1.77 && currentScreenRatio < = 1.79) {
    canvas.style.width = gameWidth + "px";
    canvas.style.height = gameHeight + "px";
}
else {
    canvas.style.width = 800 * optimalRatio + "px";
    canvas.style.height = 480 * optimalRatio + "px";
}
The “if” statement creates an exception: If you hit F11 in your browser to switch to full-screen viewing, and you’ve got a 16:9 screen (like my 1920×1080 Sony VAIO Z screen or the 1366×768 Samsung BUILD tablet), the game will be completely stretched. This experience was quite awesome. Without this exception, here is the type of output you’ll see: Notice the black areas below and to the right of the game, controlling the ratio. It would be even better if the game was centered, giving it a widescreen movie effect, right? Let’s do it.

Step 3: Center the game with CSS3 Grid Layout

Centering an HTML element can sometimes be painful. There are several ways to do it—and there are plenty of resources on the web to help. I like to use a new specification named CSS Grid Layout (currently only supported by IE10), which is the base of our Metro-style layout in Windows 8. Centering an element with CSS Grid Layout is straightforward:
  • Switch the display of the container to display:grid.
  • Define 1 column and 1 row.
  • Center the inner element with the column-align and row-align properties.
Here’s the CSS used in my case:
.canvasHolder {
    width: 100%;
    height: 100%;
    display: -ms-grid;
    -ms-grid-columns: 1fr;
    -ms-grid-rows: 1fr;
}

#platformerCanvas {
    -ms-grid-column: 1;
    -ms-grid-row: 1;
    -ms-grid-column-align: center;
    -ms-grid-row-align: center;
}
You’ll notice the prefix is “-ms” for IE10. Mozilla has recently annonced they will also support the CSS Grid Layout specification for Firefox in 2012, which is excellent news. In the meantime, this centering trick only works with IE10. Here’s what it looks like: IE10 windows will display vertical or horizontal black bars, similar to what you might see on a television screen. In other browsers, the results will match those of Step 2, because the CSS3 Grid Layout specification will be ignored.

Using smooth animations

Now that we’re handling multiple resolutions with an easy scaling operation, it would be nice to play a smooth transition when the user is resizing the window. It would also be great to play a cool animation while each level loads. For that, we’re going to use the CSS3 Transitions and CSS3 3D Transforms tools. On most platforms, hardware acceleration is provided by the GPU.

Animating every change made to Canvas style properties

CSS3 Transitions is easy to use and produces smooth, efficient animations. To discover how to use them, you can read my colleague’s excellent article, Introduction to CSS3 Transitions, or play around on our Internet Explorer test-drive site, Hands On: transitions.

#platformerCanvas {
    -ms-grid-column: 1;
    -ms-grid-row: 1;
    -ms-grid-column-align: center;
    -ms-grid-row-align: center;

    -ms-transition-property: all;
    -ms-transition-duration: 1s;
    -ms-transition-timing-function: ease;
}
The canvas with the “platformerCanvas” ID will now automatically reflect any change made to its style properties in a one-second animation generated by an easing function. Thanks to this new rule, resizing the browser window reduces/increases the size of the canvas with a smooth animation. I love the effect it produces—all with only 3 lines of CSS. Note: I also add the different prefixes (-moz, -webkit, and -o for Mozilla, WebKit, and Opera, respectively) for compatibility. You’ll want to remember to do the same.

Building a cool animation between each level

Now I’d like to use CSS3 3D Transforms to temporarily make the canvas disappear. This will be done with an animated 90-degree rotation on the Y axis. I’ll load the next level once the animation rotates 90 degrees and comes back to its initial position (rotated zero degrees). To better understand the effect, you can play with our Hands On: 3D Transforms on the Internet Explorer test-drive site: We’re also going to play with the scale and rotateY properties to build a fun animation. For that, I’m adding two CSS rules and targeting two classes:
.moveRotation
{
    -ms-transform: perspective(500px) rotateY(-90deg) scale(0.1);
    -webkit-transform: perspective(500px) scale(0);
    -moz-transform: perspective(500px) rotateY(-90deg) scale(0.1);
}

.initialRotation
{
    -ms-transform: perspective(500px) rotateY(0deg) scale(1);
    -webkit-transform: perspective(500px) scale(1);
    -moz-transform: perspective(500px) rotateY(0deg) scale(1);
}
First, my canvas has the initialRotation class set:
<canvas id="platformerCanvas" width="800" height="480" class="initialRotation"></canvas>
Now the idea is to wait until the player beats the current level. Once that’s done, we’re changing the class of the canvas from initialRotation to moveRotation. This will automatically trigger the CSS3 Transitions we set before to generate the animation. In order to know when the animation is finished, an event is raised. It’s named differently in each browser. Here is the code I use for registering the event and targeting IE10, Firefox, WebKit, and Opera:
// Registering to the various browsers vendors transition end event
PlatformerGame.prototype.registerTransitionEndEvents = function () {
    // IE10, Firefox, Chrome & Safari, Opera
    this.platformerGameStage.canvas.addEventListener("MSTransitionEnd", onTransitionEnd(this));
    this.platformerGameStage.canvas.addEventListener("transitionend", onTransitionEnd(this));
    this.platformerGameStage.canvas.addEventListener("webkitTransitionEnd", onTransitionEnd(this));
    this.platformerGameStage.canvas.addEventListener("OTransitionEnd", onTransitionEnd(this));
};
And here is the code that will be called back:
// Function called when the transition has ended
// We're then loading the next level
function onTransitionEnd(instance) {
    return function () {
        if (instance.loadNextLevel === true) {
            instance.LoadNextLevel();
        }
    }
};
At last, here is the code for my game that set the moveRotation class on the canvas:
// Perform the appropriate action to advance the game and
// to get the player back to playing.
PlatformerGame.prototype.HandleInput = function () {
    if (!this.wasContinuePressed && this.continuePressed) {
        if (!this.level.Hero.IsAlive) {
            this.level.StartNewLife();
        }
        else if (this.level.TimeRemaining == 0) {
            if (this.level.ReachedExit) {
                // If CSS3 Transitions is supported
                // We're using smooth & nice effects between each levels
                if (Modernizr.csstransitions) {
                    this.loadNextLevel = true;
                    // Setting the moveRotation class will trigger the css transition
                    this.platformerGameStage.canvas.className = "moveRotation";
                }
                // If CSS3 Transition is not supported, we're jumping directly
                // to the next level
                else {
                    this.LoadNextLevel();
                }
            }
            else
                this.ReloadCurrentLevel();
        }
        this.platformerGameStage.removeChild(statusBitmap);
        overlayDisplayed = false;
    }

    this.wasContinuePressed = this.continuePressed;
};
Notice I’m using Modernizr to do a feature detection of CSS Transitions. We’re finally setting back the initialRotation class inside the LoadNextLevel function:
// Loading the next level 
PlatformerGame.prototype.LoadNextLevel = function () {
    this.loadNextLevel = false;
    // Setting back the initialRotation class will trigger the transition
    this.platformerGameStage.canvas.className = "initialRotation";
    // ... loadNextLevel logic stuff...
};
Note: You may have noticed that I’m not setting the same transformation (and animation) for WebKit as for IE10 and Firefox in my previous CSS block. This is because, in my case, Chrome won’t behave the same way as IE10 and Firefox 11. I’ve set up a simple reproduction case on jsFiddle.

Demo video and URL

Here is a short video demonstrating the IE10 features covered in this article: You can also play with this demo in IE10 or your favorite browser here: Modern HTML5 Platformer. In the next part of this article, I’ll show how to implement offline API to make my game work without network connections, and how drag-and-drop API can create another cool feature.

Frequently Asked Questions about Modernizing Your HTML5 Canvas Game

What are the benefits of using HTML5 Canvas for game development?

HTML5 Canvas is a powerful tool for game development. It allows developers to create rich, interactive graphics and animations directly in the browser without the need for plugins. This makes games more accessible to a wider audience, as they can be played on any device that supports a modern web browser. Additionally, HTML5 Canvas games are easy to distribute and monetize, as they can be hosted on any website and do not require users to download or install anything.

How can I optimize my HTML5 Canvas game for better performance?

There are several ways to optimize your HTML5 Canvas game for better performance. One of the most effective methods is to use offscreen canvases. This involves rendering complex parts of your game to a separate canvas and then drawing that canvas onto the main canvas. This can significantly reduce the amount of time it takes to render each frame, resulting in smoother gameplay. Other optimization techniques include using requestAnimationFrame for game loops, minimizing the number of draw calls, and using web workers for heavy computations.

How can I handle user input in my HTML5 Canvas game?

Handling user input in an HTML5 Canvas game can be done using the standard JavaScript event handling system. You can listen for keyboard and mouse events using the addEventListener method, and then update your game state based on the input. For touch events, you can use the touchstart, touchmove, and touchend events. It’s important to remember to prevent the default behavior of these events to stop the browser from scrolling or zooming.

How can I add sound to my HTML5 Canvas game?

Adding sound to an HTML5 Canvas game can be done using the Web Audio API. This API provides a high-level way to create, manipulate, and play sounds. You can load sound files using the fetch API, decode them into audio buffers, and then play them using an AudioContext. You can also control the volume, pitch, and spatial position of sounds, allowing for a rich audio experience.

How can I make my HTML5 Canvas game responsive?

Making an HTML5 Canvas game responsive involves adjusting the size and layout of the game based on the size of the viewport. You can do this by listening for the resize event and then updating the width and height of the canvas. You may also need to adjust the size and position of game elements to ensure they fit within the new dimensions. It’s important to remember to use devicePixelRatio to ensure your game looks sharp on high-DPI displays.

How can I debug my HTML5 Canvas game?

Debugging an HTML5 Canvas game can be done using the developer tools built into most modern web browsers. These tools allow you to inspect the DOM, view console logs, and step through your code. You can also use the CanvasRenderingContext2D method to draw debugging information directly onto the canvas. This can be useful for visualizing collision detection, pathfinding, and other complex game mechanics.

How can I add multiplayer functionality to my HTML5 Canvas game?

Adding multiplayer functionality to an HTML5 Canvas game can be done using WebSockets. This technology allows for real-time, bidirectional communication between the client and server. You can use this to synchronize game state between multiple players, allowing them to interact with each other in real time. Implementing multiplayer can be complex, as it requires careful consideration of networking, latency, and security issues.

How can I save and load game state in my HTML5 Canvas game?

Saving and loading game state in an HTML5 Canvas game can be done using the Web Storage API. This API provides a way to store key-value pairs in the user’s browser, which can be used to persist game state between sessions. You can store simple data types like numbers and strings, as well as complex data types like objects and arrays by converting them to JSON.

How can I animate characters and objects in my HTML5 Canvas game?

Animating characters and objects in an HTML5 Canvas game can be done using sprite sheets. A sprite sheet is a single image that contains multiple frames of an animation. You can draw different frames onto the canvas at different times to create the illusion of movement. This technique can be used to animate characters, enemies, projectiles, and more.

How can I add physics to my HTML5 Canvas game?

Adding physics to an HTML5 Canvas game can be done using a physics engine like Box2D or Matter.js. These libraries provide a way to simulate realistic physics, including gravity, collisions, and forces. You can use this to make your game feel more immersive and interactive. Implementing physics can be complex, as it requires a good understanding of mathematics and physics principles.

David RoussetDavid Rousset
View Author

David Rousset is a Senior Program Manager at Microsoft, in charge of driving adoption of HTML5 standards. He has been a speaker at several famous web conferences such as Paris Web, CodeMotion, ReasonsTo or jQuery UK. He’s the co-author of the WebGL Babylon.js open-source engine. Read his blog on MSDN or follow him on Twitter.

HTML5 Dev Center
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week