About

This article covers how to serve high resolution images on your website for users of high resolution ‘retina’ device screens.

If you find this post interesting or useful, you may want to check out my book ‘Responsive web design with HTML5 and CSS3‘ as it covers associated topics and techniques.[End of shameless plug.]

Introduction

Yesterday, Luke Wroblewski shared the following summary of info from Apple’s new iPad launch:

A total of 315 million iOS devices have been sold with 62 million sold the last quarter alone.
76% of Appleโ€™s revenue in Q42011 came from the iOS product lines: iPod, iPhone, and iPad
Apple now has 362 retail stores worldwide.
Over 25 billion app downloads since Apple introduced the App Store.
There are now over 100 million customers on iCloud.
Apple sold 15.5 million iPads last quarter alone. That’s more iPads sold in the last quarter alone than any PC manufacturer sold in their entire line. The iPad is just 2 years old.
200k apps have been custom built for the iPad.

It doesn’t take a genius to appreciate that if you aren’t designing and building websites today with the gamut of available devices (mobile, tablet, laptop, portable game station, desktop etc.) in mind you’re very quickly going to fall behind.

One feature of the newer iOS devices I’m particularly excited about is the ‘Retina’ displays. Whilst the ‘new iPad’ (was 3rd generation, now 4th generation) has a 2048-by–1536-pixel resolution at 264 pixels per inch (ppi) screen, the iPhone 4, iPhone 4S and iPhone 5 have an ever higher resolution screen: that is at least 960-by–640-pixel resolution at 326 ppi. If you have one of these devices you’ll know that images and text look beautiful on them. These digital devices (handheld, desktop, whatever) are now enjoying a level of ppi more akin to print.

When it comes to text, fonts, with their mathematical scalability fare excellently on these higher ppi devices. A good font looks beautiful on a retina display. However, web images, typically exported at 72 dpi (dots per inch, not pixels per inch – I’m not getting into that here), don’t fare quite so well. Here’s how we can fix that.

Media Queries let us target high resolution devices

Understanding how media queries work is essential for serving alternate content on web sites (through CSS) for high resolution screens. Whilst the venerable Dave Hyatt was talking about serving different content to high resolution screens back in 2006, I’ve read lots of material covering the specifics of the issue since. I’d therefore recommend checking out smarter people than me too: Josh Clark, Walt Dickenson to name a couple.

In a nutshell, the super smart W3C people who help bring about and ratify new web standards had the foresight to consider high resolution screens. Therefore, when defining Media Queries, we have the capability to target media based upon device pixel ratio. For example:

@media all and (-webkit-min-device-pixel-ratio : 1.5) {
    .logo a {
        background-image: url('../img/[email protected]');
    }
}

We’ll break that down but first some things to note:

  • It’s important to provide a full vendor-prefix stack to future proof these kind of rules.
  • I’ve named my ‘high-res’ file using the Apple naming convention (adding @2x to the end) – it’s the way it’s done on iOS app development and I’ve adopted the convention here. When designing web sites, it’s just a handy way for me to see which files are high res and which aren’t.
  • As with all media queries, it’s advisable to load/write them after the main styles so that the cascade gives them preferential treatment. If you are using Sass, just use a partial for the media queries and import it at the end of the main style sheet or use Sass 3.2’s ability to inline the media query.

So what’s the media query actually doing? First, we’re telling the user agent (the web browser for all intents and purposes) that we want the rule to apply to ALL media (that’s the ‘@media all’ part).
Next, we are stipulating that the rule should apply to all devices with a minimum pixel ratio of 1:1.5 or more – that’s the (-webkit-min-device-pixel-ratio : 1.5) part. Inside we then just stipulate as many CSS rules as we like that should apply to appropriate devices in the normal way.

You can alter this kind of query so that only devices with a pixel ratio of 1:2 are targeted. For example:

@media all and (min-device-pixel-ratio : 2) {
*styles here*
}

