Responsive Images: Experimenting with Context-Aware Image Sizing

Posted by Scott on 12/14/2010

Responsive Image illustration: contextual image sizes shown on iPhoneanddesktopResponsive Web Design has been a very hot topic this year, inspiring developers who long for a “one codebase serves all” future. But critics of the technique have pointed out several issues that need to be addressed before it can scale perfectly from handheld to desktop — specifically, while HTML, CSS and JavaScript can be light enough to share across all experiences, desktop-optimized images are often too heavy for mobile connections, and mobile-optimized images are too low quality for desktop viewers.

Recently, we began work on a large-scale responsive design (collaborating with the incredibly talented Mr. Ethan Marcotte, who notably got this whole “responsive” party started), in which we’ve set out to address this shortcoming of responsive design with a technique that requests an appropriate image size for a given context without resorting to tactics like User Agent sniffing. We feel strongly that these responsive techniques present a most practical way forward in web development. This post documents our in-progress experiment with “Responsive Images.”

What is this all about?

The goal of this technique is to deliver optimized, contextual image sizes for responsive layouts that utilize dramatically different image sizes at different resolutions. Ideally, this approach will allow developers to start with mobile-optimized images in their HTML and specify a larger size to be used for users with larger screen resolutions — without requesting both image sizes, and without UA sniffing.

How’s it work?

The technique consists of a few key files: rwd-images.js, rwd.gif, and some .htaccess directives. To use, just drop the .htaccess file into your root web directory (this requires an Apache web server, and if you already have an .htaccess file, you can paste its contents into your existing file), and reference rwd-images.js from an HTML page.

Any images that you’d like to scale and load responsively should start by referencing a mobile-friendly image size, adding a “.r” prefix to the file extension, and reference a larger size using a HTML5 custom attribute data-fullsrc (the image referenced by data-fullsrc will be used for screen resolutions greater than 480px).

<img src="small.r.jpg" data-fullsrc="large.jpg">

As that page loads, the rwd-images.js script inserts a BASE element into the head of your page, directing all subsequent image, script, and stylesheet requests through a fictitious directory called "/rwd-router/". As these requests reach the server, the .htaccess file determines whether the request is a responsive image or not. It redirects responsive image requests temporarily to rwd.gif (a transparent 1px gif image), while all non-responsive image requests go to their proper destination through a URL rewrite that ignores the "/rwd-router/" segment. When the HTML finishes loading, rwd-images.js removes the BASE element from the DOM (resetting the base URL) and sets the responsive image sources to either their small or large size (if specified), depending on whether the screen resolution is greater than 480px.

We’ve found that the technique works in most modern browsers (Safari (desktop, iPhone, iPad), Chrome, Internet Explorer (8+), Opera). The technique degrades gracefully in other browsers, such as IE7 and under. In those browsers, desktop users will still receive an appropriate image size, but the mobile image will download as well, causing slight but unfortunate overhead. As development continues, we may find a workaround to bring full support to these browsers.

Demo Page

You can view a demonstration of the technique at the following URL: http://filamentgroup.com/examples/responsive-images/

Usage Instructions, Documentation, and Ongoing Development

We’re still experimenting with this technique, and we’re looking for contributions and ideas. To read more about the project, or to fork the project and contribute, please check out our project on Github. While you’re there, check out the issue tracker to see the problems we’re currently working out.

Responsive Images on Github

Comments

<div id="commentNumber1" class="commentEntry">
<p>This is great.

For further reading, see http://tinysrc.net - I’m trying to see how I can work your ideas into the way it works. More thoughts welcome!

(tinySrc uses either explicit screen-size declaration or device-specific sizing - so might be useful for those instances when you don’t have control over the images being pulled into your app or you’re also dealing with non-JS-friendly clients)

</div>
	<p class="posted"><a href="#commentNumber1">Comment</a> by

James Pearce on 12/14  at  03:27 PM

<div id="commentNumber2" class="commentEntry">
<p>Looks great on my Droid and imac

</div>
	<p class="posted"><a href="#commentNumber2">Comment</a> by

stacey on 12/14  at  05:48 PM

<div id="commentNumber3" class="commentEntry">
<p>This would be the perfect place for content negotiation. Anyone else think a “Vary: Resolution” header is in order?

</div>
	<p class="posted"><a href="#commentNumber3">Comment</a> by

Jason Karns on 12/15  at  01:26 AM

<div id="commentNumber4" class="commentEntry">
<p>It would be better to have ‘progressive’ images and the browser aborts the download when it has good enough resolution of the image.

</div>
	<p class="posted"><a href="#commentNumber4">Comment</a> by

zcorpan on 12/15  at  04:44 PM

<div id="commentNumber5" class="commentEntry">
<p>“To use, just drop the .htaccess file into your root web directory (this requires an Apache web server”

- Does this mean it is not possible with IIS?

</div>
	<p class="posted"><a href="#commentNumber5">Comment</a> by

Lorro on 12/16  at  08:10 AM

<div id="commentNumber6" class="commentEntry">
<p>Lorro, should be possible in IIS.&nbsp; All the .htaccess file does it some URL rewriting.&nbsp; You would just need to translate those rules to routes or some regex based rewrite rules.

</div>
	<p class="posted"><a href="#commentNumber6">Comment</a> by

Thomas on 12/16  at  05:24 PM

<div id="commentNumber7" class="commentEntry">
<p>Awesome Scott—I will be working this into my projects which use Responsive Design. #IOU-Beer

</div>
	<p class="posted"><a href="#commentNumber7">Comment</a> by

Eric Webster on 12/17  at  01:54 PM

<div id="commentNumber8" class="commentEntry">
<p>Tested the demo on firefox 4 “minefield” with last build.

No “double” image download.
It seems to be working fine.

</div>
	<p class="posted"><a href="#commentNumber8">Comment</a> by

Guillaume Moulin on 12/29  at  10:29 AM

<div id="commentNumber9" class="commentEntry">
<p>This is fantastic. Absolutely amazing work. I love responsive designs. It just makes good sense.

</div>
	<p class="posted"><a href="#commentNumber9">Comment</a> by

Joseph Rosenblatt on 01/17  at  11:06 PM

<div id="commentNumber10" class="commentEntry">
<p>I’ve been wrestling with this problem myself for the past few weeks, which is how I came across your article. I’ve written up my attempt, would be interested to see what you think.

http://www.craig-russell.co.uk/responsive-images-and-context-aware-image-sizing/

</div>
	<p class="posted"><a href="#commentNumber10">Comment</a> by

Craig on 01/22  at  12:11 PM

<div id="commentNumber11" class="commentEntry">
<p>I’ve just found you cited on Smashing Magazine’s article about responsive web design.

Just a question: I know that I can use custom data attributes with HTML5, but could it be feasible/easier (and useful for use with non HTML5 websites) to use your solution without “data-fullscr”?

You already rely on a rule - the “.r” prefix - for mobile-friendly images, I think you could add a second rule, adding by default another prefix when using of fullsize images is needed: “fullsize_” (or whatever else).

So in case of responsive images, you would have this code <img scr="test_image.r.jpg" > converted by your Javascript file in <img scr="test_image.jpg" > for small display and <img scr="fullsize_test_image.jpg" > for bigegr display.

Useful, I think, for using with no HTML5 - but responsive and standard compliant - webpages.

</div>
	<p class="posted"><a href="#commentNumber11">Comment</a> by

ilpiac on 02/17  at  11:06 AM

<div id="commentNumber12" class="commentEntry fg">
<p>Thanks everyone.

@Lorro: You may be able to get it to work on IIS or other server setups, but the URL rewrites will need to be adapted to work in different environments. Here’s an article about converting htaccess directives to IIS: http://learn.iis.net/page.aspx/557/translate-htaccess-content-to-iis-webconfig/

@Guillaume: thanks for the heads up! Indeed, this does now work (1 request per image) in Firefox 4 :)

@Craig: we commented on your article, but in general, PHP-generated image sizes could be a fine solution. However, we do think it’s important to serve a functional image in the base markup rather than a 0px gif (so that older devices including those with JavaScript unavailable receive the content too), so we wouldn’t recommend removing the scripts and rewrites we’ve produced to facilitate that.

@ilpiac: That’s a good point. If your server is configured in a reliable way, you could do without the data-fullsrc attributes and simply redirect any images with the “r.” extension prefix to their full size image file immediately. Doing so would likely require a fixed pattern for your file naming conventions, where you can trust that an image’s full-size equivalent can be found through a consistent change in the filename (for example, police.jpg becomes police.full.jpg or even police.jpg?full). We didn’t want to impose any filename pattern constraints on users of this script, which is why we went with the data-fullsrc approach, but if you can set things up to work like that, it sounds great!

</div>
	<p class="posted"><a href="#commentNumber12">Comment</a> by

Scott (Filament) on 02/17  at  11:26 AM

<div id="commentNumber13" class="commentEntry fg">
<p>Hi everyone,

I just pushed a new version of this technique that is significantly streamlined, and much improved. The new approach sets a cookie to store screen resolution, and the server immediately redirects to the appropriate image size. Also, the data-attr driven approach outlined above still works too, if you need the flexibility of data attributes (if your filenames are not consistent, predictable). Otherwise, the new script assumes a filename convention of (image.jpg vs. image.large.jpg) by default.

Also, this approach will work on any browser that has cookies and javascript available, which is likely a much broader range than the initial script. Of course, browsers without cookies or JS will just get the mobile image size, so whether it “works” is a matter of experience level rather than a general accessibility concern.

Check it out on Github and let us know how it works for you. Thanks!
https://github.com/filamentgroup/Responsive-Images/tree/cookie-driven#readme

</div>
	<p class="posted"><a href="#commentNumber13">Comment</a> by

Scott (Filament) on 04/02  at  03:10 PM

<div id="commentNumber14" class="commentEntry">
<p>We’ve used CSS3 Media Queries to load different image sizes for different screens. Very simple: <a href="http://techblog.viewbook.com/2011/03/the-css3-media-query-for-dynamically-resizing-elements-on-your-website-for-different-screen-sizes/" rel="nofollow">http://techblog.viewbook.com/2011/03/the-css3-media-query-for-dynamically-resizing-elements-on-your-website-for-different-screen-sizes/</a>

</div>
	<p class="posted"><a href="#commentNumber14">Comment</a> by

Rien on 04/03  at  09:20 AM

<div id="commentNumber15" class="commentEntry fg">
<p>@Rien: looks good thanks. I should clarify that this approach is about foreground images, not CSS background images, but for backgrounds we agree: media queries work well :)

</div>
	<p class="posted"><a href="#commentNumber15">Comment</a> by

Scott (Filament) on 04/03  at  11:40 AM

<div id="commentNumber16" class="commentEntry">
<p>@Scott: Indeed that’s why we used background images. And we wanted to make sure the images did not scale themselves, but that bigger or smaller images are loaded based on screen size. Thanks!

</div>
	<p class="posted"><a href="#commentNumber16">Comment</a> by

Rien on 04/04  at  05:36 AM

<div id="commentNumber17" class="commentEntry">
<p>Does this technique only switch the image on page load, or also do so if the browser is resized?

For instance, if I open a page on a desktop, but with the browser window very narrow, which image is loaded and does that change as I resize the window?

</div>
	<p class="posted"><a href="#commentNumber17">Comment</a> by

Philip Morton on 05/09  at  05:40 AM

<div id="commentNumber18" class="commentEntry">
<p>How is this better than dynamically changing the path to images that need to be optimized:

Use JS to dynamically replace image path with say /images/small, /images/medium, /images/large based on screen size.

The default image is just a 1px placeholder.

No need for .htaccess or inserting/removing BASE element.

@Rien - using background is not good for content images. For example what is a desktop user wants to print and article with images?

</div>
	<p class="posted"><a href="#commentNumber18">Comment</a> by

johans on 05/10  at  03:33 PM

<div id="commentNumber19" class="commentEntry">
<p>Ahh - found the answer to my question in your reply to Craig’s PHP approach:

