Picture of Brian Love wearing black against a dark wall in Portland, OR.

Brian Love

Retina and Responsive HTML5 Images

With the popularity of retina and high definition displays on the rise it is now more important than ever for web developers to use high definition images. There are a couple of ways to tackle this. I am going to cover the following methods:

Load 2x Everywhere

This is probably the easiest solution, but not good for your mobile and non-retina users as you will be loading high definition images for all users. Non-retina users on a broadband connection will likely not notice it, however, your mobile users or those with a slower connection are likely to wait longer for your page to load.

For example, the non-retina (or high definition, I will probably interchange these terms) image is 100×200 pixels. So, specify the height and width of the image using CSS or the attributes in your HTML to be 100×200. But, load the image that is twice the size, or 200×400 pixels.

Your HTML for this might look like:

<img src="/images/foo@2x.png" style="height:100px; width: 200px;" alt="bar" />

Use JavaScript

Another option is to use JavaScript to load the retina graphics after the page has loaded. There are several libraries that are freely available for you to use. I chose to use the retinajs library. I also submitted a pull request to correct a bug where the library was attempting to load the retina graphics when I was already loading the retina graphics via the srcset attribute (we’ll talk more about that next).

Briefly, the most common approach is to loop over all of the images in the DOM and to attempt to load the high definition image. Retinajs looks for images on your server that match the file name plus @2x appended. This naming convention is becoming standard after being created by Apple for iOS. If the image file exists on your server (returns a 200 status code), then the retina image is substituted for the non-retina one. Of course, it also has to enforce the original image dimensions (just like what we did previously with loading retina images everywhere) so that the images do not appear twice the size on the screen.

One thing I didn’t mention first is that the JavaScript should first check if the user is on a retina display device before doing anything. We would not want to try to load the retina images unless necessary.

This solution works well, but there are several downsides:

Last, some JavaScript implementations take this further by determining the user’s bandwidth and only loading the retina images if they have a fast enough connection. This is a great addition to solving this problem, and is probably appreciated by mobile users with both a retina screen and a slow Internet connection.

HTML5 srcset Attribute

A new srcset attribute for the <img> element has been proposed by the W3C as part of the HTML5 specification and it has already been implemented in some of the browsers. The first two methods that I wrote about above are really just hacks until we have broad support for high definition images in the browsers. They are both adequate solutions but we can clearly see the need for a standard approach that is implemented by the major browsers. Enter the next two methods: using the srcset attribute and using the new <picture> tag.

The srcset attribute enables you to specify a list of image sources to use based on the width of the browser and the pixel density of the device. This is really cool, and I’m very happy to say that Google Chrome has already implemented this. You can see what browsers have implemented this at caniuse.com/srcset. Note that WebKit (the rendering engine for Safari) has already implemented this and it will likely be released in Safari 8 that will ship with OS X.

The HTML syntax for loading the retina display might look like:

<img src="/images/foo.png" alt="bar" srcset="/images/foo.png 2x" />

Nice and clean.

You can also specify a list for the srcset attribute. Using a list for srcset enables the developer to tell the browser that there are other image sizes available that can be used in place of the default src image. Using the wflag we can tell the browser that we have the same image available in different widths. The width is similar to using the max-width CSS property in media queries. For example, we can enable the browser to use a medium and large image based on the sizes of the image in the viewport:

<img
  src="/images/foo.png"
  alt="bar"
  srcset="
    /images/foo-medium.png 1024w,
    /images/foo-large.png  2048w,
    /images/foo.png         800w
  "
/>

Combining this with the sizes attribute, the browser can choose the image that is best suited for the user based on the desired size of the image in the viewport and the available image sizes provided in the srcset attribute. With the sizes attribute we can also use media queries to set the size relative to the viewport based on the width of the viewport. Here is an example of showing the image at 50% of the viewport width on desktops and 100% on mobile devices.

<img
  src="/images/foo.png"
  alt="bar"
  srcset="/images/foo-medium.png 1024w, /images/foo.png 800w"
  sizes="(min-width: 1024px) 1024px, 800px"
/>

The srcset attribute is a great way to include high definition graphics for your retina display users. Further, the browser will not load the standard low definition image for retina display users, so there is no hit on the page load weight. And combining the srcset and sizes attributes you can further specify the width of your image in the browser’s viewport so that the browser can choose the best fit image to display to your user.

HTML5 picture Element

The last method that I will talk about is the new <picture> element. The picture element will use a similar approach as the HTML5 <audio> and <video> elements allowing you to specify multiple sources with the option to fallback to the default non-retina image. The picture element uses the srcset and sizes attributes that we discussed above, and encapsulates the various image sources that the browser can choose from based on the width of the viewport and the devices pixel density. Unfortunately, at the time of this writing it is not yet implemented in any of the major browsers: caniuse.com/picture.

In the following example we are going to specify two different sources for the picture with a fallback to the original image. The first <source> uses a media query to limit the source to be used only on devices that have a screen resolution greater than 1024 pixels. This <source> also specifies various image sizes to be used based on the width of the viewport. The second <source> specifies the images to be used based on the pixel density of the device. Finally, the <img> tag uses the default image to display in the browser. The fallback <img> tag is always required when using the picture element. The first two sources will not display an image in the browser if the img tag is missing. The sources simply provide information to the browser so that it can choose the best image to display based on the viewport size and the pixel density of the device.

<picture>
  <source media='"(min-width:' 1024px)" srcset='"foo-large.jpg' 1024w,
  foo-medium.jpg 640w, foo-small.jpg 320w" sizes='"50vw"' /> <source
  srcset='"foo@2x.jpg' 2x, foo.jpg 1x" />
  <img src='"foo.jpg"' alt='"Bar"' />
</picture>

I hope this post has helped you to learn more about retina and responsive images in HTML5. The future of high definition devices and images is now. There is no reason to not begin adding retina and responsive images to your website today.