The new iPhone 4 is certainly a marvel of technology. One of the surprising side effects it has is how bad it makes things look that weren’t designed for high-DPI displays. For the most part, text is automatically beautiful on the iPhone 4 1. However, images are a different story. What follows is my research and then the technique I used to update images on flowz.com. We use wordpress here and with this technique, image replacement is automatic. All you have to do different is upload the second, high-res file at the same time you insert the normal file in your post or page. The first thing we need to do is pull in CSS specific to high-DPI device(s) in a standards compliant way. Here is how you can do that: http://thomasmaier.me/2010/06/css-for-iphone-4-retina-display/ The trick being to use a CSS3 media query to target high DPI devices (*not* just the iPhone 4!):
<link rel="stylesheet" media="only screen and (-webkit-min-device-pixel-ratio: 2)" type="text/css" href="css/highdpi.css" />
But that is only 1/3rd the story. How do you make sure high resolution images are used when viewing with a High-DPI display? Aral Balkan has the answer: http://aralbalkan.com/3331 But what he illustrates only works for “sprites.” That is, divs and other elements that use background-images where the src is specified in CSS. In that special CSS file, we specify the size of the background and include high-res background images that override the normal CSS. However, there is the problem of elements that specify their src in the HTML. I.e., img, video, etc. Aral, touches on what is required but leaves the implementation as an exercise. I’ll dive into the details:
- Create double resolution copies of all your images. Name these with a suffix of “@2x”. So for
logo.png, the double res would belogo@2x.png. Make sure that all of your image tags have at least a width attribute specified. (But, you are already doing that aren’t you?) If you don’t, your image dimensions will be increased to the new high-DPI source by the browser. - Create a highdpi.css file. Contents:
The sole class,.replace-2x { font-size: 1px; }.replace-2xcontains an attribute that has no effect on img tags. This is the flag that will tell our javascript it is okay to do image replacement. - In your source HTML, add the
replace-2xclass to all of your images that have a double resolution counterpart. - In your HTML head, add the link for your high-dpi stylesheet:
<link rel="stylesheet" media="only screen and (-webkit-min-device-pixel-ratio: 2)" href="/css/highdpi.css"/>
- Create and include in your source, a highdpi.js file. We are going to assume you are using jQuery for your site. Contents:
function highdpi_init() { if(jQuery('.replace-2x').css('font-size') == "1px") { var els = jQuery(".replace-2x").get(); for(var i = 0; i < els.length; i++) { var src = els[i].src src = src.replace(".png", "@2x.png"); els[i].src = src; } } } jQuery(document).ready(function() { highdpi_init(); });
The trick here is that we check to see if the font size for our special class is set to value declared in our high-DPI CSS file. (Our flag) It will only be set if the browser passed the media query. If the flag is set, grab every element in the DOM with the replace-2x class and change the src attribute to point to our high-res counterparts. If you have access to one, pull up http://flowz.com on an iPhone 4 and take a look at our logos here (zoom in if it isn't immediately apparent to you):

Image without ".replace-2x"

Image with ".replace-2x"
(Under normal DPI monitors, the images will look identical.)
- Note: all text looks good, except text rendered using Cufón and sIFR. We hit that problem. A blog post will follow on that. ↩
Sidebar
I really, really wanted to be able to do something different then what I described above. I wanted to define a class: .highdpi-supported on the body tag of all the pages that support high-dpi. I then wanted to add an uncommon attribute on that class to act as the flag to tell javascript to go ahead with image replacement. Unfortunately, I could not find any such attribute that was relatively safe *and* didn't affect page layout. So in the end, I'm stuck with adjusting the font-size attribute on the .replace-2x class. Maybe someone else can come up with a better flag.

Thanks for the very useful article Mike.
I noticed that one of the snippets of code you’ve posted doesn’t work, simply due to the missing -webkit- in front of min-device-pixel-ratio within the css3 media query.
Once I added -webkit- to this line, it all worked great.
Thanks again.
Thanks, good catch! I’ve updated the code.
Mike,
Great – thanks for this, it’s helped me get my code up to snuff for Retina on the iPhone. The only issue I had with your code is that you assume I use jQuery. On a webserver, you’re absolutely correct, I would – but this same code is relevant for internal HTML files on the iPhone itself.
One can load jQuery on the iPhone, I’ve done it – but on a native app it lags 1-2 seconds while loading, which is jarring to the user and I’d rather avoid it.
I suppose it’s possible to do this w/o jQuery but I am not a Javascript master, so…
Anyway, thanks!
How are you handling .jpg and .gif using this technique? Or is there a better solution now (a year later).
Mark,
I’d imagine the easiest thing to do would be to look at the line:
src = src.replace(“.png”, “@2x.png”);
and insert right after it:
src = src.replace(“.jpg”, “@2x.jpg”);
src = src.replace(“.gif”, “@2x.gif”);
That way, all three formats are covered and you don’t care which file format is being used.
You are my hero for posting this. Thanks so much.
Really useful, Mike!
Thanks a lot
I’m currently implementing this method, but noticed that it causes the browser to make 2 requests for each image. First, it requests the normal image, and then if the JS recognizes we’re on a high-res device, it changes the img src, and a second request happens–the request for the @2x image.
You can use the net tab in Firebug to see this happening. Is there a recommended way to avoid this? I imagine that loading twice as many images will have a significant effect on a mobile site’s performance.
thanks so much for this. I am using jqtouch and was struggling BIG TIME with the fact that their icons were super pixelated. I have looked on countless sites before I came across yours. Your fix it brilliant!
Thanks again!
Chris,
This method is a trade off between performance and maintainability/ease-of-use. You certainly could create a site that only makes one request to the server for each image. The way to do that would be to serve up the HTML with src’s pointing to @2x files the first time around. However, that would require either your server-side application to support that, specifically, or you to maintain two separate source files.
Not working on http://staging.appulize.com any ideas please?
Thanks
Maciej
What I don’t understand is that the JavaScript to replace the images is being called regardless of whether “retina.css” is loaded. What can I add to the jQuery function call to see if the retina.css even go loaded?
I found the answer to my own question. Detect whether its a retina display with Javascript. http://briancray.com/2011/05/05/detect-retina-displays-with-javascript/
@amy:
The function, highdpi_init(), is being called no matter what. However, the code that actually does the image replacement is only executed if the retina.css file is loaded. This line here:
if(jQuery(‘.replace-2x’).css(‘font-size’) == “1px”) {
Will only evaluate to true if retina.css was included.
HTH.