Two weeks ago Bruce and I released JSGameBench version 0.1. Today marks the release of version 0.2, a much faster and cleaner version. We continue to learn both from tightening the code and from the strong HTML5 community. Version 0.2 reinforces our belief in HTML5 as a strong, horizontal platform for games and highly interactive applications across the web.
Benchmarking
In order to talk about browser performance, we needed to standardize. We now have two machines that will be our testing machines:
Both of these laptops are significantly less powerful than the Mac Pro the original tests were run on. In addition, the 3100M offers approximately half the performance of the 330M.
More is Better
Thanks to help from the HTML5 community, JSGameBench has roughly doubled performance in the most widely used browsers in only two weeks. This a very promising development for the future of HTML5 games and interactive apps. The two most significant changes are clamping positions to integer values and using the transform/transform3d
style property.
Also, despite the far less powerful graphics cards and slower CPU in the Lenovo 410s, Win 7’s hardware accelerated browsers -- Firefox 4, Internet Explorer 9, and Chrome 11 -- continue to dominate, achieving scores above 1,000. Our new leader, Firefox 4, is well ahead of the pack.
Some Thank Yous
First, thanks to Bruno Garcia, a developer at Three Rings, who submitted a pull request clamping position values to integers. An obvious improvement for software render pipelines, but one we hadn’t thought to try. For axis-aligned sprites, clamping to integer values was faster in every browser except for Chrome 11 on Windows. A huge win and the majority of the performance gains. Thanks, Bruno!
Second, thanks to Zynga’s Laurent Desegur, Paul Bakaus and Rocco Di Leo. At the HTML5 tech talk, we had a chance to talk about HTML5 performance and some of their ideas. Paul also blogs extensively about HTML5 tricks.
Desktop Recommendations
For the desktop browsers, the following techniques generated the highest frame rates:
“parseInt”
and “| 0”
. We would expect using the or operator to be faster than a function call, but have not yet tested this. Note that snapping could generate visible artifacts, particularly at very low resolutions.DIV
tag with background image instead of IMG
tag. The difference is particularly noticeable when using individual sprites instead of sprite sheets, as changing the IMG
’s src property is very slow.CANVAS
tag, we expect an optimum balance of download and draw performance is to download sprite sheets but then use CANVAS
to cut them up.CANVAS
tag performance was far superior to DOM manipulation, so update and switch to CANVAS
when possible.CANVAS
tag was superior across all desktop browsers. This had not been the case with floating point values -- and indicates software blitting in their render pipelines -- but if you are snapping and drawing axis aligned images, use CANVAS
when you are able to.translate3d(x,y,z)
is equal to or faster than translate(x,y)
.In order to reduce JSGameBench’s testing time, we have chosen to use these recommendations -- positions snapped to integer values, DIV tags with background images, updating DOM elements, 3D transforms when possible, no CSS transitions or keyframes -- as our default test. One further tweak was to settling on the transform property “rotate” since “rotate“ versus “matrix“ generated no difference. We believe that HTML5 game developers and browser developers will gain the most if we optimize the smallest number of render paths, rather than constantly testing every possibility.
Mobile Differences
We thought the most interesting part of the HTML5 tech talk was how different our performance test results were. Much of this week was spent exploring where those differences might have come from, beyond normal testing variance. We believe the single largest reason for differences in our results comes from the iPhone -- or more specifically, Mobile Safari running on the iOS ecosystem. While many HTML5 techniques work well on Mobile Safari, truly wringing maximum performance will require more special case code than any other browser.
We have primarily been testing desktop browsers so far, but what follows are our preliminary points of data about iOS.
CSS
For Safari, both desktop and mobile, CSS transitions and animations are hardware accelerated. As we previously discussed, this acceleration is not a win on desktop, but on iOS, it generates intriguing results.
CSS transitions are pretty simple. For a given CSS property, such as position, CSS transitions provide a method to interpolate between the current value and the new value. For example, on a Webkit browser the following CSS:
#Foo {
-webkit-transition: left 0.1s linear;
left: 45px;
}
This will interpolate between Foo’s current left value and “45px” in 0.1 seconds using linear interpolation. (The Art of the Web has a nice discussion and demos of transition timing.) Generally, CSS transitions work for smoothly varying a CSS property.
CSS keyframe also provide a different approach, allowing you to set keyframes for a given property. Normally, those properties would still be smoothly interpolated, but as Paul Baukus discovered (and refined), by exploiting numerical precision limits in the browser, it is possible to snap between values.
For example, to use keyframes to run a 2 frame animation of the flickering drive flame on our spaceship, you would use a CSS keyframe like this:
@-webkit-keyframes 'ship' {
from { background-position: 0px 0px; }
49.99% { background-position: 0px 0px; }
50% { background-position: -128px 0px; }
to { background-position: -128px 0px; }
}
This example animates the background image position. To accomplish the same effect using a DIV element masking an IMG element, you would instead use:
@-webkit-keyframes 'ship' {
from { -webkit-transform: translate(0px,0px,0); }
49.99% { -webkit-transform: translate(0px,0px,0); }
50% { -webkit-transform: translate(-128px,0px,0); }
to { -webkit-transform: translate(-128px,0px,0); }
}
In both cases, you would associate the animation with the DIV via:
img.ship_animating {
-webkit-animation-name: 'ship';
-webkit-animation-timing-function: linear;
-webkit-animation-duration: 0.25s;
-webkit-animation-iteration-count: infinite;
}
Using HTML5 techniques form the desktop, both iOS and Android return values in the 20-30 sprites range, using a 20 fps baseline. By using the DIV with background position approach, performance on the iPhone increases by 25% or more, warranting further research.
Taking advantage of the second CSS keyframe technique -- using a DIV to mask an IMG tag -- generated similar performance increases, but began severely stuttering JS execution. We found similar results using CSS transitions to move the game objects: the JavaScript thread would execute erratically, likely as a result of the UI and render threads on iOS being higher priority.
CSS Only?
Viewing the thread starvation led us to try a further experiment. Rather than relying on JavaScript to update object positions, what performance levels are achievable if all of the sprite motion is in CSS? The results were impressive, with the iPhone 4 easily pushing over 100 moving, animating, and rotated sprites. Unfortunately, this level of performance is achieved at the expense of JavaScript execution, with the JS thread getting called at an erratic 2-5fps.
Is it possible to build a game using this approach? Almost certainly, but it is going to be a very quirky development path and iOS specific. Still intriguing, however.
Browser Improvements
As always, version 0.2 brings a list of requests to browser manufacturers:
That’s All for Now
We will continue to improve and refine JSGameBench in the coming weeks. We’ve begun to reorganize the code to better serve as a reference design for JS games and have a list of tasks ahead of us. We continue to be sure that we’ve missed good ideas and look forward to continued feedback. HTML5 continues to impress us and we are excited by what we will bring to JSGameBench next.