Responsive Images Revisited—High Resolution Images

What makes for a high resolution image? Is it the pixels per inch (ppi)? Is it the dots per inch (dpi)? What distinguishes an image as @1x, @2x, or @3x? If we’re going to create pixel dense images for high resolution devices, it probably makes sense to understand a little about what is a high resolution image.


For the last couple of weeks I’ve been talking about responsive images, specifically how the spec is stabilizing around the srcset attribute and the picture element.

As part of the series, I’m building a simple demo and as I set out to create the high resolution images, I realized I didn’t have as good a handle on what exactly makes for a high resolution image as I thought. I figured if I didn’t have a great handle, there are probably others feeling the same.

It seemed like a good opportunity for more research to answer my questions and then present what I found here. I want to share what I learned about what makes for a high resolution image along with a CSS4 property that can be used to change background images based on their resolution. I’ll close with some general thoughts about responsive images.

What is a High Resolution Image?

My instinct is to think high resolution means more pixels per inch (ppi). In a sense it does, but it has nothing to do with the settings you might choose in Photoshop or your image editor of choice.

You want to display a single px of the image in a single px on the screen

For years I’d heard that 72ppi was the limit on the web, because that was the resolution of computer screens. 96ppi gets thrown out as a limit too, depending on whether you’re viewing something on a Mac or a Windows based PC.

It’s all myth. The numbers (72 and 96) are from ancient days (the early 80s). Today’s screens have higher resolution obviously. If they didn’t this whole conversation about serving high resolution images would be meaningless. When you’re setting resolution in Photoshop it’s really for print and not for screens.

The important factors are the pixel dimensions of the image and the container it displays in and, of course, the resolution of the screen. Say you have an image that’s 800px by 600px and you want to display that image at the same size on the screen. If the screen is @1x resolution then every pixel in the image will fit exactly into one pixel on the screen.

If, however, the screen is @2x then each pixel in the image will need to represent four pixels on the screen. Twice the width and twice the height means you’ve gone from 1px × 1px to 2px × 2px or 4 pixels total.

To get back to a 1:1 ratio of pixels between image and screen, the image needs four times as many pixels. The original image would need to be 1600px by 1200px, which when displayed as 800px by 600px on the screen will be matching the @2x resolution of the screen.

In the end, the goal is to display a single pixel of the image in a single pixel of the screen. If you want an image to fit inside an 800px by 600px area on a screen with @2x resolution. you need to create an image twice the width and height or four times the area and then display it in that same 800px by 600px space.

Now that @3x screens are coming, we’ll need to create images with 9 times the total pixels (3px × 3px) as well. You want to start with the most pixels you can get and create images with smaller dimensions from the original image.

One additional consideration is image compression. Daan Jobsis discovered that compressing an image with 0 quality results in a smaller file size than it’s half sized version saved at 90 quality. All without any significant loss in quality.

Awhile back I ran some testsk with mixed results, though I suspect the fault was more to do with me than the idea behind compressive images.

How to Work with Background images

One of the things you may have noticed in the previous posts in the series is that the images were all included in the HTML. What about background images?

Switching background images is nothing new. You set a background-image with a url() and you change the url for various conditions. For example

.background { background-image: url("path-to-image-one"); }