“The explanation of our article doesn’t quite describe the reasoning for particular approach we took, which is providing a meaningful mobile-optimized image in the HTML, and enhancing it on larger screens without requesting both image sizes. By removing the .htaccess and the JS-generated base element from our technique, it’s impossible to serve a mobile-friendly image in the markup from the start while ensuring larger screen users do not download both image sizes (because JS alone can not prevent that first request from going out to the server). This point is critical to the approach. Simply defaulting to an empty image in the HTML means users on many feature phones (or any browser with JavaScript unavailable or disabled, and search engines as well) will receive no image at all.”

</div>
	<p class="posted"><a href="#commentNumber19">Comment</a> by

johans on 05/10  at  03:44 PM

<div id="commentNumber20" class="commentEntry">
<p>Great idea! The folder seems unnecessary. Can’t the htaccess rewrite simply look for file calls which have “.r.” in them and manipulate those?

</div>
	<p class="posted"><a href="#commentNumber20">Comment</a> by

Kevin on 05/15  at  09:58 PM

<div id="commentNumber21" class="commentEntry">
<p>Really nice article, only just getting onto the responsive web design band wagon and I’ve come up a jQuery &amp; PHP solution to image resizing which works quite well:

http://www.jamesfairhurst.co.uk/posts/view/responsive_images_with_php_and_jquery/

Would love to hear what you think.

</div>
	<p class="posted"><a href="#commentNumber21">Comment</a> by

James on 05/27  at  07:25 AM

<div id="commentNumber22" class="commentEntry">
<p>Many mobile/handheld guidelines stress including explicit WIDTH= and HEIGHT= on &lt;IMG, so underpowered layout engines don’t have to deal with changing their layout every time an image’s real size is discovered. No “Responsive Images” solutions (including this one) I’ve seen supply the WIDTH= and HEIGHT=. Am I not fully understanding something that should be obvious? Or are the mobile/handheld guidelines I’ve seen all a bit out of date and no longer to be taken seriously? Or is this still a problem in search of a solution? Or...?

</div>
	<p class="posted"><a href="#commentNumber22">Comment</a> by

Chuck Kollars on 06/12  at  10:41 PM

<div id="commentNumber23" class="commentEntry fg">
<p>Hey Chuck,

I see that advice a lot in performance guidelines too, and I’m sure it does provide a small performance gain. Unfortunately, if you’re scaling your images fluidly, the benefits of providing the width and height attrs would be lost anyway, since the browser will need to calculate its dimensions depending on its parent element when it renders the page. Also, the height attribute gets in the way of allowing an image to scale proportionally as it width increases and decreases.

It’s always game of tradeoffs, I suppose. We think the benefits of images that scale fluidly are worth it in many cases. Thanks! :)

</div>
	<p class="posted"><a href="#commentNumber23">Comment</a> by

Scott (Filament) on 06/13  at  11:24 AM

<div id="commentNumber24" class="commentEntry">
<p>Chuck, the original justification for those guidelines (such as the W3C Mobile Best Practices) was to limit the need for the browser to reflow its layout as it was loading resources - since it can plan around the bounding box of the images as soon as the markup arrives.

With a high latency network and a slow-rendering mobile browser, the effect of the page reflowing for several seconds after the markup has rendered can be very painful and obvious.

Nevertheless, constraining width and height in markup is obviously inappropriate for sites which flow and react to screen size. Since designers are increasingly targeting better performing mobile browsers on faster networks with these techniques, some of the original best practices need no longer be so rote.

However, if you were building a site or app carefully targeted to a particular screen size (or were adapting the site on the server side, where you have a chance to influence the markup responsively too), one might continue to consider adding those attributes. Although very valid, that sort of prescription is anathema to the techniques being discussed here.

</div>
	<p class="posted"><a href="#commentNumber24">Comment</a> by

James Pearce on 06/13  at  11:53 AM

<div id="commentNumber25" class="commentEntry">
<p>thank ypu adimin

</div>
	<p class="posted"><a href="#commentNumber25">Comment</a> by

herbalife on 06/17  at  06:56 AM