Five things you can do to make HTML5 perform better
Friday, January 25th, 2013 at 5:33 pmDuring the last few weeks we were busy helping developers to convert their HTML5 apps from platforms like WebOS and ChromeOS to FirefoxOS and the target hardware this operation system is right now aiming for. As these are slow mobiles, we found that quite some tweaking had to be done. The reason was in most cases libraries using outdated ways to animate and position things on the screen. These old ways of working are needed to support legacy browsers and in a lot of cases are not needed as these legacy browsers will never see the apps in the first place.
Aside: I will cover a lot of this in my keynote at the jQuery Europe conference called “Helping or hurting?”.
Tip 1: Use CSS animations/transitions
Instead of using your library’s animate()
which – for now – uses many badly performing technologies (setTimeout, top/left positioning) use CSS animations. In many cases actually Transitions are enough. The reason is that browsers optimise them for you and hardware accelerate them. Another benefit is that the animation is defined in the CSS with the rest of the look and feel and in an agreed standard syntax. CSS animations give you massively granular control over the effect using keyframes and you can even listen to events that happen during the animation. You can easily trigger the animations in CSS with hover, focus or target selectors or by dynamically adding and removing classes to a parent element. If you want to create animations on the fly or modify them in JavaScript, James Long has written a simple library for that called CSS-animations.js
Tip 2: Use requestAnimationFrame()
instead of setInterval()
This has been explained many times – a setInterval()
call runs code at an assumed frames per second speed that may or may not be possible. It tells the browser to render results even whilst the browser is currently not drawing as the video hardware is not in the next display cycle. On the other hand, requestAnimationFrame()
will wait till the browser is ready and only send your changes when they will result in a visual display. Another great side-effect for using rAF is that animations don’t happen when the tab is inactive – that way your app is not responsible for hammering the CPU/GPU and sucking battery whilst it doesn’t do anything for the end user.
Tip 3: Use CSS transforms instead of position: absolute
and top/left
Again the reason is hardware acceleration. CSS transforms using translate(x, y)
run on the GPU whereas positioning absolutely and using top and left hammer the main processor. This is worsened by not rounding your pixel positioning. Using transforms also means that you have a much bigger arsenal at your proposal. Rotation and 3D transformations just mean adding to the transform string. With top/left you need to calculate them yourself and mess around with depth of field. Paul Irish has an in-depth analysis of the benefits of translate()
from a performance point of view. In general, however, you have the same benefits you get from using CSS animations: you use the right tool for the job and leave the optimisation to the browser. You also use an easily extensible way of positioning elements – something that needs a lot of extra code if you simulate translation with top and left positioning. Another benefit is that the technique is the same in Canvas.
Tip 4: Make events immediate
As old-school, accessibility aware web developers we love click events as they also come with the added benefit of supporting keyboard input. On mobile devices these are too slow though and you should use touchstart
and touchend
instead. The reason is that these don’t have a delay that makes the interaction with the app appear sluggish. If you test for touch support first, you don’t sacrifice accessibility either. The Financial Times use a library called fastclick for that purpose, which is available for you to use.
Tip 5: Keep it simple and re-use as much as you can
One big performance issue we found in HTML5 apps was that moving lots of DOM elements around makes everything sluggish – especially when they feature lots of gradients and drop shadows. Simplyfying your look and feel and moving a proxy element around when you drag and drop helps a lot.
When you have for example a long list of elements (let’s say Tweets) don’t move them all but only keep the ones visible in the current screen and a few before and after live and hide or remove the others. Keeping the data in a JavaScript object instead of reading/writing to the DOM showed massive improvements. Think of the display as a presentation of your data rather than the data itself. That doesn’t mean you can not use straight HTML as the source. Just read it once and then scroll 10 elements changing the content of the first and last accordingly to your position in the results list instead of moving 100 elements that aren’t visible. The same trick applies in games to sprites – if they aren’t in the current screen there is no need to poll them. Instead re-use elements that scroll off screen as new ones coming in.
More reading:
(Includes videos and all)
- Jake Archibald’s Snow in Canvas Land shows how he sped up some old code by switching to
requestAnimationFrame()
and re-using objects instead of constantly creating new ones - James Long: Making (good) webapps – some Firefox OS specific things, but good overall tips
- Why moving elements with
translate()
is better than pos:abs top/left is Paul Irish’s in-depth analysis of the differences - Patrick Lauke’s German article on touch events (use Google translate – the code and the demos are the important bits)
- Video of James Long’s Writing Firefox OS HTML5 Apps using Mortar talk
- A great collection of App performance articles by Paul Lewis