@media only screen and (min-width: 30em) {
  .background { background-image: url("path-to-image-two"); }

Most of the time your media queries, probably do little more than test for a min or max width. I know most of the media queries I write are for a simple test of width. But there are other conditions you can test against, including pixel density.

The standard way to test for pixel density is to use min/max-resolution like

@media only screen and (min-resolution: 2dppx) { }

Allowed units are:

  • dpi (dots per inch)
  • dpcm (dots per centimeter)
  • dppx (dot per pixel unit)

Unfortunately not every browser supports the standard just yet. IE9+ and Opera Mini support only the dpi value. Safari and iOS Safari support the non standard min-device-pixel-ratio: 2.0 with the -webkit vendor prefix.

@media only screen and (-webkit-min-device-pixel-ratio: 2.0) { }

Switching images this way is easy enough though one issue to consider is whether or not browsers download all images inside and outside of media queries or if they down only the one needed?

Tim Kadlec ran some tests a couple years back to find out and you should check his post for the details. Tim ran a variety of tests, many with surprisingly good results. I want to call attention to one, the test using min-device-pixel-ratio.

.background { background-image: url("path-to-image-1x.jpg"); }

@media only screen and (min-device-pixel-ratio: 2.0) {
  .background { background-image: url("path-to-image-2x.jpg"); }

With one exception, browsers loaded only the appropriate image given the above code, which is what we want. However, in the time since these tests, a standard emerged and we should use the standard values for supporting browsers and offer fallback for others.

About two years ago Chris Coyier offered what at the time was a future proof media query to target resolution.

  only screen and (-webkit-min-device-pixel-ratio: 2),
  only screen and (   min--moz-device-pixel-ratio: 2),
  only screen and (     -o-min-device-pixel-ratio: 2/1),
  only screen and (        min-device-pixel-ratio: 2),
  only screen and (        min-resolution: 192dpi),
  only screen and (        min-resolution: 2dppx) { 

Relative to Chris’ post it is the future and given the increased support for min/max-resolution we can probably reduce the above to the following:

  only screen and (-webkit-min-device-pixel-ratio: 2), // Safari and iOS Safari
  only screen and (min-resolution: 192dpi), // IE9+
  only screen and (min-resolution: 2dppx) // All other browsers
  Image switching code here

Looks like all we need right? Actually no. The code above works for the art direction use case, but it doesn’t allow browsers to make an optimal choice in which image to display. Fortunately CSS gives us a way to let them.

Using image-set() on background-image

During the research for this series I came across a great and short post at Google’s Developers site on using images in CSS. The post introduced me to image-set(). CSS4 adds image-set() as a value you can use on the background-image property.

background-image: image-set()(
  url(image-1x.jpg) 1x,
  url(image-2x.jpg) 2x

Having worked with the srcset attribute over the first two posts in this series, the above shouldn’t be too hard to understand. The image-set() value lets you set multiple urls and associated resolutions as background-images and lets browsers decide when each should be used. Browsers will automatically scale the images so your @2x image will display at half it’s dimensions.

Browser support for image-set() isn’t quite as good as it is for srcset. You need vendor prefixes to make this work, however, if you do use them, you can make this work in the same browsers as srcset. In fact given the browsers that have support, it’s only the -webkit prefix you’d need at the moment.

The code above would be better written as:

background-image: -webkit-image-set(
  url(image-1x.jpg) 1x,
  url(image-2x.jpg) 2x
background-image: image-set(
  url(image-1x.jpg) 1x,
  url(image-2x.jpg) 2x

Unfortunately image-set() isn’t built into the Picturefill polyfill so we have no way to support Firefox, Internet Explorer, or Opera Mini at the moment. Also be aware things can change between now and when the spec is recommended.

The good news is you can still set a default background-image for those browsers without support and progressively enhance for those with support. In other words there’s no reason you can’t use image-set() now in practice as long as you don’t mind the vendor prefixes.

background-image { url("fallback-image.jpg"); }
background-image: -webkit-image-set()(
  url(image-1x.jpg) 1x,
  url(image-2x.jpg) 2x
background-image: image-set()(
  url(image-1x.jpg) 1x,
  url(image-2x.jpg) 2x

Non-supporting browsers will display fallback-image.jpg, which is the same thing they’d do if the image-set() code wasn’t there.

Closing Thoughts

I hope you’ve enjoyed the series to the this point. My experience working with srcset, picture and their associated attributes and elements is admittedly limited. However, even with that limited experience, working with the srcset attribute and the picture element is fairly easy.

It takes a little more thought to figure out the best sizes for the images, but the code is straightforward and it shouldn’t take long to figure out how to work with it. It’s worth the effort.

Even though browser support isn’t perfect, I think you can use srcset and picture now as well image-set() and everything else I’ve mentioned in this series. You can use the Picturefill polyfill for non-supporting browsers or you can just make good decisions about fallback images.

The hardest part will be creating the images. A little practice and choosing sizes will become just another thing we do. Of more concern is actually getting and creating the images. It’s easy enough to create several versions of an image for the few boilerplate images in a design. It’s all doable, but how often do your clients send you images now that are good enough for @1x, let along @2x, or @3x?

How about sites that add multiple images every day or sites that feature image galleries. An automated solution will be necessary for these sites. Fortunately automated solutions exist. Photoshop actions for example could make resized versions of your highest resolution in the background.

WordPress can automatically resize images you upload, though you need to stick to a handful of defined sizes to make it practical. A variety of responsive image plugins already exist as well.

I’m not overly concerned. I’m sure as more sites create multiple versions of images, we’ll discover new tools and others will be created to help.

Art direction probably won’t become any more popular, but those sites wanting to art direct images can now do so more easily.

Bitmap images will still remain a performance bottleneck and I think the common wisdom in regards to images will hold. Where possible you should:

  • Replace images with code
  • Replace bitmap images with SVG, icon fonts, and other vector based images
  • Combine images into sprites to reduce http requests
  • Overall try to reduce the number of bitmap images you use

Next week I’ll finish this series by presenting a more practical, though still simple demo that puts much of what we’ve covered in this series into practice.

« »

Download a free sample from my book, Design Fundamentals.


  1. Working with background images is something I want to learn to master well. I also learn a little about resolution. I studied or read your post twice in other to fully understand everything. Thanks for sharing.

  2. For me this post made me take out my writing materials as if I was in class all over again. Sometimes we need to really remind ourselves of some basic things like image resolution for fast load up times especially in our days of ‘responsiveness’ where speed counts a great deal

Leave a Reply

Your email address will not be published.