HTML5 performance techniques that pay off now

Most performance advice focuses on optimizing what you already send to the browser — compressing assets, reducing requests, setting cache headers. But the HTML5 generation of browser APIs changes the equation: in several cases you can simply avoid doing work on the wire altogether, or hand it to native browser code that runs faster than the JavaScript it replaces. None of this requires abandoning older browsers if you feature-detect and fall back cleanly.

Move persistent data out of cookies

Cookies have a structural flaw that no amount of optimization fixes: every cookie you set is appended to every HTTP request header, including XHRs. That overhead accumulates quickly and measurably degrades response times. The standard advice has been to shrink cookie payloads; a better answer is to stop using cookies for client-side data entirely.

sessionStorage and localStorage persist data on the client for the session or indefinitely, and their contents never travel to the server unless you explicitly send them. Their APIs are simple enough to use directly, and a cookie-based shim covers older engines:

// if localStorage is present, use that
if (('localStorage' in window) && window.localStorage !== null) {

  // easy object property API
  localStorage.wishlist = '["Unicorn","Narwhal","Deathbear"]';

} else {

  // without sessionStorage we'll have to use a far-future cookie
  //   with document.cookie's awkward API :(
  var date = new Date();
  date.setTime(date.getTime()+(365*24*60*60*1000));
  var expires = date.toGMTString();
  var cookiestr = 'wishlist=["Unicorn","Narwhal","Deathbear"];'+
                  ' expires='+expires+'; path=/';
  document.cookie = cookiestr;
}

Animate with CSS transitions and GPU compositing

Animating with JavaScript libraries means shipping library code, running layout work in the main thread, and often producing visibly choppy motion. CSS Transitions cover the common case — smoothly interpolating between two visual states — with a fraction of the bytes. Most style properties can transition, including text-shadow, position, background, and color, and transitions can fire on pseudo-class states like :hover or the HTML5 form states :invalid and :valid. More usefully, adding any class to an element can trigger a transition, so you can drive animation from normal application logic rather than animation code:

div.box {
  left: 40px;
  -webkit-transition: all 0.3s ease-out;
     -moz-transition: all 0.3s ease-out;
       -o-transition: all 0.3s ease-out;
          transition: all 0.3s ease-out;
}
div.box.totheleft { left: 0px; }
div.box.totheright { left: 80px; }

Compare that with the amount of code any JavaScript animation library requires. The CSS version also has a performance advantage beyond byte count: in browsers with hardware acceleration, these visual transitions are composited on the GPU, which keeps them smooth even when the page is otherwise busy. The catch is that GPU compositing engages only under restricted conditions; 3D transforms and animated opacity are the most reliable triggers. One unobtrusive trick is to request a 3D transform that has no visual effect:

.hwaccel {  -webkit-transform: translateZ(0); }

There are no guarantees, but when compositing does kick in, translation, rotation, scaling, and opacity run on the GPU without redrawing layer contents. Be aware that properties affecting page layout remain relatively slow even with acceleration enabled.

Keep data local with client-side databases

Web SQL Database and IndexedDB put a real datastore in the browser, letting you read, filter, sort, and search data without a server round trip. A data grid, an inbox with hundreds of messages, a friends list, or an autocomplete that filters on each keystroke are all candidates: store the dataset locally once, then serve user interactions entirely from the client. HTTP requests drop, and the UI stays responsive because you are no longer waiting on the network per action.

For simpler cases, localStorage and sessionStorage — say, capturing form progress — have proven noticeably faster than the client-side database APIs. Choose the simplest store that fits the task.

Favor native JavaScript methods

ECMAScript additions that arrived alongside the HTML5 push deliver speedups you get for free by not hand-rolling loops. The additional Array prototype methods introduced in JavaScript 1.6 are now widely available, and in most cases using them is significantly faster than a manual loop such as for (var i = 0, len = arr.length; i < len; i++):

// Give me a new array of all values multiplied by 10.
[5, 6, 7, 8, 900].map(function(value) { return value * 10; });
// [50, 60, 70, 80, 9000]

