How to use client hints to deliver responsive images – and why we should all soon be doing it

We recently posted a short guide to responsive images, which included a few examples of how to deal with some of the more common requirements.

A couple of the examples showed how using srcset allows you to deliver high-resolution images to high-resolution displays.

This is something the browser takes care of without you having to do too much work.

Here’s an example:

<img src="hero-large.jpg" alt="" sizes="100vw" srcset="hero-small.jpg 360w, hero-medium.jpg 720w, hero-large.jpg 1024w, hero-v-large.jpg 1400w">

In this case, the sizes attribute tells the browser that the intention is to display the images at 100 per cent of viewport width. It gets to pick from several different images (whose width, in pixels, is given by the w descriptor), taking viewport width and device pixel ratio (DPR) into account. Device pixel ratio is the ratio of physical pixels to logical or CSS pixels. For example, a phone could have a display that’s nominally 360-pixels wide, but actually have double the number of physical pixels (720). This would give it a DPR of 2.

In the above example, a device with a viewport 360 pixels wide would get hero-small.jpg. But a device with a viewport 360 pixels wide and a DPR of 2 would get hero-medium.jpg.

This is great. But it assumes that the highest possible resolution image is best for the end user. And that’s not always the case. The higher the resolution, the bigger the file. And people on mobile devices with high-resolution displays are often on slow connections. They may also be on data-limited packages.

The result is that you could be sending ultra-high-quality images to people who, given the option, might well prefer slightly lower quality. They might be better off with images that load and display faster, and eat up less of their data allowance.

The filmstrip below illustrates the impact on mobile. It shows two similar pages being loaded on a Nexus 5 on a relatively slow (1.6Mbps) connection (frames are at 0.2 second intervals). The image in the top filmstrip is a high-resolution version that takes advantage of the device pixel ratio of the device. The page in the bottom filmstrip doesn’t take device pixel ratio into account and therefore uses a smaller image. The result is that the high-resolution image takes around half a second longer to load and display that its standard resolution counterpart.

Film Strip of Loading Times

It’s worth saying that browsers are supposed to be able to take network conditions into account when you use srcset. This could in theory mean that standard resolution images get delivered when the network is slow. Indeed, part of the beauty of srcset is that it ultimately leaves the choice of image to the browser, instead of the developer, who can’t know the precise conditions in which the image will be viewed. However, at the moment, network conditions aren’t automatically taken into account. 


The alternatives

One option is to be prescriptive – to use <picture> and <source> to force the browser to use a certain image at a given viewport width regardless of device pixel ratio.

This works, but it’s a shame to have to resort to this approach, as it takes away the flexibility of srcset. For example, if you use srcset, the browser may actually pick a higher resolution image than it needs if it already has it in cache (for example, if the orientation switches from landscape to portrait). This means the end user doesn’t waste time and network resources waiting for a smaller, but otherwise identical, image to load.

On the other hand, if you use <picture> and <source>, you’re basically telling the browser that each <source> element contains different images, not different versions of the same image. It must therefore load the image that matches the relevant media query.

Moving image selection to the server

The responsive images spec is a huge step forward, but the complexity and the occasional shortcomings highlight the difficulty of trying to cater for every eventuality.

So perhaps the better option is to deal with all this server side.

This is just starting to become much easier thanks to client hints, many of which are now supported in Chrome.

Client hints are a way for the client to tell the server something about the conditions in which a page is being viewed.

One such hint is save-data. When users have data saving enabled in their browser (either in their browser settings on Android or via the Chrome Extension on desktop), all requests include the header ‘save-data: on’.

This is a good indication that the end user might not want imagery that takes full advantage of the high pixel density on their device.

However, it doesn’t tell you much else about the conditions under which they’re viewing your site.

For this you need another set of client hints, which have to be enabled, either by returning the appropriate header in the HTML response or by using a meta element in the HTML:

  • Accept-CH: DPR, Width
  • <meta http-equiv="Accept-CH: DPR, Width">

The result is that requests for all subsequent objects on the page will contain headers for DPR and (for <img> elements with a sizes attribute) width.

This gives us enough to generate or retrieve the right-sized image server side without worrying about srcset or <picture>:

<img src="hero.jpg" alt="" sizes="100vw">

Here’s how.

If we were on a Nexus 5X, for example, the request for this image would include the following headers:

  • DPR: 2.625
  • Width: 1082

The Nexus 5X’s display is 412 CSS pixels wide, and it has a DPR of 2.625. Since the sizes attribute on the <img> element is set to 100vw (100 per cent of viewport), the Width hint is set to 1082 (412 x 2.625). In other words, the Width header is giving the hint: ‘this image should match the width of the screen in physical pixels’.

What’s more, if we want to deliver a lower resolution image to people who want to save data, we now have everything we need.

We have the save-data header.

We have the DPR.

We have the correct width for the image, taking DPR into account.

Now we just have to check whether the save-data header is set to on. If it is, instead of delivering an image that’s 1082 pixels wide (the value of Width), we deliver one that’s 412 pixels wide (Width / DPR).

The downside, of course, is that this is all quite a lot of work to set up. So as support for client hints grows, we’re likely to see are more and more third-party services offering to do the heavy lifting. Some, such as Cloudinary, are already using client hints for image selection.

Just as one way to deliver responsive images starts to gain traction, what looks like a better alternative may be poised to take over. Whether we’ll ever get to the point when images are no longer one of the top barriers to a fast website remains to be seen.

Published date:  07 March 2018

Written by:  Alex Painter

comments powered by Disqus

Filter By Service

Filter By Date