Sunday, October 10, 2010

Scrolling Games in JavaScript

So I'd previously tried to program a game in JavaScript with a scrolling background, and repeatedly encountered performance problems. In the background of the game was about 100 images, positioned absolutely, that had to scroll as the player moved. These images were part of a larger map involving thousands of image tiles, so not all of the images could be created in advance--images that were not visible had to be removed and new images had to be created as for map tiles coming into view.

All the benchmark information available at the time talked a lot about how innerHTML to create new objects was much faster than manipulating the DOM by hand in JavaScript. This makes sense. JavaScript is slow, but in most browsers, DOM objects are separated from JavaScript by heavy COM or XPCOM abstraction layers, so if you can minimize the number of DOM calls you make, that should make your JavaScript faster. Based on this reasoning, I initially wrote my scrolling code by erasing all my objects and creating replacements with a single innerHTML call. Unfortunately, this was really slow (too slow for most games), but I was too busy to work out a better approach.

I recently found some time to take a second look at this, and I found that the performance issues were more comlex than that. In fact, there is an overhead for manipulating the DOM, but it's actually not too bad. Creating (and to a lesser extent deleting) DOM objects involves a much larger overhead. Also strange is that IE8 is much slower than IE6 for a lot of this DOM manipulation (probably because it's properly synchronized etc.).

So the correct way to create a scrolling background is not to restrict oneself to a single DOM call. Instead, to scroll a bunch of images, it's fastest to go through each image and move each one by an appropriate amount (by using divElement.style.left = '?px'). It results in hundreds of DOM calls, but it's much faster than creating new images. I was thinking of creating some sort of complicated DOM hierarchy, so that I could move multiple images with a single DOM call, but that wasn't necessary--moving each image individually was sufficiently fast. Of course, you need to create new images and remove old ones as new areas come into view or move out of view, but the performance saved by creating fewer new HTML elements counteracts the cost of the extra DOM calls.

So some quicky benchmarks reveal that when 100 moves of around 25 tiles, 128x128 in size, in 5 pixel increments take this amount of time (in milliseconds) :

IE8 with innerHTML complete refresh of all objects14563
Chrome with innerHTML complete refresh of all objects5843
FF3.5 with innerHTML complete refresh of all objects10918
IE8 with simple move elements around1919
Chrome with simple move elements around605
FF3.5 with simple move elements around1691
IE8 with simple move elements and reusing old elements1641


This means that it's probably possible to do simple games in IE8, so there's no immediate need to switch to using canvas and restricting oneself to non-IE browsers. I'm hoping that developers won't program their HTML5 games using only the canvas element because I think more traditional methods are sufficiently fast and more compatible. In fact, if I do move to use HTML5 features, I'm vaguely thinking of going with SVG, but I'll have to see how the performance evolves.

No comments:

Post a Comment