Thomas Fuchs
Hi, I'm Thomas Fuchs. I'm the author of Zepto.js, of, and I'm a Ruby on Rails core alumnus. With Amy Hoy I'm building cheerful software, like Noko Time Tracking and Every Time Zone and write books like
   Want me to speak at your conference? Contact me!

Making an iPad HTML5 App & making it really fast

June 4th, 2010

About a month ago or so, Amy and I release a little (literally, it’s about 5k) HTML5 iPad App for looking up time zones. I don’t mean select-box wasteland like all other time zone sites (who likes select boxes anyway?!), I mean a nicely polished, touch-enabled UI that works offline, too.

The site uses no images (‘cept for the ad), no JavaScript framworks, and no external CSS, and fits quite comfortably in a few k’s of gzipped HTML.

First visit the site on your iPad (desktop browsers work, too!), and play around with it a bit.

Our first attempt ran really great on the Apple-supplied SDK iPad Simulator. But when we got our real iPads, everything seemed slow and sluggish. We went in an optimized the perceived “snappyness” when dragging the the time bar with your finger, and about doubled the rate of frames per second rendered.

And here’s what we did:

1. Images slow things down immensely– get rid of them

At first, we used a quite complex -webkit-gradient for the gradients in the “day bars” for the various cities. It turns out the this really, really slows things down in terms of rendering speed on the iPad. What -webkit-gradient really does is construct an image bitmap, and to the rendering engine it’s exactly the same as if you would supply and externally loaded image (like a PNG file). Images on Mobile Safari are notoriously slow (hope this will improve in 4.0!), and there’s basically nothing you can do about this– except not using images and gradients.

We replaced those bars with a big canvas element that sits in the background, and we redraw only those portions of the screen that undergoes changes (the different highlighting when you drag around the green bar). You can sort-of reuse -webkit-gradient when using canvas, like this:

// WebKit CSS gradient
-webkit-gradient(linear, left top, right top, 
  color-stop(0.249, #4b4c4d),
  color-stop(0.25, #575b5c),
  color-stop(0.329, #575b5c),
  color-stop(0.33, #6b7071),
  color-stop(0.749, #6b7071),
  color-stop(0.75, #575b5c),
  color-stop(0.909, #575b5c),
  color-stop(0.91, #4b4c4d),

// <canvas> gradient
var gradient = $('canvas').getContext("2d").createLinearGradient(0,0,230,0);

2. Avoid text-shadow & box-shadow

Also a major source of slowdown. It’s best to avoid those CSS properties.

3. Hardware-acceleration is quite new… and buggy

On Safari, all you need to enable hardware-acceleration is to use the -webkit-transform CSS property (opacity also works, but see above).
There are limits to what works with hardware-acceleration, for example the number of concurrent animations is limited– there will be flickering and rendering errors if you have more then a handful of animations going on. Gently used, it makes things awesome however (can’t wait to share the new scripty2 hw-accel demos, coming next week I hope!).

4. Use touch events whenever you can

Touch events are great, because regular old onclick events only happen with a slight delay on the iPad. Opt for touch event instead– look into the source of the page for some hints on how you can provide support for non-touch-enabled browsers at the same time:

var supportsTouch = 'createTouch' in document;

element[supportsTouch ? 'ontouchmove' : 'onmousemove'] = function(event){
  var pageX = event.pageX;
  if (event.touches) pageX = event.touches[0].pageX;
  // ...

5. Avoid opacity

For some reason, using the opacity CSS property sometimes interferes with hardware-accelerated rendering, so if you experience slowdowns and think you’re doing everything right, see if you have opacity set somewhere.

6. There is no silver bullet– hand-code JavaScript and CSS

Don’t rely on frameworks or what standards zealots tell you. In our case, a highly trimmed HTML page, with inline CSS, just some pure JavaScript without a framework and using the capabilities of the target platform (iPad) as much as possible allows for a lean page that loads almost instantly, caches well and works great offline. Yes, we could have used JavaScript and CSS frameworks, but sometimes less is more (and remember, you don’t need all the cross-browser heavy lifting that frameworks do for you).

// mini-pico-tiny convenience micro-framework, ymmv
function $(id){ return document.getElementById(id); }
function html(id, html){ $(id).innerHTML = html; }
function css(id, style){ $(id).style.cssText += ';'+style; }
function anim(id, transform, opacity, dur){
  css(id, '-webkit-transition:-webkit-transform'+
    ',opacity '+(dur||0.5)+'s,'+(dur||0.5)+'s;-webkit-transform:'+

7. Use translate3d, not translate

When using -webkit-transform, be sure to use the translate3d(x,y,z) syntax, instead of using translate(x,y). For some reason, the latter is not hard-accelerated, at least not on iPhone OS 3.x (it seems to work fine on desktop Safari, tho). Thanks to Matteo Spinelli for pointing this out (check out his iScroll project for some a great code that demonstrates how to optimize performance on mobile WebKit)!

I think it’s quite exciting to develop apps with HTML5 on these devices, with almost complete desktop-like performance in most cases, and you can even distribute apps yourself, no need to go through the App Store for this.

I’d love to hear about your experiences, and quirks and tricks you may have come across.

Want to learn how to build your own AWESOME mobile HTML5 apps? Amy and I proudly present: HTML5 Mobile Pro Workshop, a half-day online workshop on September 20, 2010, on building really great mobile HTML5 apps. We’ve limited the workshop to 25 virtual seats—so hurry if you want to join!