Conquering the uncanny valley – Techniques for matching native app performance on the web with HTML5 – Third party scripts



Conquering the uncanny valley – Techniques for matching native app performance on the web with HTML5 – Third party scripts

1 2


talk-html5perf

Talk about HTML5 performance, first delivered at Web Directions south 2013

On Github triblondon / talk-html5perf

Conquering the uncanny valley

Techniques for matching native app performance on the web with HTML5

by Andrew Betts, FT Labs (trib.tv / @triblondon)

The uncanny valley

Robotics professor Masahiro Mori, 1970
Uncanny valley teaches us not to try and be something we're not. This is often a problem experienced by web applications. Web apps that simply try to emulate native without balancing capabilities of tech with actual user needs likely to fall into uncanny valley. BUT, we think web is still the best way, going to explain briefly why

We've been doing this for a while

The FT has actually been publishing content on mobile for 125 years. For most of that time we've used a technology we call a newspaper.

Newsprint

  • Works offline
  • Portable
  • Long battery life
  • Can be read in bright sunlight
  • Cheap
  • Ubiquitous
  • Bookmarkable
  • Sharable
  • Fast start up
  • Supports clipping/saving
  • Can be read in the dark
  • Updates in real time
  • Electronic delivery
  • Search
  • Personalisation
  • Deep linking
Might seem glib, but newsprint has a whole host of features that neither native apps, nor the web to date have been able to match
We need to care about supporting existing features as much as getting new ones And look ahead to new platforms, new ways that people will want to get our content

Lose the constraints

  • Tablet
  • Phone
  • Desktop
  • Laptop
  • TV
  • Games console
  • In car screen
  • In flight screen
  • Billboard
  • Kiosk
  • e-ink reader
  • Hot air balloon
Obviously we don't think this is a viable way to read the FT, but it makes you think, doesn't it, about how the web might evolve and what we might come to use it for
So with these ambitions in mind, and thinking about the UX innovations achieved by native app developers, and the risks of uncanny valley effects, the FT web app demonstrates an evolving compromise. Balances great UX that adapts to your needs, with what we can do on the web today.
For example, our economist app starts out assuming that you have a touchscreen, but if it detects a mouse cursor moving, we add hover effects, navigational aids and tune our click targets for use with a mouse.

Smooth transitions

16ms

No pauses

100ms

Matching expectations

Network

HTTP requests are painful, so do fewer of them and make them count.

The basics

  • Async everything: defer and async on script tags
  • Even async scripts can block the load event:Start loading your scripts after onload
  • Reduce HTTP requests with spriting and script concatenation

Power yum yum

Source: Qualcomm

Latency by radio state

  Chrome, MacOS,Velocity Conf wifi Safari, iOS,T-Mobile EDGE 1s intervals 180ms 850ms 10s intervals 180ms 1500ms 20s intervals 180ms 2100ms

Try it on the JSFiddle.

Batching

  • Aggressive batching - collect requests asynchronously:
    api.add('getStory', {'path': '/1'}, callback1);
    api.add('getStory', {'path': '/1'}, callback2);
    api.send(callback3);
    api.add('healthcheck', params, callback4);
    api.send(callback5);
  • Callbacks per action and per group
  • Process queues in the background

HTTP 2.0 / SPDY

  • Makes this unnecessary – in theory, but:
    • Can't optimise cross-origin (third party scripts)
  • Still use cases for delayed requests:
    • Offline persistence of queued requests

Images

  • Typically 70-95% of web page data is images
  • Use an accessible responsive images technique
  • Consider high resolution, high compression for high DPI screens

Third party scripts

  • One of the biggest users of polling timers, especially analytics
  • Test your performance before and after
  • Ask the right questions before you add a third party script

Live demo (ooo err)

Interested in helping me with this? Look at the code and drop us a line.

Prefetch and friends

Prefetch and friends

Pre-resolve DNS hostnames for assets later in the page:

<link rel='dns-prefetch' href='hostname-to-resolve.com'>

Fetch subresources early so they're already there when needed:

<link rel='subresource' href='/path/to/some/script.js'>

Pre-fetch resources for likely future navgiations:

<link rel='prefetch' href='/most/likely/next/page.html'>

Pre-render an entire page in the background (Chrome only)

<link rel='prerender' href='/overwhelmingly/likely/next/page.html'>

Rendering

Layout, paints, animation frames and 'jank' coming up.

Use the tools

  • Timeline
  • Chrome tracing
  • FPS meter

Old flexbox

IE <11, Firefox (current), Safari <7, iOS <7, Android (current), Blackberry <10

New flexbox

IE 11+, Chrome, iOS 7+, Blackberry 10+

Hover effects

Disable hover effects on non-hoverable devices and during scrolls

.hoverable a:hover { ... }
<body class='hoverable'>
 ...
</body>

Live demo (ooo err)

Frame rates

Activate meter in chrome://flags

No border-radius

Border-radius

Eli Fidler from BlackBerry at Edge 2: 1px text-shadows are really expensive. Don't do that stuff

Layout boundary

The area the browser has to re-layout when you change something