In that example, I have omitted the vendor prefix (I mention that just in case you cut and pasted and nothing’s happening). Want a full vendor prefix stack to cover all browsers? Take a look at this associated post showing how to produce a full vendor stack to target different pixel densities. If you want cut and paste goodness, this is as future proof as I know how to write a media query targeted at devices with high pixel density (alter values to suit your need) at the time of writing:

/* This one targets high resolution screens
   ========================================================================== */
@media (min-resolution: 192dpi), (-webkit-min-device-pixel-ratio: 2), (min--moz-device-pixel-ratio: 2), (-o-min-device-pixel-ratio: 2/1), (min-device-pixel-ratio: 2), (min-resolution: 2dppx) {
  /* your styles here */ 
}

Resizing the background image

In my main rule for that image (the non-media query rule that serves all non-retina displays) I already have a declaration for resizing the background image with CSS3: background-size: contain;. You’ll need something like that or explicitly state the background size. For example, if the non-retina image is 200px by 200px but the high-resolution image is 400px by 400px, you could do this:

.logo a {
    background-image: url('../img/logo2.png');
}

So, the background image for non-retina displays will display at 200px x 200px (assuming the containing element is big enough, otherwise is will be cropped). Then the rule specific for high resolution displays:

@media all and (-webkit-min-device-pixel-ratio : 1.5) {
    .logo a {
        background-image: url('../img/[email protected]');
        background-size: 200px 200px;
    }
}

Here, although the high-res ‘retina’ image is actually 400px x 400px, our background-size: property allows us to define the size it should display at.

Considerations for using high resolution images

Some obvious considerations: bigger images typically take longer to download (more on this shortly however) – it’s a dangerous assumption to make but mobile devices may be accessing your content over a cellular service – bigger images will slow the site down. When an image is double the physical size, the KB size is considerably greater* (*again, more on this shortly). For example, consider that this picture of HRH Queen Elizabeth II is 184KB at standard size (direct link to image at 800 x 584px) and the image double the physical size (direct link to image at 1600 x 1168px) is 694KB. That’s over 3 times as much data. If you do this for all the images on your page, regardless of whether or not users actually have an HD/retina display – the page will be slow for most people, with no discernible increase in quality for them.

Important: Be sure to read the section on the ‘Jobsis’ observation below!

Sadly, there are no CSS rules like this we can implement.

@connection all and (min-speed-1mb) and (max-speed-8mb) {
*styles*
} 

At present, it’s arguably necessary to think long and hard about what high res content you want to use in this way.

Want to show high resolution icons for retina displays? Use a font!

We already know that fonts scale brilliantly. If you need to use an icon in your design, don’t use an image, use a font instead. There are a growing number of ‘Icon Fonts’ available that you can implement into a web page using the @font-face rule. Icomoon.io is my favourite.

Icon fonts on a retina/high-res display are beautifully crisp. It can also scale to whatever size you need. Just amend the CSS to suit. For example:

.icon-font {
    color: #ff9900;
    font-size: 3em;
    letter-spacing: 6px;
}

There are other benefits, namely speed – only 1 http request for a raft of icons. If you’re concerned about semantics and don’t want loads of random letters scattered throughout the markup, another option is using the :before or :after pseudo classes. For example, use markup like this:

<span class="navIcon" data-icon="'"></span>

And then, in the CSS do this:

span.navIcon:before {
    content: attr(data-icon); 
    font-family: icons;
        speak: none;
    font-weight: 400;
    font-size: 3em;
}

As span is essentially meaningless from a semantic point of view, it presents the best of both worlds if the icon in question is purely there for aesthetic reasons.

Can’t get your icon font working on Windows Phone 8? Be aware that although Windows 8 phone supports @font-face it can be a little particular. For best results, I’d recommend creating a data-uri of the font. So instead of linking to the file as a resource, do something like this:

src: url('data:application/x-font-woff;charset=utf-8;base64,**DATA-URI-HERE***'),

Replacing **DATA-URI-HERE** with the relevant DATA-URI – more info on this here.

Providing alternate high resolution inline images for retina devices

The above media query based solution works fine and dandy for background images but what about standard img tags added in markup? Sadly, at present there is no ‘normal’ way to do this purely through markup. There are JavaScript solutions (such as this one at Flowz) that are relatively simple to implement. There’s also a jQuery Retina Display plugin to replace inline images for high res counterparts (should they exist). Modernizr may also be able to help with this kind of solution in the future. There are some merge requests/info on the Moderniz Github that look like they’ll help in future iterations.

For now, there are three solutions that offer the best workaround (in my humble opinion):

Adaptive images

If you are happy to serve high resolution images to all high resolution screens (regardless of whether the user actually wants to see and wait for the high-res images to load or not), you can use adaptive-images.com. That lets you upload a single high-res image and then automatically serves alternate sized versions for other breakpoints/screen sizes. It works well with CMS systems and doesn’t require any changes to markup. Basically fit and forget. For use with high pixel density devices, you’ll need to use the alternate JavaScript snippet:

<script>document.cookie='resolution='+Math.max(screen.width,screen.height)+("devicePixelRatio" in window ? ","+devicePixelRatio : ",1")+'; path=/';</script>

You can also take a look at the Adaptive Images GitHub project here

W3C

The W3C also has a responsive images community working group that is trying to establish a way of inserting alternate media sources into code. This may provide the ability to specify different media for different device ranges directly through markup. In the meantime, you can use the proposed picture element using a polyfill…

Picturefill

Scott Jehl produces some cracking work. His Picturefill is a JavaScript polyfill solution that allows you to write img tag markup similar to the markup pattern used in HTML5 video tags. Add the JavaScript file and then include markup like this:

<div data-picture data-alt="Source changes depending upon the media query style expression(s)">
 <div data-src="normal.jpg"></div>
 <div data-src="high-res.jpg" data-media="(min-resolution: 192dpi),(-webkit-min-device-pixel-ratio: 2),(min--moz-device-pixel-ratio: 2),(-o-min-device-pixel-ratio: 2/1),(min-device-pixel-ratio: 2),(min-resolution: 2dppx)"></div>
 <!-- Fallback content for non-JS browsers. Same img src as the initial, unqualified source element. -->
 <noscript><img src="normal.jpg" alt="Source changes depending upon the media query style expression(s)"/></noscript>
 </div>

You’ll see in that example that the syntax for the media query expression to target high pixel density devices is a little different.

If a user doesn’t have JavaScript, they get the ‘Mobile’ version of the image (inside the noscript tag, which should be the same as the smallest one that you set first). The benefit of Picturefill is that it allows different images for different resolutions. By different, I don’t just mean different sizes, you can use a completely different image if you want. The downside is that you need to manually make those images and upload them yourself (unless you have some means to automate the process).

Personally, I like ‘lazy’ solutions so tend to opt for Adaptive Images at present but use cases vary. Neither solution is perfect, they are workarounds for the current shortfall in the tools and systems available so choose your poison.

Despite all the ‘hoo-hah’ (technical term) over ‘Responsive images’ in the web community in the last year or so and plenty of developers ranting at the W3C and associated bodies for not implementing the < ;picture>; element, it just hasn’t excited me much. Although the ability to define different images in different situations with < ;picture>; certainly has it’s uses I don’t see it being widely popular even if it was implemented. It’s just too much work for authors to add a bunch of different images in the markup – even if the CMS is doing the heavy lifting for you. Sure, there will be instances but for the average website, I just don’t see it working out (that said, I thought the iPad was a terrible idea when I saw it so don’t listen to me!).

Use SVG? Responsive/High Resolution Images through markup?

