Posted on March 31, 2012 by Steve Mu

I can’t wait until the day when every device we have is using a retina display. Meanwhile, the transition between normal resolution display to retina display is a painful process. Images made for normal websites look horrible on retina display, because mobile Safari have to double the pixels and stretch the normal low resolution graphics to fit on the screen. The result on photographic images aren’t all that bad, but the less complex images, such as logos and icons are clearly grainy & pixelated on the edges.

Making a retina display compatible image requires two steps, the first of which is really simple… take your normal size image and making one that’s twice as big. For example, if your logo.jpg is 200px by 200px, just make a version of it that’s called logo2x.jpg and make it 400px by 400px. You can simply upsample it in your application of choice, of course the result won’t be ideal if you simply upsample your images, but it’s still better than browser’s own upsampling. The better approach is to recreate your logo image from the ground up to fit in 400 by 400. Of course, that will be more time consuming… but it’s just a necessary pain to deal with.

Once you have your logo2x.jpg, you can simply use media query to make sure retina display picks up the higher resolution image:

.logo {
  display: block;
  background: transparent url(logo.jpg) no-repeat;
  width: 200px;
  height: 200px;
}

@media only screen and (-webkit-min-device-pixel-ratio: 2),
only screen and (max--moz-device-pixel-ratio: 2) {
  .logo {
    background: url(logo2x.jpg) no-repeat;
    background-size: 200px 200px;
  }
}

The first .logo class is for the normal display, while the media query will load the double resolution image for retina displays. The important thing to remember here, is that mobile Safari will automatically calculate dimension values so developers don’t have to recode all of their stylesheets. So when you say a div has a width of 200px, mobile Safari will actually render the div at 400px on its screen. So similarly you would have to pretend the higher resolution logo image is actually going to be displayed at its “normal” dimension values. Thus the line: “background-size: 200px 200px” to squeeze the logo graphic down to size.

So this all works very well if you make 2 separate graphic images for retina displays and normal displays. What if I wanted to create a single sprite image that contained both? Turns out this could be done as well. Let’s say if you created a sprite that’s a combination of both logo.jpg and logo2x.jpg. You would have an image sprite of 400px wide by 600px height, where the high resolution logo would be at the -200px height background position:

.logo {
  display: block;
  background: transparent url(logo.jpg) no-repeat 0 0;
  width: 200px;
  height: 200px;
}

@media only screen and (-webkit-min-device-pixel-ratio: 2),
only screen and (max--moz-device-pixel-rati0: 2) {
  .logo {
    background-position: 0 -100px;
    background-size: 200px 300px;
  }
}

Notice that the background-size attribute is half the size of the “entire” sprite image, not just the part that’s visible. Since the background size is again squeezed down, you also have to halve the pixel displacement of the sprite image.

Besides the little bit of oddity with sprite sizing and positioning, making graphics for retina display is pretty straight forward. Hopefully some people out there will find this helpful.