For more info see Wilson Page's post or Boundarizr by Paul Lewis.

Layout boundary (fixed)

Layout 'thrashing'

  • DOM operations are synchronous but 'lazy' by default
  • Browser will batch writes for you
  • But you force it to write if you try to read something

Interleaved reads/writes

var h1 = element1.clientHeight;              <== Read (measures the element)
element1.style.height = (h1 * 2) + 'px';     <== Write (invalidates current layout)
var h2 = element2.clientHeight;              <== Read (measure again, so must trigger layout)
element2.style.height = (h1 * 2) + 'px';     <== Write (invalidates current layout)
var h3 = element3.clientHeight;              <== Read (measure again, so must trigger layout)
element3.style.height = (h3 * 2) + 'px';     <== Write (invalidates current layout)
etc.

Batching reads/writes manually

var h1 = element1.clientHeight;              <== Read
var h2 = element2.clientHeight;              <== Read
var h3 = element3.clientHeight;              <== Read
element1.style.height = (h1 * 2) + 'px';     <== Write (invalidates current layout)
element2.style.height = (h1 * 2) + 'px';     <== Write (layout already invalidated)
element3.style.height = (h3 * 2) + 'px';     <== Write (layout already invalidated)
h3 = element3.clientHeight                   <== Read (triggers layout)
etc.

Asynchronous DOM?

Use Wilson's FastDOM library to get asynchronous DOM today.

fastdom.read(function() {
  var h1 = element1.clientHeight;
  fastdom.write(function() {
    element1.style.height = (h1 * 2) + 'px';
  });
});

fastdom.read(function() {
  var h2 = element2.clientHeight;
  fastdom.write(function() {
    element2.style.height = (h1 * 2) + 'px';
  });
});

This works by using requestAnimationFrame to batch writes

Live demo (ooo err)

Images.

Image decoding

Image decoding is probably the most expensive thing you ask the browser to do when your page loads.

  • Don't load on demand (battery killer on mobile)
  • Load but don't decode? No native API (yet. Ahem)
  • Polyfill by downloading data: URIs
    • Base64-encode on server
    • Download with XHR
    • Insert string into <img src=''> to trigger decoding
  • Con: Fools the browser, may mean you lose browser level optimisations

Hardware accelerated transforms

You need the GPU if you're going to animate a

move, scalefilter, rotate

Using accelerated animations

  • Repositioning elements causes a relayout
  • Accelerated animation = paint only
  • First: move element to GPU layer
    .thing { -webkit-transform: translateZ(0); }
  • Then: apply a transition or animation
    .thing { -webkit-transition: all 3s ease-out; }
    .thing.left { -webkit-transform: translate3D(0px,0,0); }
    .thing.right { -webkit-transform: translate3D(600px,0,0); }

This will-change

.thing { will-change: transform, opacity }

Storing data

HTML5 can store data on device too, it's just... well, it's complicated.

A happy family of technologies

  • Cookies
  • localStorage (and sessionStorage)
  • WebSQL
  • IndexedDB
  • HTML5 Application Cache
  • Files API
  • Cache API

localStorage vs IndexedDB

HTML5 Application cache

If you really want to know more, go read this and watch this

AppCache is dead, long live

ServiceWorker

It's like a server in the browser

Storage optimisation

While we are limited by tiny quotas, we need to learn to live with less.

Text encoding 'compression'

  • JavaScript internally uses UTF-16 for text encoding
  • Great idea for processing: fast string operations, full support for Unicode BMP
  • Terrible idea for storage of English text or base-64 encoded images.

Squeeze your bits

Text S i m p l e Decimal 83 105 109 112 108 101 As binary 01010011 01101001 01101101 01110000 01101100 01100101 Shifted 01010011 01101001 01101101 01110000 01101100 01100101 As hex 53 69 6D 70 6C 65 UTF-16 卩 浰 汥

ASCII packed into UTF-16

function compress(s) {
    var i, l, out='';
    if (s.length % 2 !== 0) s += ' ';
    for (i = 0, l = s.length; i < l; i+=2) {
        out += String.fromCharCode((s.charCodeAt(i)<<8) + s.charCodeAt(i+1));
    }
    return out;
}

Decompressing

function decompress_v1(data) {
    var i, l, n, m, out = '';
    for (i = 0, l = data.length; i < l; i++) {
        n = data.charCodeAt(i);
        m = Math.floor(n / 256);
        out += String.fromCharCode(m) + String.fromCharCode(n % 256);
    }
    return out;
}

Click delay

More click, less wait.

Fastclick

  • github.com/ftlabs/fastclick
  • Removes 300ms delay on touch
  • Programmatic clicks aren't quite the same - we handle it where we can (eg apply focus)
  • Fixed in Chrome 32 (Stable), Firefox 30 (Nightly). Still an issue in Safari & IE

Live demo (ooo err)

Unenhanced

With fastclick

Note: This demo shows the effect of Fastclick without using Fastclick itself

Perception

When you can't make it any faster...make it seem faster.

That's it!

Contact me:

For jobs in Beijing, visit www.ftchinese.com/jobs

For jobs in London, visit labs.ft.com/jobs