// Create links to specs and drop them into #links.
['html5', 'css3', 'webgl'].forEach(function(value) {
  var linksList = document.querySelector('#links');
  var newLink = value.link('http://google.com/search?btnI=1&q=' + value + ' spec');
  linksList.innerHTML +=  newLink;
});

// Return a new array of all mathematical constants under 2.
[3.14, 2.718, 1.618].filter(function(number) {
  return number < 2;
});
// [1.618]

// You can also use these extras on other collections like nodeLists.
[].forEach.call(document.querySelectorAll('section[data-bucket]'), function(elem, i) {
  localStorage['bucket' + i] = elem.getAttribute('data-bucket');
});

Likewise, native JSON.parse() replaces the need to ship json2.js — it is faster, safer, and present in IE8, Opera 10.50, Firefox 3.5, Safari 4.0.3, and Chrome. Native String.trim is faster than longhand equivalents and potentially more correct. These are not technically HTML5 features, but they round out the modern browser baseline worth coding to.

Use the application cache even when users are online

The cache manifest was designed for offline access but is just as valuable as an online performance tool. Think of how WordPress Turbo used Google Gears to cache admin resources locally: HTML5's applicationCache and its cache.manifest reproduce that pattern natively. Against plain Expires headers, the app cache has a unique advantage: your manifest declares which static resources are cacheable, so the browser can optimize aggressively — even pre-caching resources before you need them.

Treat your page markup as fixed templates, list those templates plus other static assets in the manifest, then update content by exchanging JSON over the wire. The model mirrors what native news apps on iPhone and Android do, and it drastically cuts request volume.

Delegate heavy processing to Web Workers

Web Workers give you two things: the work runs fast, and the browser stays responsive while it runs. Anything CPU-heavy that currently blocks UI interaction is a candidate. Practical uses include text formatting of long documents, syntax highlighting, image processing and synthesis, and processing large arrays.

Use HTML5 form widgets and validation instead of JavaScript

HTML5 adds a real set of input types beyond text, password, and file: search, tel, url, email, datetime, date, month, week, time, datetime-local, number, range, and color. Support varies — Opera implements the most today — so feature detection decides whether the browser provides a native UI like a date picker or color picker. Where Native support is missing, continue using JavaScript widgets as a fallback.

Ordinary input fields benefit too. The placeholder attribute provides default text that clears on focus; autofocus puts the caret in a field at page load. Declarative validation arrives via the required attribute, which blocks submission until a field is filled, plus the pattern attribute, which tests input against a custom regular expression. Relying on these reduces the JavaScript and CSS you must ship for custom widgets, speeding up page load, and the native widgets tend to be more responsive than their JavaScript counterparts.

Replace image effects with CSS3

Many visual treatments historically requiring image files can now be expressed as CSS alone. Swapping a 2 KB image for 100 bytes of CSS is a clear win, and you remove one more HTTP request in the process. The properties worth knowing include linear and radial gradients, border-radius for rounded corners, box-shadow for shadows and glow, RGBA for alpha opacity, transforms for rotation, and CSS masks. Buttons that look polished are achievable with gradients alone. Browser support for most of these effects is strong; for engines that lack them, a library like Modernizr detects support and lets you serve image fallbacks to those browsers only.

Prefer WebSockets over polling XHR

WebSockets was designed in response to Comet's growing popularity, and it delivers the same server-push model with considerably tighter framing. Bandwidth consumption is often lighter than XHR, with some measurements reporting a 35% reduction in payload bytes. At higher message volumes the difference is starker: testing has clocked XHR at an aggregate time over 3500% longer than WebSockets. Ericsson Labs measured ping times over HTTP at 3–5 times larger than over WebSockets, attributing the gap to more substantial processing overhead, and concluded that the WebSocket protocol is clearly better suited to real-time applications.

Measuring results

When applying these techniques, use the diagnostic tools built for the job: Page Speed and YSlow for Firefox, Speed Tracer for Chrome, and DynaTrace Ajax for IE when you need deep request-level logging. They will show where the remaining bottlenecks live.