Scalable Vector Graphics will (if support is good enough) work great for some situations. Sadly many tools, other than say Illustrator and Sketch (OS X only, sorry), don’t have decent SVG export options (as an example, Fireworks CS5/CS6 can export SVG with an add-on – but it doesn’t export allow exporting the SVG as outlines so any fonts that are included as part of the graphic don’t export well). Plus SVG won’t help with photos and the like. However for simple vector images, it’s hard to beat in terms of file sizes. For example, I’ve also added an SVG version of my logo for browsers that support it, rather than the higher res PNG – it looks equally good on retina displays. File size is considerably less: the PNG is 33KB, whilst the SVG file is only 4KB – that’s some difference! So to provide a PNG/JPG/GIF fallback graphic with an SVGZ for the user agents that understand, assuming you’re using Modernizr:

.logo a {
    background-image: url('../img/[email protected]');
}
.no-svg .logo a {
    background-image: url('../img/logo2.png');
}

The ‘Jobsis’ observation

Section added October 2012

At present, if you’re working with Fireworks or Photoshop, when you export a picture (a JPG say) you look at the preview window and amend the quality slider. I tend to bring it down until I can perceive the image quality deteriorating and then adjust it back up a little. The threshold differs depending upon the image so it’s not advisable to use the same setting each time (although we all do it!).

Daan Jobsis made an intriguing observation. Remember above when we looked at exporting images at a certain size and then scaling them down? The nub of the ‘Jobsis observation’ is that an image exported at greater size looks just as good when scaled down (e.g. a 400x 400px image scaled to a space 200 x 200px) even when the compression of the image is very high (e.g. that quality slider set to something like 20 in Fireworks/Photoshop). It seems that what’s important for perceived quality is the amount of compressed pixels rather then the quality of the compressed pixels.

I highly recommend you check out the ‘Jobsis observation’ and his follow up post (you may need to translate it from Dutch but Google will do that for you). In the meantime, here’s an initial example of what Jobsis has observed:

Here’s my own little test of Daan’s observation:

Her Majesty exported at 250px x 187px, 100% quality set in Fireworks on export. Size is 42.3KB

Her Majesty exported at 1600px x 1186px (displaying at 250px wide), 80% quality set in Fireworks on export. Size is 186.1KB

Her Majesty exported at 1600px x 1186px (displaying at 250px wide), 34% quality set in Fireworks on export. Size is 30.9KB

Taking it to the logical conclusion…

Her Majesty exported at 1600px x 1186px, 1% quality set in Fireworks on export. Size is 15.8KB

Obviously at this extreme things are starting to look noticeably poorer than the alternative but how about that previous example? 34% quality, full size export looks good to me on retina and is still smaller (in KB) than the native at full quality.

I don’t want to steal Daan’s thunder so go have a look at his post for more on this.

The takeaway for me: at this point, this sounds like a viable technique, export a graphic once and have it look good across multiple screen densities. It needs using in anger far more but at this point I’m loving the simplicity of this technique.

One big caveat to this technique though is that if the scaled image is intended to downloaded for use locally (or at native size for any reason) it will likely look like a dog’s dinner.

Testing different devices

Previously, testing multiple devices required, well… multiple devices. There’s still no substitute for that. But, if, like me, you work with responsive designs most of the time, it’s a bit of a chore to test iterations: pressing refresh on 3 or more devices to see what the latest tweaks did. I’d looked at Weinre but never actually set it up. Thankfully, Adobe Labs have released a GUI version of the system called ‘Adobe Edge’. It enables your devices to remain in sync so that when you refresh the browser on one device, it automatically refreshes the page on the other devices too. Furthermore, it enables Firebug/Developer tools style debugging so you can see if the high resolution images are actually working or not.

Your feedback welcomed

I’m hoping this ‘whistle-stop tour’ of providing high resolution images for retina displays was informative. However, I welcome feedback, queries and corrections in the hope of improving this piece in any way.