Click here to Skip to main content
13,767,441 members
Click here to Skip to main content
Add your own
alternative version

Tagged as


19 bookmarked
Posted 7 Dec 2012
Licenced CPOL

Modernize Your HTML5 Canvas Game Part 2: Offline, Drag-and-Drop, and File APIs

, 7 Dec 2012
Rate this:
Please Sign up or sign in to vote.
Modernize Your HTML5 Canvas Game Part 2: Offline, Drag-and-Drop, and File APIs

Editorial Note

This article is for our sponsors at CodeProject. These articles are intended to provide you with information on products and services that we consider useful and of value to developers

Develop a Windows 8 app in 30 days

HTML5 features in modern browsers like Internet Explorer 10 are making possible a whole new class of web applications and gaming scenarios. This two-part article demonstrates how I’ve used some of these new features to modernize my last HTML5 game, HTML5 Platformer.

In part 1 of this article, I covered how to use CSS3 3D Transform, Transitions, and Grid Layout for an HTML5 Platformer game. In part 2, I’ll show you how to use the Offline, Drag-and-Drop, and File APIs to implement some interesting new ideas.

Playing a game in offline mode

The original version of my game only worked if your device was currently connected to the Internet. So, if you wanted to play to my fabulous game while you were on the train, in a taxi, or somewhere else without an Internet connection, you were out of luck—stuck without access to the awesomeness. And that’s too bad because there really isn’t anything in my game that needs a " live" connection to the web server once all the resources have been downloaded. Fortunately, offline APIs provide a solution for this in HTML5.

Step 1: Choosing the resources you’d like to cache

It’s actually pretty easy to tell the browser which resources you’d like to cache for offline use. But before you go any further, I recommend reading these two articles:

For my game, I built a file named platformer.cache that looks like this:


# Version 1.5

.. up to ..
.. up to ..


.. up to ..

I inserted all the PNG files containing my sprites, background layers, and overlays; the necessary JS files from the EaselJS framework; as well as my own gaming logic and the main HTML and CSS files. Then, I simply indicate that I’d like to use this manifest file in my main HTML page. In this case, it’s " index.html" :

<!DOCTYPE html>
<html manifest="platformer.cache">
    <title>HTML5 Platformer Game</title>
    // ... 

Please note that your manifest file should be served as "text/cache-manifest" . For my game, I added a new " .cache" content type mapped to " text/cache-manifest" because it’s stored in the blob storage of Windows Azure.

Be aware that this specification doesn’t allow delta changes. Even if only one of your files has changed, you need to force a complete re-download for the browser to update to the new version. However, any change to your manifest file will be detected by the browser, which will then re-download all the resources specified inside it. The change could be a version number, a date, a GUID—whatever works for you—at the beginning of the file via a comment ("Version 1.5" in the above example).

Step 2: Modifying the logic for loading the levels

The original version of my code downloaded each level via an XHR call to the web server. I will need to change that to make my game run in offline mode. Also, I’d like to indicate to the user that he’s currently playing in offline mode by adding the "official" HTML5 associated logo inside the gaming canvas.

Let’s go through the changes to make it happen. The first time a user launches my game, all the levels will be downloaded (described into {x}.txt files) into the local storage. This is widely supported (since IE8) and very easy to use. And most important, it’s available in offline mode.

Here is the code I added inside the " PlatformerGame.js" :

