How Apple.com will serve retina images to new iPads
One of the more interesting questions raised by the new iPad and its retina display is whether or not web sites should deliver higher resolution images when there is no way to know the connection speed. AppleInsider found that Apple.com is being prepped to deliver high-res images and documented how you can test it in Safari on your desktop.
As you can imagine given my research on responsive images, I was keenly interested to see what approach Apple took.
What they’ve chose to do is load the regular images for the site and then if the device requesting the page is a new iPad with the retina display, they use javascript to replace the image with a high-res version of it.
The heavy lifting for the image replacement is being done by image_replacer.js. Jim Newberry prettified the code and placed it in a gist for easier reading.
The code works similarly to responsive images in that data attributes are added to the markup to indicate what images should be replaced with high-res versions:
1 2 3 4 5 6 7 8 9 10 11 12 13 | <article id="billboard" class="selfclear" data-hires="true"> <a id="hero" class="block" class="/ipad/"> <hgroup> <h1> <img src="/img/spacer.gif"> |
Unlike most of the solutions I reviewed last summer, Apple is applying the data-hires attribute to the parent container instead of to the images to themselves. Also, the images borrow from native iOS development and have consistent sizes. So the high-res version of ‘ipad_title.png’ can be found at ‘ipad_title_2x.png’.
As far as I can tell, there is no attempt to prevent duplicate downloads of images. New iPad users are going to download both a full desktop size image and a retina version as well.
The price for both images is fairly steep. For example, the iPad hero image on the home page is 110.71K at standard resolution. The retina version is 351.74K. The new iPad will download both for a payload of 462.45K for the hero image alone.
The total size of the page goes from 502.90K to 2.13MB when the retina versions of images are downloaded.
Another interesting part of image_replacer.js is that it checks for the existence of 2x images before downloading them:
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 | requestHeaders: function (c) { var a = this; if (typeof a._headers === "undefined") { var b = new XMLHttpRequest(); var a = this; src = this.hiResSrc().replace(/^http://.*.apple.com//, "/"); b.open("HEAD", src, true); b.onreadystatechange = function () { if (b.readyState == 4) { if (b.status === 200) { a._exists = true; var f = b.getAllResponseHeaders(); a._headers = { src: src }; var d, e; f = f.split("r"); for (d = 0; d < f.length; d++) { e = f[d].split(": "); if (e.length > 1) { a._headers[e[0].replace("n", "")] = e[1] } } } else { a._exists = false; a._headers = null } if (typeof c === "function") { c(a._headers, a) } } }; b.send(null) } else { c(a._headers, a) } }, |
This is probably necessary as they move to providing two sets of assets in case someone forgets to provide the retina image. It prevents broken images. Unfortunately, it means that there are now three http requests for each assets: a GET request for the standard image, a HEAD request to verify the existence of the retina image, and a GET request to retrieve the retina image.
Another interesting bit of image_replacer.js is when they decide to go retrieve the double-size image:
26 | if ((this.options.debug !== true) && ((typeof AC.Detector !== "undefined" && AC.Detector.isMobile()) || (AC.ImageReplacer.devicePixelRatio() <= 1))) { |
Of particular interest is the test for AC.Detector.isMobile()
. This is defined in a separate script called browserdetect.js (prettified gist version).
The browserdetect.js is full of user agent string parsing looking for things like operating systems and even versions of OS X. The isMobile()
function does the following:
166 167 168 169 | isMobile: function (c) { var d = c || this.getAgent(); return this.isWebKit(d) && (d.match(/Mobile/i) && !this.isiPad(d)) }, |
Basically, is this a WebKit browser, does the user agent mention mobile, and let’s make sure it isn’t an iPad. Browsers not using WebKit need not apply.
Testing for yourself
AppleInsider’s instructions on how to test the retina version of Apple.com on your computer are very easy. Open Apple.com in Safari. Go to the console in the Web Inspector and type the following:
AC.ImageReplacer._devicePixelRatio = 2 new AC.ImageReplacer() |
You’ll get back a klass
object as shown below.
As an aside, notice SVG references in the console screenshot.
What can we learn from this?
Probably not a whole lot for a typical site because our goals will be different than Apple’s.
For Apple, it probably makes more sense to show off how wonderful the screen is regardless of the extra time and bandwidth required to deliver the high-resolution version. For everyone else, the balance between performance and resolution will be more pressing.
There are a few minor things that we can take away though:
- Planning ahead and knowing that you can depend on high-res images being available would be preferable to making extra HEAD requests to check to see if the images exist.
- Setting priority on which images to replace first is a good idea. This is something to look at and borrow from image_replacer.js.
- The retina version of Apple.com’s home page is four times the standard home page. Delivering retina images should be considered carefully.
Perhaps most importantly, Apple isn’t sitting on some secret technique to make retina images work well. Maybe they will provide better solutions in iOS 6. The way they handle images—downloading both sizes plus an additional HEAD request—may be the least efficient way to support retina displays. But for Apple, it likely doesn’t matter as much as it will for your site.
Hat tip to Jen Matson for pointing me to the original AppleInsider article.
Further reading
- ipad3′s retina display will wreak havoc on the web
- optimizing web experiences for high resolution screens
- 3.1 Million Pixels Are Heavy
48 Comments on “How Apple.com will serve retina images to new iPads”
Do we need to rebuild the web every time Apple releases a new device?
God I hope so.
They have a spectacular habit of driving developers down a better path.
Every time a brand release such a nice display with a higher DPI? Yes, unless you don’t want to look good. But no worries, it won’t happen again anytime soon.
It’s not because of a new Apple device that this will become a common routine, but we all know that in a somewhat near future desktop and laptop screens will be replaced with high-dpi screen such as the iPad’s and larger images will be required to show the same level of detail and sharpness. This will not happen _too_ soon though.
This is great! I was wondering how best to handle the new site on iPad. I tried it in the iOS Simulator the other day and the design looked all pixel-ey… *shudder*
Can’t wait to update the site for retina iPad, thanks! :D
I am planning to move to using mostly/solely high-res images, at least for things like logos, but I will not be providing a fallback (except in some cases for IE <= 8). High-res only, let the browser scale it.
It's not a perfect solution, but I think it's at least as good as Apple's, and it's not overthinking it.
@room34 : so any device with any connexion will download big images using 4 times more bandwith and RAM then will need to scale them down for the vast majority of your users ?
they will apreciate for sure.
and you too will need 4 times more bandwidth (server-side), 4 time more disk space and your disk cache will handle 1/4th of what it did before. not a problem if you don’t have too many users.
Not really. Even if your site is image heavy, due to compression properties, it’ll be closer to 3x more disk space and bandwidth for images.
“Planning ahead and knowing that you can depend on high-res images being available would be preferable to making extra HEAD requests to check to see if the images exist.”
OR as they already rely on JS and download the first version they could just use transparent content negociation my.opera.com/karlcow/blog/2011/12/08/responsive-images-and-transparent-content-negotiation-in-http
Send Alternates: with the first request and download the appropriate second one if needed.
OR if they want to download once. use the HEAD first, look for Alternates HTTP header and GET the one which is appropriate for their own use.
@karl your post says the spec was never implemented. So this isn’t a viable option without changes to browsers, correct?
@Jason What Apple is doing is a kind of JS proprietary shiv. Basically there is no spec for responsive img and they implement a mechanism which is not that effective. What I propose is to improve a little bit what they are doing.
1. Serve the page with img src="/img/spacer.gif"> 2. GET /verylowresfoo HTTP/1.1 send along the “Alternates:” URI.
3. the shiv makes the request for the appropriate one using the “Alternates:”.
So indeed the browser could be modified, but it is not mandatory. It’s basically a similar option than the ones used by Apple but with a request less. 2 HTTP GET (instead of 3)+2 downloads.
In the case where you would not care at all for JSless user agents, you could have HEAD+GET, it makes 2 HTTP req + 1 download.
This technique is very similar to what Josh Emerson proposed at Responsive Summit a month ago. The JS is on GitHub: https://github.com/joshje/Responsive-Enhance
It’s worth mentioning the *perceived* speed isn’t +4x. The browser downloads inline images first, then after the page load JS sweeps through requesting the hi-res images and replacing them.
If replacement occurs before the user scrolls to the updated content the process is invisible, and not really slower than the lo-res load.
Jeremy Keith has a good outline of the approach here: adactio.com/journal/5208/
Very interesting—thanks for the details and testing procedure. I think I’ll use _2x for things like logos, maps, diagrams and hard art, and only rarely for photos and elements that look OK soft. Replacing just a couple key images (logo, background texture) on my upcoming site has only added about 30-40k, but it’s a great effect. A low-res logo really is noticeable with retina-sharp text it. I’ve felt than even when simply pinch-zooming on an older iPad. But now it’s important even at default zoom.
Also interesting to see WHICH images they opt to provide 2x versions for. From poking around on:
www.apple.com/ipad/features/
The largest images are explicitly named “nohighres” like:
images.apple.com/ipad/features/images/retina_nohires.jpg
whereas a lot of the smaller images have 2x versions.
Anyone have an idea why the bigger images are not getting the upgrade? Bandwidth?
Cause those skin pores would be HUGE?!
I have no idea. Not clear why the the text images weren’t done as background images via CSS and media queries. Right?
@lukew
My guess would be because they just didn’t have the assets for it.
It seems logical that they would use ‘nohighres’ as a flag that no high res image was available.
The larger the image is, the more likely a super high res version was thrown out before they knew they would be doing this.
@lukew A few things are different about images with nohires in the file names.
First, two of them are background images, not img tags like the other assets that are being controlled by image_replacer.js. I’ve reviewed the CSS and I don’t see any rules that replace the images with high res versions using a pixel density rule.
But what I do see is a bunch of logic in