spacer

Cloud Four Blog

Technical notes, War stories and anecdotes

How Apple.com will serve retina images to new iPads

spacer
Jason Grigsby

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.

spacer

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.

spacer

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”

  1. spacer fredo says:
    March 13, 2012 at 3:22 pm

    Do we need to rebuild the web every time Apple releases a new device?

    • spacer Greg McAusland says:
      April 11, 2012 at 1:10 pm

      God I hope so.

      They have a spectacular habit of driving developers down a better path.

  2. spacer fabien says:
    March 13, 2012 at 3:53 pm

    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.

  3. spacer Fernando says:
    March 13, 2012 at 3:55 pm

    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.

  4. spacer George Hammerton says:
    March 13, 2012 at 4:29 pm

    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

  5. spacer room34 says:
    March 13, 2012 at 4:29 pm

    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.

  6. spacer joey says:
    March 13, 2012 at 7:06 pm

    @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.

    • spacer Gustav says:
      June 13, 2012 at 11:29 am

      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.

  7. spacer karl says:
    March 13, 2012 at 9:28 pm

    “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.

    • spacer Jason Grigsby says:
      March 13, 2012 at 9:32 pm

      @karl your post says the spec was never implemented. So this isn’t a viable option without changes to browsers, correct?

  8. spacer karl says:
    March 14, 2012 at 4:30 am

    @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.

  9. spacer Brendan Falkowski says:
    March 14, 2012 at 7:00 am

    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/

  10. spacer Adams Immersive says:
    March 14, 2012 at 11:20 am

    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.

  11. spacer LukeW says:
    March 14, 2012 at 12:25 pm

    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?

  12. spacer Nate says:
    March 14, 2012 at 12:47 pm

    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?

  13. spacer uxmatthew says:
    March 14, 2012 at 1:12 pm

    @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.

  14. spacer Jason Grigsby says:
    March 14, 2012 at 2:39 pm

    @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

gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.