Improving DOM Performance for HTML5 Games
By Louis Stowasser
So you’ve chosen to use the DOM for your game instead of SVG or canvas. Great! Using the DOM is a great all-around solution, and this article will look into four ways to update game objects in the DOM to find which performs best.
The tests used for the graphs involved changing the position of game objects and updating many many times. Only some objects were changed randomly to make it more fair.
The test script used to benchmark these methods is here. Feel free to run it and to leave your feedback in a comment at the end of this post.
Test 1: InnerHTML
innerHTML is the oldest way of updating content on a web page, and it’s the fastest and the most widely used method of creating DOM elements. Any Element object will have this property. Once assigned a value it will parse the value as HTML creating new Element objects from the string.
This test involves a loop in which each iteration re-renders the DOM tree by using innerHTML and returning the HTML string from the game objects.
Test 2: Modify all the style properties
A fairly standard method in game development, this test sets the style properties to their new value, such as left, top, width, height and background, on every render(). With all the calls one after another, only one Repaint event should be triggered, so in theory this should be the quickest.
However, this might seem counterintuitive because we are setting properties that may not have changed and are therefore wasting time. This leads us to the next test.
Test 3: Modify changed properties
Similar to the last test, when it is time to render we check if the property has changed by storing the last value. If it has changed, then the style property is updated. The problem is that more code needs to be written, more memory is used, and more Repaint events might be triggered, thus reducing performance.
Test 4: Setters
The last test is a bit different — Instead of having a game loop that renders our game objects iteratively, we use setters that only re-render when modified. A setter, or accessor, is just like using a property except we can decide exactly what happens when setting or getting it.
Note: Compatibility isn’t great with setters. Webkit and Mozilla supports __defineSetter__ and even though is deprecated, seems to be the fastest. Opera and IE9 can make use of Object.defineProperty.
In this test there is a getter and setter for the property x. When x is assigned a value the setter is invoked. The setter then changes the style property (left) to the new value.
Now, when the loop runs, only the properties that were modified will be updated, and they’ll be updated as soon as they are changed — not when the next iteration of the game loop occurs. This has many benefits but also causes some problems. Say we need to set lots of properties and frequently — updating a property means that more than one setter function is executed and multiple Repaint events are fired per loop iteration.
Enough speculation. Let’s look at some graphs!
We can see from the graphs that checking the value before modifying is the fastest method, and the second-fastest is to use setters.
In my earlier version of the tests, the background image was reapplied on each render which was drastically affecting the performance. Once I removed this the tests doubled in speed. Thus, a handy tip: don’t update the image on every render.
Setters look like a nice solution as you don’t need a game loop to maintain the rendering and setters are quite quick. But in games this is not always ideal; updating many properties at once would mean a function call for every changed property, not to mention a Repaint event for every modification. Also, due to compatibility issues mentioned earlier, using setters isn’t really practical.
All of the browsers report innerHTML as the slowest method so that is out of the question.
The conclusion we can draw is you should keep track of your values and only modify the property if it needs changing. The other crucial thing to notice is updating the image, such as the background property, affects the performance drastically, even if it is the same image.
It’s suprising to see that modifying the properties even where nothing has changed has quite an affect on performance. My initial speculation was that the browser would know if it needs to change, but this appears to not be the case. Checking if the value changed before setting it is faster.（Source：html5grind）