Tony Gets Technical: Retina Displays

Tony Gets Technical: Retina Displays

Posted on December 13, 2016 nerdymind

Supporting and Optimizing Website Images for High Pixel Density Screens

We've recently started supporting "Retina®", or high-DPI, screens with websites that we develop. For those not familiar with how a web browser behaves when used on a higher density screen, normal images are scaled up, stretching them, and making them appear blurry. The iPhone 4S started this whole retina trend, and it's moved on to the iPad, the Macbook Pro, and a quickly growing number of Android devices. It's clear that retina is here to stay, so how do we support it?

Let's start with a 200x200 pixel image and put it as the background on a div:

#image {
  background-image: url(/images/image.png);
  width: 200px;
  height: 200px;
}

Now, if we view this image on a retina device, it's going to get scaled up a little to fill the pixels on the higher DPI screen, and won't look sharp. To fix this, we need a higher resolution version of our image, so we'll create a 400x400 pixel copy of our original image, and use background-size to scale it down:

#image {
  background-image: url(/images/retina-image.png);
  background-size: 200px 200px;
  width: 200px;
  height: 200px;
}

What we're saying here is that we want the div to be 200x200, and we'll take the 400x400 image and squeeze it into that 200x200 space. Retina devices will see a sharp image, as will non-retina devices. But there's one downside to this technique...The non-retina device just downloaded a file that has up to four times the filesize of what it's capable of displaying.

Optimizing Download Size for Non-Retina Devices

We like websites that look great, but we also like it for them to load as quickly as possible. With the growing number of mobile providers moving customers to metered or capped bandwidth plans, and the large number of mobile viewers still viewing sites on 3G instead of 4GLTE, speed should still be a concern when it doesn't affect the look of the site. In addition, IE8 (which still accounts for over 5% of all web traffic as of this post, and is still more widely used than IE9 or IE10) does not support the background-size tag, so using a retina-only approach will render your webpage unusable in IE8 if that is a concern for your audience. This is where media queries come in.

Let's add a media query that sends the retina image to retina devices, and the non-retina image to non-retina devices:

#image {
  background-image: url(/images/image.png);
  background-size: 200px 200px;
  width: 200px;
  height: 200px;
}
@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {
  #image {
    background-image: url(/images/retina-image.png);
  }
}

Here we have our 200x200 image.png being sent to non-retina devices, and then use a media query to detect high-DPI displays and send the 400x400 retina-image.png instead. In addition, IE8 completely ignores the background-size tag and media query, and displays the non-retina image properly.

Retina® Optimized Sprites

Around here, we love using sprites for our images, so let's take this just one step further:

.sprite {
  background-image: url(/images/sprite.png);
  background-size: 200px 200px;
}
.sprite.logo {
  background-position: -0px -0px;
  width: 100px;
  height: 100px;
}
.sprite.icon {
  background-position: -100px -0px;
  width: 50px;
  height: 50px;
}
@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {
  .sprite {
    background-image: url(/images/retina-sprite.png);
  }
}

This lets us define the sprite's background image and size once for each screen type, and then we only need to set background-position for each piece of the sprite that we want to use. Everyone gets the image that works best for their screen type with very little extra work for the designer or developer.

Of course, sometimes you need to use the <img> tag, so supporting that is pretty simple. Unfortunately, there doesn't seem to be a good way to send the non-retina image to non-retina devices without server-side detection. With our 400x400 retina image:

<img src="/images/retina.png" style="width: 200px; height: 200px" />

Some tutorials may recommend increasing the resolution by 1.5x, but we recommend 2x for a few reasons. First, when you create your non-retina images, a 50% resolution reduction will create a cleaner image than a 66% reduction. Second, most of the sprite tools we've seen that deal with retina images have an automatic setting for 2x images to help you speed up your process. Third, in the event that you have to do the math yourself, it sure is a lot easier to divide and multiply by 2 than by 1.5.

One last note... There seems to be a common conception that browsers will download files for all media queries, including non-matching queries, which would cause our retina/non-retina media query to send both files to the browser. This may have been the case at one point, but we are not seeing this behavior with any browsers that we currently have access to on any devices. In all of our tests, the web browser will only download the image that matches the correct media query. If someone can point us to a browser that behaves differently, we'd love to see it so we can figure out how to deal with it properly.