IE9 – Debugging a Canvas Game

A few weeks ago, I discussed one compatibility issue we’d found when running a new HTML5 game. The game’s developers quickly fixed their site to return a proper character set declaration and we were able to get the game running in IE9. However, after playing the game for about 5 seconds, it would invariably hang the browser tab.

What went wrong?

The problem is that the site was relying upon a problematic pattern in the logic that would copy an image to the game board’s <canvas>. The code was similar to this:

// WARNING: Dangerous pattern. Do not copy!!
imgToDraw = document.getElementById("IMGSpriteToDraw");
(imgToDraw.height > 0)
canvasGame.drawImage( /* arguments elided */);
catch(e){ SetTimerForRetry(); }

Attempting to copy an image to a canvas using drawImage() will fail if that image hasn’t yet finished downloading. Hence, the intent of this code’s imgToDraw.height check is to ensure that the IMG tag containing the desired sprite has completed its download before trying to draw to the canvas. If it hasn’t, the code block calls a function which uses setTimeout() to try again in a short time.

We quickly tracked down the hang to the fact that the SetTimerForRetry() function was being called thousands of times; because it contained no logic to coalesce the timers into a animation frame or “beat”, instead of trying, say, 60 times per second, the code was trying thousands of times, each of which resulted in a failure that queued up another retry.

After we pointed this out, the game developers developed a simple paint queuing system that prevented a large stack of redundant timers from being created, and the game stopped hanging. However, the game board still wasn’t being painted properly, and the root cause of the problem explained why the stacking of timers occurred at all. As we debugged the game further, we realized that the problem was that imgToDraw.height was always 0. A quick look with the F12 Developer Tools showed that the image was successfully downloading and being added to the DOM. The HTML tab yielded a clue however:

<img id="IMGSpriteToDraw" src="sprite.png" style="display: none" />

When the style attribute was removed, the game began running correctly, albeit with a minor display glitch because a duplicate copy of the sprite appeared next to the canvas.

So, why does the style matter?

The developer was using a standard image tag with display: none styling to allow the browser to download his sprites without needing to write any extra JavaScript. He didn’t want the sprites to appear on the canvas, however, so he styled them to not display in the page.

The problem: In IE, when an image has the style display: none, the image.height and image.width properties will always return 0. The reason is that these values are returned from the layout subsystem, but display: none images do not participate in layout. Had the developer styled these images using visibility: hidden instead of display: none, IE will still calculate a layout size for the element and the code would have worked as expected.  Alternatively, the code could use the naturalHeight or naturalWidth properties, which always return the intrinsic size of the image, even when it is not displayed.

Another alternative would be to forgo the direct size check and instead use one or more onload handlers to detect when content is fully available. Since this sites images were all directly in the markup, an onload handler on the body would work too: IE will fire the onload for the body only after all the images have finished their downloads.

After tweaking the code to correctly detect when the image was available, the game now runs correctly in IE—it makes great use of IE9’s hardware-accelerated HTML5 features, and it’s a lot of fun too!


Comments (6)

  1. Aryeh Gregor says:

    IE's behavior here for img.height/img.width does not match the specification, which says the intrinsic height/width must be returned if the image isn't being rendered:…/embedded-content-1.html. Are there plans to fix it?

  2. @Aryeh: Yes, we've filed a bug for the next version of Internet Explorer. Thanks.

  3. hAl says:

    Looking at  Futuremarks Peacekeeper browser benchmark it seems to not invoke chakra's  JIT compalition.

    The 64it version of IE9 is equally as fast as the 32 bits version which has the JIT compilation.

    This is strange as you would expect the 32-bit version with JIT compilation to be MUCH faster on a benchmark test.

    Could you debug why a site like futuremarks benchmakr does not go into JIT compilation even thouth you are using the debugger which itself is not JIT compiling?

    Could a site developer easily see that his site is not invoking the fast performance it would like to have for its users?

  4. John says:

    It's cool how you guys are helping developers like this. Any chance you could look at this site? It completely fails in IE9, and I have no idea why. I emailed the developer and he said he doesn't have access to a Windows machine right now.

  5. EricLaw [MSFT] says:

    @John: The WeaveSilk site is cool, but he's doing user-agent sniffing somewhere. If you use the F12 Developer Tools' Tools > Change User Agent string option to change the UA String to Google Chrome, you'll find that the site works just fine.

    The site is using JQuery 1.4.4 internally, which may be the problem. JQuery released version 1.5.1 to fix bugs in their earlier version.

  6. @John: Yuri has fixed the WeaveSilk site.

Skip to main content