I recently came across Christian Heilmann’s Five things you can do to make HTML5 perform better article, where point number three is to use CSS Transforms instead of position and top/left when moving elements across a page. It is a practice increasingly championed, such as in the noted Paul Irish video, Why Moving Elements With Translate() Is Better Than Pos:abs Top/left. The central point is when animating you’ll achieve higher frame rates using this transform code:
element.style["-webkit-transform"] = "translate(xPos, yPos)";
than this code:
element.style.position = "absolute"; element.style.top = xPos; element.style.left = yPos;
Both will change the displayed element position on a web page, though if performance is no object you may choose to semantically differentiate the techniques for different purposes. Translate also allows non-integral values which can result in smoother animations when moving an element slowly over a long period of time.
I still found the advice of always preferring to use transforms surprising; a while ago in developing Tumult Hype I had run some tests on the two different techniques and recalled transform not necessarily being faster. But, browsers have changed a lot since then. I couldn’t find any readily available benchmarks online, so I decided to make a new test and measure to see whether the advice of always using transform was valid.
The Benchmark Test
For my test, I wanted to compare various aspects involved in changing positions and compositing a page. These are:
- transform:translate vs. position:absolute and top/left
- Graphics acceleration on vs. off (forced via rotateY hack)
- Opaque elements vs. partially transparent elements
The test itself is relatively simple, it generates n boxes and animates them from point A to point B. It runs the 16 combinations serially. I’ve posted the test online so others can run it and pick it apart if need be:
My desktop testing system is a 27″ iMac (Mid 2011) with a 3.4 Ghz Intel Core i7 processor and AMD Radeon HD 6970M 1024 MB Graphics card. I also tested on an iPad Mini and iPhone 5. FPS on the desktop were taken using Quartz Debug and Instruments for iOS. I ran the test several times to make sure the results were repeatable. The FPS itself has some degree of eye-balling; the main point was to relatively compare various techniques.
All numbers are in frames per second. Because Firefox’s performance was too slow to draw conclusions, I re-ran using only 500 boxes to help bring out results from the tests. I also used 500 boxes for testing Mobile Safari for the same reason. For fun, I ran IE 10 in VMWare to see how it performed (surprisingly well), but don’t count the results as valid since it isn’t a real world setup.
- Setting the top/left properties will be faster than using a transform if you are not using transitions
- If the target is WebKit, the fastest frame rates will come from using transitions with the translate property, and forcing graphics acceleration for Safari/Mobile Safari (Chrome automatically does this)
- If compositing non-opaque items, forcing graphics acceleration in WebKit will have a huge performance boost in Safari/Mobile Safari and a modest boost in Chrome
- If compositing only opaque items, forcing graphics acceleration in WebKit will have a negative impact on performance
- The rotateY(0deg) method to force graphics acceleration has no significant effect on Firefox
- Safari is ~1.5x faster than Chrome across the tests
- Firefox is ridiculously slow at this test, even compared to IE 10 running through VMWare — Update: see below.
Q: What does Tumult Hype use for its animations?
Unfortunately there’s no straightforward answer! Tumult Hype v1.6 prefers to use top/left positioning with requestAnimationFrame() and graphics acceleration enabled. In some cases we may automatically disable graphics acceleration or use transforms to workaround specific browser bugs. There’s a checkbox in the Document inspector to disable graphics acceleration if manual intervention is required. On iOS we cannot use requestAnimationFrame() at all due to a Mobile Safari bug*, and clearly cannot use it on older browsers because it’s not supported. Tumult Hype v1.0 used transitions when available for the best performance, however we encountered issues keeping animations synchronized, and this technique would not work with newer features such as our bounce effect and pausing/resumes/go-to-time features so it was abandoned in v1.5.
Q: Why shouldn’t I always use CSS Transitions?
- They are difficult to programmatically manipulate.
- They are missing many features which might be required for interactivity (getting current positions, synchronizing with other animations, pausing, etc.).
David Poyner has an elaboration I agree with.
Q: Why use transforms if not using CSS Transitions or CSS Animations?
- You may encounter browser bugs*. They go in both directions and sometimes using top/left is the only workaround.
I recommend reading Zachary Johnson’s comment for more information.
Q: Why not always use the graphics acceleration hack?
- It’s a hack! Browsers may be able to make more intelligent decisions, and in some cases are faster (opaque elements).
- You may encounter browser bugs*.
- It could have battery life implications.
This is admittedly a simple and singular test I hope accurately covers general performance characteristics which might be seen in the real world. Because the GPU is a factor, it would be interesting to test against different hardware. Completely missing is a test on CSS Animations, but my assumption is their performance characteristics are the same as transitions. With more time, I would vary box sizes, textures, and numbers. I would also test different effects known to cause performance issues such as box shadows and blurs. I’d also test against the latest WebKit nightlies and Chrome Canary builds, perhaps compare against older versions of the browsers, and run a native tests on Windows and Android. I’m also curious about scale performance vs. width/height.
There’s no single path to best performance where web browsers are concerned. The first step in achieving performance for your site is measurement.
* List of Related Browser Bugs We’ve Hit
<rdar://problem/9764859> 3d webkit-transforms intersect elements inconsistently
<rdar://problem/9973514> Hardware compositing breaks full screen video in Safari 5.1
<rdar://problem/10346853> REGRESSION: webkit-transform:rotate causes other css properties to not change
<rdar://problem/10506553> Box-shadow not always drawn when an element is rotated and animating
<rdar://problem/10543798> Table border not drawn when there’s a 3D transform
<rdar://problem/10737092> contenteditable cursor displayed incorrectly with translates and 3D rotations
<rdar://problem/10786551> 3D transformed div has aliased/jagged edges
<rdar://problem/12307742> CSS3 Filter Effects rendered inconsistently across Safari CPU path, GPU path, iOS, and Chrome
<rdar://problem/12363449> webkitRequestAnimationFrame does not work between pages on Mobile Safari
<rdar://problem/12628214>/Chromium 159253 Changing background-color and left at the same time does not work
<rdar://problem/12722122> elements with 3d transforms non-retina if they are in two divs with perspective
<rdar://problem/13043725> Child with rotateY is not clipped by parent with overflow:hidden and border-radius
Chromium 161423 Blur CSS3 Filter Effect does not blur outside the bounds of the div
Chromium 164703 Rendering artifacts on page while rotating element on z axis
Mozilla has found the performance issues with Firefox 19 are the result of a regression, and are tracking with https://bugzilla.mozilla.org/show_bug.cgi?id=849263.