PlatformerGame.prototype.DownloadAllLevels = function () {
    // Searching where we are currently hosted
    var levelsUrl = window.location.href.replace('index.html', '') + "levels/";
    var that = this;

    for (var i = 0; i <
 numberOfLevels; i++) {
        try {
            var request = new XMLHttpRequest();
  'GET', levelsUrl + i + ".txt", true);
            request.onreadystatechange = makeStoreCallback(i, request, that);
        catch (e) {
            // Loading the hard coded error level to have at least something to play with
            //console.log("Error in XHR. Are you offline?"); 
            if (!window.localStorage["platformer_level_0"]) {
                window.localStorage["platformer_level_0"] = hardcodedErrorTextLevel;

// Closure of the index 
function makeStoreCallback(index, request, that) {
    return function () {
        storeLevel(index, request, that);

function storeLevel(index, request, that) {
    if (request.readyState == 4) {
        // If everything was OK
        if (request.status == 200) {
            // storing the level in the local storage
            // with the key "platformer_level_{index}
            window.localStorage["platformer_level_" + index] = request.responseText.replace(/[\n\r\t]/g, '');
        else {
            // Loading a hard coded level in case of error
            window.localStorage["platformer_level_" + index] = hardcodedErrorTextLevel;

        if (numberOfLevelDownloaded === numberOfLevels) {

All the levels in the PlatformerGame constructor will be downloaded asynchronously. Once all the levels have been downloaded (numberOfLevelDownloaded === numberOfLevels), the first level loads. Here is the code for the new function:

// Loading the next level contained into the localStorage in platformer_level_{index}
PlatformerGame.prototype.LoadNextLevel = function () {
    this.loadNextLevel = false;
    // Setting back the initialRotation class will trigger the transition
    this.platformerGameStage.canvas.className = "initialRotation";
    this.levelIndex = (this.levelIndex + 1) % numberOfLevels;
    var newTextLevel = window.localStorage["platformer_level_" + this.levelIndex];

The beginning of the code handles the CSS3 transitions as described in the previous article. The game will simply access the local storage via the appropriate key to retrieve the previously downloaded content.

Step 3: Checking online/offline and displaying a logo when launched in offline mode

Two tests are required to confirm a game is running in offline mode. The first is whether the browser has implemented offline/online events, as most modern browsers do. If the browser says the user is offline, it’s confirmed, and the game should immediately switch to the offline logic. Often, this simple check is not enough, though. The browser may say it’s online, but it doesn’t know if the web server is still online or not. So you need to do a second check by pinging the server with a simple XHR.

Here is my code for both these checks:

PlatformerGame.prototype.CheckIfOnline = function () {
    if (!navigator.onLine) return false;

    var levelsUrl = window.location.href.replace('index.html', '') + "levels/";

    try {
        var request = new XMLHttpRequest();'GET', levelsUrl + "0.txt", false);
    catch (e) {
        return false;

    if (request.status !== 200)
        return false;
        return true;

My test is to try to download the first level. If that fails, it switches to the offline part of my code. Now, here’s the code launched in the constructor part of the PlatformerGame.js:

PlatformerGame.IsOnline = this.CheckIfOnline();

// If we're online, we're downloading/updating all the levels
// from the webserver
if (PlatformerGame.IsOnline) {
// If we're offline, we're loading the first level
// from the cache handled by the local storage
else {

And here is the code displaying the offline logo in Level.js in the CreateAndAddRandomBackground

if (!PlatformerGame.IsOnline) {
    offlineLogo.x = 710;
    offlineLogo.y = -1;
    offlineLogo.scaleX = 0.5;
    offlineLogo.scaleY = 0.5;

With these changes implemented,here is what my game looks like when launched without network connection:



The offline logo is displayed just before the frame rate, indicating to the user that the game is currently running purely offline.  

Step 4: Conditionally downloading the MP3 or OGG files and storing them as blob in IndexedDB 

This is something I’ve not implemented, but I’d like to share the concept with you as a bonus so you can implement it yourself!  

You may have noticed that I didn’t include my game’s sound effects and music in the manifest file of step 1. When I wrote this HTML5 game, my first goal was to be compatible with the largest number of browsers possible. To accomplish that, I have two versions of the sounds: MP3 for IE and Safari, and OGG for Chrome, Firefox, and Opera. The content download manager only downloads the type of codec supported by the current browser launching my game. That’s because there’s no need to download the OGG version of the files if I’m playing it inside IE, and no need to download the MP3 version for Firefox.

The problem with the manifest file is that you can’t conditionally indicate which resource to load based on the current browser’s support. I’ve come up with three solutions to work around this limitation:

  1. Download both versions by putting all file versions inside the manifest file. This is very simple to implement and works fine, but users will be downloading some files that will be never used by some browsers.
  2. Build a server-side dynamic manifest file by sniffing the browser agent to guess the codec supported. This is definitely a very bad practice!
  3. Use a client-side feature to detect the codec support in the content manager object and then download the appropriate file format in IndexedDB or in the local storage for offline use.

I believe the 3rd solution is the best, but you need to be mindful of a couple things to make it work:

  • If you’re using local storage, you’ll need to encode the files in base64, and you may run into quota limits if you have too big/many files.
  • If you’re using IndexedDB, you can either store the base64 encoded version of the files or store them as a blob.

The blob approach is definitely the smarter and more efficient solution, but it requires a very up-to-date browser like the last version of IE10 (PP5) or Firefox (11). If you’re curious about this idea, check out our Facebook Companion demo from our IE Test Drive site here:



You’ll find more details about this demo in this article: IndexedDB Updates for IE10 and Metro style apps

In the game version supplied with this article, I decided to cache all formats (solution 1). I may update that in a future article by implementing an IndexedDB caching. Stay tuned!

Drag-and-drop and File APIs

Here’s a fun new feature that takes advantage of the new Drag-and-Drop and File APIs. The user can create/edit a level using his favorite text editor, then simply drag and drop it from his file explorer directly into the HTML5 game and play it!

I won’t go into too much detail about drag and drop as it has been very well covered in this article: HTML5 Drag and Drop in IE10, which explains how the Magnetic Poetry demo was built. I recommend reading the article first to fully understand the code below.

For my game, I created the dragDropLogic.js file containing this code:

(function () {
    "use strict";

    var DragDropLogic = DragDropLogic || {};

    var _elementToMonitor;
    var _platformerGameInstance;

    // We need the canvas to monitor its drag&drop events
    // and the platformer game instance to trigger the loadnextlevel function
    DragDropLogic.monitorElement = function (elementToMonitor, platformerGameInstance) {
        _elementToMonitor = elementToMonitor;
        _platformerGameInstance = platformerGameInstance;

          _elementToMonitor.addEventListener("dragenter", DragDropLogic.drag, false);
          _elementToMonitor.addEventListener("dragover", DragDropLogic.drag, false);
          _elementToMonitor.addEventListener("drop", DragDropLogic.drop, false);

    // We don't need to do specific actions
    // enter & over, we're only interested in drop
    DragDropLogic.drag = function (e) {

    DragDropLogic.drop = function (e) {

        var dt = e.dataTransfer;
        var files = dt.files;

        // Taking only the first dropped file
        var firstFileDropped = files[0];

        // Basic check of the type of file dropped
        if (firstFileDropped.type.indexOf("text") == 0) {
            var reader = new FileReader();
            // Callback function
            reader.onload = function (e) {
                // get file content
                var text =;
                var textLevel = text.replace(/[\s\n\r\t]/g, '');
                // Warning, there is no real check on the consistency
                // of the file. 
            // Asynchronous read

    window.DragDropLogic = DragDropLogic;

This code is called inside main.js in the startGame function:

// Callback function once everything has been downloaded
function startGame() {
    platformerGame = new PlatformerGame(stage, contentManager, 800, 480, window.innerWidth, window.innerHeight);
    window.addEventListener("resize", OnResizeCalled, false);
    DragDropLogic.monitorElement(canvas, platformerGame);

That’s all there is to it! For instance, copy/paste this text block into a new " .txt" file:


And drag and drop it into my game. The new level will be loaded like magic!



Demo and source code

If you’d like to see a demonstration in IE10 of all the features implemented in this article, check out this short video:


Download Video: MP4, WebM, HTML5 Video Player by VideoJS

You can also play with this demo in IE10 or your favorite browser here: Modern HTML5 Platformer


Since you’ve been kind enough to read this entire article, please enjoy the complete source code here: HTML5 Modern Platformer Source Code


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

David Rousset
United States United States
David Rousset is a Senior Program Manager working for Microsoft Corp, in charge of driving adoption of HTML5 standards. He was a speaker in 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 @davrous on Twitter.

You may also be interested in...

Comments and Discussions

GeneralMy vote of 5 Pin
Aniruddha Loya10-Dec-12 23:41
memberAniruddha Loya10-Dec-12 23:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01-2016 | 2.8.181116.1 | Last Updated 7 Dec 2012
Article Copyright 2012 by David Rousset
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid