Jump to main navigation, main content

Archived entry | Matt Wilcox .net

Adaptive Images for Responsive Designs

Adaptive Images for Responsive Designs

So you’ve been building some responsive designs and you’ve been working through your checklist of things to do:

  • You started with the content and designed around it, with mobile in mind first.
  • You’ve gone liquid and there’s nary a px value in sight; % is your weapon of choice now.
  • You’ve baked in a few media queries to adapt your layout and tweak your design at different window widths.
  • You’ve made your images scale to the container width using the Fluid Image technique.
  • You’ve even done the same for your video’s using a nifty bit of JavaScript.

You’ve done a good job so pat yourself on the back. But there’s still a problem, and it’s as tricky as it is important: image resolutions.

HTML has an <img> problem

CSS is great at adapting a website design for different window sizes - it allows you not only to tweak layout but also to send re-scaled versions of the design’s images. And you want to do that because a mobile phone does not need a 1900px background image after all [1].

HTML is less great. In the same way that you don’t want CSS background images being larger than required, you don’t want that happening with <img>’s either. A mobile phone only needs a small image but desktop users need a large one. Unfortunately <img>’s can’t adapt like CSS, so what do we do?

Well, you could just use a high resolution image and the Fluid Image technique would scale it down to fit the viewport: but that’s sending an image five or six times the file size that’s really needed. Which makes it slow to download and unpleasent to use. Mobiles are pretty impressive devices - my ancient iPhone 3G is more powerful in every way than my first proper computer - but they’re still terribly slow in comparison to today’s desktop machines. Sending a massive image means it has to be manipulated in memory and re-drawn as you scroll. You’ll find phones rapidly run out of RAM and slow to a crawl.

Well OK, you went mobile first with everything else so why not put in mobile resolution <img>’s too? Well because even though mobile device’s are rapidly gaining share in your analytics stats they’re still not likely to be the major share of your user base. I don’t think desktop users would be happy with pokey little mobile resolution images, do you? What we need are Adaptive Images.

Adaptive Image techniques

There are a number of possible solutions, they all come with pro’s and con’s, and it’s not as simple to find a graceful solution as you might expect.

Your first thought might be to use JavaScript to trawl through the mark-up and re-write the source attribute. That’ll get you the right end result, but it’ll have done it in a way you absolutely don’t want. That’s because of the way browsers load resources. It starts to load the HTML and builds the page on-the-fly, as soon as it finds an <img> tag it immediately asks the server for that image. After the HTML has finished loading the JavaScript will run, change the src attribute, and then the browser will request that new image too. Not instead of, but as well as. Not good, that’s added more bloat instead of cutting it.

Plain JavaScript is out then, which is a problem because what other tools do we have to work with as web designers? Let’s ignore that for now and let me outline another issue with the concept of serving different resolution images for different window widths: a basic file management problem. To request a different image that image has to exist on the server. How’s it going to get there? That’s not a trivial problem, especially if you have non-technical users that update content through a CMS. Let’s say you solve that - do you plan on a simple binary switch “big image|little image”? Is that really efficient or future proof? What happens if you’ve an archive of existing content that needs to behave this way? Can you apply such a solution to existing content or mark-up?

There’s a detailed round-up of potential techniques for solving the Adaptive Images problem over at http://www.cloudfour.com/responsive-imgs-part-2/ if you fancy a dig around exploring all the options currently available. But I’m here to show you what I think is the most flexible and easy to implement solution, so here we are.

Adaptive Images

Adaptive Images aims to mitigate most of the issues surrounding the problems of bringing the venerable <img> tag into the 21st century. And it works by leaving that tag completely alone - just add that desktop resolution image into the mark-up as you’ve been doing for years now. We’ll fix it using secret magic techniques and bottled pixie dreams. Well, fine: with one .htaccess file, one small PHP file, and one line of JavaScript. But you’re killing the mystique with that kind of talk.

So, what does this solution do?

  • It allows <img>’s to adapt to the same break-points you use in your Media Queries, giving granular control in the same way you get with your CSS.
  • It installs on your server in five minutes or less and after that is automatic and you don’t need to do anything.
  • It generates it’s own re-scaled images on the server and doesn’t require mark-up changes; so you can apply it to existing web content.
  • If you wish, it will make all of your images go mobile-first (just in case that’s what you want if JS/Cookies aren’t available).

Sound good? I hope so. Here’s what you do:

Setting up and rolling out

I need to assume you’ve got some basic server knowledge along with that wealth of front-end wisdom exploding out of your head: that you know not to over-write any existing .htaccess file for example, and how to set file permissions on your server. Feeling up to it? Excellent:

  • Download the latest version of Adaptive Images either from the website or from the GitHub repository.
  • Upload the included .htaccess and adaptive-images.php files into the root folder of your website.
  • Create a directory called ai-cache and make sure the server can write to it (CHMOD 755 should do it).
  • Add the following line of JavaScript into the <head> of your site:

    <script>document.cookie='resolution='+Math.max(screen.width,screen.height)+'; path=/';</script>

That’s it, unless you want to tweak the default settings. You likely do, but essentially you’re already up and running.

How it works

Adaptive Images does a number of things depending on the scenario the script has to handle but here’s a basic overview of what it’s doing when you load a page running Adaptive Images:

  1. A session cookie is written with the value of the visitor’s screen size in pixels.
  2. The HTML encounters an <img> tag and sends a request to the server for that image. It also sends the cookie, because that’s how browsers work.
  3. Apache sits on the server and receives the request for the image. Apache then has a look in the .htaccess file to see if there are any special instructions for files in the requested URL.
  4. There are! The .htaccess says “Hey server, any request you get for a JPG, GIF, or PNG file just send to the adaptive-images.php file instead.”
  5. The PHP file then does some intelligent thinking which can cover a number of scenario’s but I’ll illustrate one path that can happen:
  6. The PHP file looks for the cookie and finds out that the user has a maximum screen size of 480px.
  7. The PHP has a look at the available Media Query sizes that were configured and decides which one matches the users device.
  8. It then has a look inside the /ai-cache/480/ folder to see if a rescaled image already exists there.
  9. We’ll pretend it doesn’t - the PHP then goes to the actual requested URI and finds that the original file does exist.
  10. It has a look to see how wide that image is. If it’s already smaller than the user’s screen width it sends it along and stops there. But, let’s pretend it’s 1000px wide:
  11. The PHP then resizes the image and saves it into the /ai-cache/480 folder ready for the next time someone needs it.

It also does a few other things when needs arise, for example:

  • It sends images with a cache header that tells proxies not to cache the image whilst telling browsers they should. This avoids problems with proxy servers and network caching systems grabbing the wrong image and storing it.
  • It handles cases where there isn’t a cookie set, and you can choose whether to then send the mobile version or the largest configured Media Query size.
  • It compares time-stamps between the source image and the generated cache image - to ensure that if the source image gets updated, the old cached file won’t be sent.

Customising it

There are a few options you can customise if you don’t like the default values. By looking in the PHP’s configuration section at the top of the file you can:

  • Set the resolution breakpoints to match your Media Query breakpoints
  • Change the name and location of the ai-cache folder
  • Change the quality any generated JPG images are saved at
  • Have it perform a subtle sharpen on re-scaled images to help keep detail
  • Toggle whether you want it to compare the files in the cache folder with the source ones or not
  • Set how long the browser should cache the images for
  • Switch between a mobile first or desktop first approach when a cookie wasn’t found

More importantly, you probably want to omit a few folders from the AI behaviour. You don’t need or want it re-sizing the images you’re using in your CSS for example. That’s fine - just open up the .htaccess file and follow the instructions to list any directories you want AI to ignore. Or, if you’re a dab hand at re-write rules you can remove the exclamation mark at the start of the rule and it’ll only apply AI behaviour to a given list of folders.

Caveats

As I mentioned, I think this is one of the most flexible, future-proof, retro-fittable, and easy to use solutions available today. But, there are problems with this approach as there are with all of the one’s I’ve seen so far. In the case of Adaptive Images they are these:

This is a PHP solution.

I wish I was smarter and knew some fancy modern languages the cool kids discuss at parties, but I don’t. So, you need PHP on your server. That said, Adaptive Images is Creative Commons and I would welcome anyone contributing a port of the code. [2]

Content Delivery Networks.

Adaptive Images relies on the server being able to intercept requests for images, do some logic, and then send one of a given number of responses. Content Delivery Networks are generally dumb-caches, and they won’t allow that to happen. Adaptive Images will not work if you’re using a CDN to deliver your website.

A minor but interesting Cookie setting issue.

As Yoav Weiss pointed out in his article Pre-loaders, Cookies, and race-conditions: there is no way to guarantee that a cookie will be set before images are requested - even though the JavaScript that sets the cookie is loaded by the browser before it finds any <img> tags. That could mean images being requested without a cookie being available. Adaptive Images has a two-fold mechanism to avoid this being a problem:

  1. The $mobile_first toggle allows you to choose what to send a browser if a cookie isn’t set. If FALSE then it will send the highest configured resolution, if TRUE it will send the lowest.
  2. Even if set to TRUE, Adaptive Images checks the User Agent String. If it discovers the user is on a desktop environment it will over-ride $mobile_first and set it to FALSE.

This means that if $mobile_first is set to TRUE and the user was unlucky (their browser didn’t write the cookie fast enough) mobile devices will be supplied the smallest image, and desktop devices will get the largest.

The best way to get a cookie written is to use JavaScript as I’ve explained above, because it’s the fastest. However, for those that want it there is a no-javascript method which uses CSS and a bogus PHP “image” to set the cookie. A word of caution: because it requests an external file this method is slower than the JS one, and it is very likely that the cookie won’t be set until after images have been requested.

The Future

For today Adaptive Images is a pretty good solution. It works, and as it doesn’t interfere with your mark-up or source material in any way, the process is “non-destructive”. If a future solution is superior you can just remove the Adaptive Images files and you’re good to go - you’d never know AI had been there.

However this isn’t really a long-term solution in the grand scheme of things, not least because of the intermittent problem of the cookie/image-request race condition. What we really need are a number of standardised ways to handle this in the future.

Firstly we could do with browsers sending far more information about the user’s environment along with each http request (the device size, the connection speed, the pixel density, etc) because the way things work now is no longer fit for purpose. The web now is a much broader entity used on far more diverse devices than when these technologies were dreamed up, and we absolutely require the server have better knowledge about device capabilities than is currently possible. Relying on cookies to do this job is not cutting it, and the User Agent String is a complete mess no longer fit for the varying purposes we are forced to hijack it for.

Secondly, we need a W3C backed mark-up level solution for when we need to supply semantically different content at different resolutions, not just re-scaled versions of the same content, as Adaptive Images does.

I hope you’ve found this interesting and will find Adaptive Images useful.

Foot Notes

  1. While I’m talking about stopping mobile phones from downloading resources they don’t need; You should be careful of your media query construction if you want to avoid Webkit downloading all the images in all of the CSS files. [back to reference]
  2. There currently exists a ColdFusion port of an older version of Adaptive Images at https://github.com/cfjedimaster/Adaptive-Images. I do not have anything to do with ported versions of Adaptive Images. [back to reference]
  3. Adaptive Images has a very broad Creative Commons license and I warmly welcome feedback and community contributions via the GitHub repository.

This article originally appeared on 24 Ways. I’ve added it here for archival purposes.

From the archives

Other enteries filed under:

Web Development

Site information

Built with valid XHTML and CSS, designed with web standards and accessibility in mind. Best viewed in a modern browser [Firefox, Safari, Opera]

This domain and all content is a copy of my old website, for historical purposes only.