Saturday, July 27, 2013

Improving website latency by converting images to WebP format

A couple of years ago Google published a new image format called WebP (*.webp). This format is supposed to be much smaller in size, without losing from the quality (or at least no noticeable quality). You can convert jpeg images to webp without noticing the difference, with a smaller image file size and even support transparency.
According to Ilya Grigorik (performance engineer at google) - you can save 25%-35% on jpeg and png formats, and 60%+ on png files with transparency! (http://www.igvita.com/2013/05/01/deploying-webp-via-accept-content-negotiation/)

Why should we care about this ?
Your web site latency is super important! If you don't measure it by now, then you really need to start. In commerce sites it's already been proven that better latency directly equals more revenue (Amazon makes 1% more in revenue by saving 100ms).

How is this new image format related to latency ?
If your site has many images, then your average user is probably spending a fair amount of time downloading those images. Think of a site like pinterest which is mostly contrived of user based images, then the user is downloading many new images with each page view.
While on a PC at your home, with a DSL connection this might not seem like a lot, but we all know that a big percentage of our users are using mobile devices, with 3G internet connection, which is much slower and they suffer from much longer download times.

What are our options ?
Just converting all our images to WebP is clearly not an option. Why ? Well, some people in the world have special needs. In this case i'm referring to people with outdated browsers (We all know who they are!).
BUT, we can still let some of our users enjoy the benefit of a faster site, and this includes many mobile users as well!

We will need to make some changes to our site in order for us to support this, so lets see what we can do -
(Technical details on implementation at the end)

Option #1 - Server side detection :
When our server gets the request, we can detect if the user's browser supports webp, and if so reply with an html source that has '*.webp' image files in it.
This option comes with a major downside - You will no longer be able to cache the images on the server (via OutputCaching or a CDN like Akamai) since different users can get a different source code for the same exact page.

Option #2 - Server side detection of image request :
This means we can always request the same file name, like 'myImage.png'. Add code to detect if this client can support webp then just send it the same file but in webp format.
This option has a similar downside - Now we can cache the html output, but when sending the image files to the user we must mark them as 'non-cacheable' too since the contents can vary depending on the user's browser.

Option #3 - Client side detection :
Many big sites defer the downloading of images on the client only until the document is ready. This is also a trick to improve latency - It means the client will download all the resources they need, the browser will render everything, and only then start downloading the images. Again, for image intensive sites this is crucial, since it allows the user to start interacting with the site before waiting for the downloading of many images that might not be relevant at the moment.
This is done by inserting a client side script that will detect if the browser supports webp format. If so, you can change the image requests to request the *.webp version of the image.
The downside to this option is that you can only use it if the browser supports the webp format.
(btw - you can decide to go extreme with this and always download the webp version, and if the client doesn't support it, there are js decoders that will allow you to convert the image on the client. This seems a little extreme to me, and you probably will be spending a lot of time decoding in js anyway).


The gritty details -

How can we detect if our browser supports webp ?
Don't worry, there's no need here for looking up which browsers support webp and testing against a list. Browsers that support webp format should claim they do when requesting images. We can see this done by Chrome (in the newer versions) :
You can see in the request headers 'Accept: image/webp'

How do we do this on the client ?
In javascript we don't have access to the request headers, so we need to get creative.
There is a trick that can be done by actually rendering an image on the client, using base64 to store the image in the code, and then detect if the browser loaded the image successfully.
This will do the trick :
$("")
    .attr('src', 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA=')
    .on("load", function() {
        // the images should have these dimensions
        if (this.width === 2 || this.height === 1) {
            alert('webp format supported');
        }
        else {
            alert('webp format not supported');
        }
    }).on("error", function() {
        alert('webp format not supported');
    });


How do we convert our images to webp format ?
We can do it manually using Google's converter - https://developers.google.com/speed/webp/docs/cwebp
Doing it programatically depends on what language you're using.
There's a wrapper for C# - http://webp.codeplex.com/
(and there are more for other languages, but not all - I'm actually looking for a java wrapper, and couldn't find one yet)


So, should I run ahead and do this ?
All this good does come with a price, as all good things do... :)
There might be side affects you didn't think of yet. Some of them being the fact that if a user sends a link to an image that ends with webp and the user that receives this is using a browser that doesn't support it, then they won't be able to open the image.
More what, even if the user does use a new browser (e.i.: a new version of Chrome) and they save a webp file to disk, they probably won't be able to open it on their computer.
These are problems that facebook ran into, and eventually retreated from the idea of using webp. You can read all about that here.

Which browsers did you say support this ?
According to www.caniuse.com - Chrome has obviously been supporting it for a while. Opera also supports it, and FireFox is supposed to start supporting this really soon as well. The most important news is that Android browsers, Chrome for Android and Opera mobile all support this which means many of your mobile users can gain from this change.


If you're still reading and want more information -
- Ilya Grigorik explains how to implement this using your CDN and NginX
- An excellent presentation on web image optimization by Guy Podjarny