TL;DR Picturefill.js is a responsive image polyfill developed and maintained by the filament group and it is publicly available on GitHub. Picturefill allows for the implementation of responsive images in specifying breakpoints and pixel density. It’s awesome. There are two ways to to write up the code for responsive images:
- The <img> element and the [srcset] attribute – for when all you’re doing is showing the same image at different resolutions or sizes.
- The <picture> element – for when you want to show different images, or differently cropped images. You can check it’s implementation at my CodePen here.
Not familiar with responsive images? Read my post about it first and then come on back here.
Update: Chrome 38 has been launched to natively support the HTML mark-up that the polyfill Picturefill steps in to fill. This update for Chrome addresses the issue I have with the <img> & srcset method that I discuss below in the limitation section. So that’s awesome for Chrome, however, since it is not supported in all browsers, you’re still going to need picturefill.js running if you want to implement this solution for responsive images. Luckily, you can have picturefill.js running in Chrome 38 without conflict.
Unluckily for me: it seems to have broken one of my CodePen demonstrations… but it also seems to be busted on their site now when using Chrome, so I won’t beat myself up about it.
What are responsive images all about?
After the responsive layout, the responsive image is the logical next step in responsive web design (RWD). Having responsive images means serving up appropriate sized images based on a user’s device – which ultimately saves bandwidth, time and money. If you aren’t familiar with responsive images or where these savings are coming from: I suggest you check out my post Responsive Images: What’s that?
Popular frameworks such as Bootstrap and Foundation are big names in responsive layouts (they do a heck of a lot more than just that though) – however neither of them provide good solutions for responsive images. Even if Bootstrap and Foundation are too mainstream for you, I’m not aware of any other prototyping/grid system that handles responsive images. If you know of one, I’d love to hear about it… you hipster.
Picturefill in a nutshell
The super smart folks at the filament group have developed a polyfill called Picturefill which is publicly available. Thanks to this tool, developers can reference multiple sizes of an image in the HTML, but specify at what breakpoint a user should be served up the smaller or larger version of the image. So far, this is all possible with a little CSS; however the real advantage of Picturefill.js it that bandwidth isn’t wasted on downloading larger or smaller versions of the image that ultimately aren’t going to be used. Only the images you want are downloaded and the rest are waiting to be swapped in if necessary.
As an added bonus, Picturefill can detect whether the user is on a device with a high pixel-density (often referred to as a retina display for 2X displays) and have the higher pixel-density image show when possible. This is something that I am especially excited about. When I got my Mac Book I had no idea that jumping into the brand new world of retina displays also meant getting a blurrier version of the Internet. However, the ability to serve up high pixel-density images is a double-edged sword – but you can learn more about my musings on that later.
This is getting a bit theoretical, so here’s a GIF to paint a thousand words.
As you can see, different images are being called at different break points. This is still possible with CSS (check out my Bad News Bear CodePen for a demonstration). But with just CSS, every one of those 3 images would have been loaded. Not the case with Picturefill. Images will only load when needed – which results in valuable savings in bandwidth and page load time. Want proof?
I’ve had the chance to play around with picturefill.js and the first thing I found out is that there isn’t just one way you could implement this solution. Looking at the examples given on GitHub, you’re going be choosing between two methods:
- Using the familiar <img> element and then the less familiar attributes “srcset” and “sizes”
- Using the <picture> element along with ‘source’, ‘srcset’, ‘media’ and another for good measure.
A little caveat: Picturefill.js is an open-source solution and there has been multiple iterations and it continues to be developed. It looks like picturefill.js has become fairly stable, but there’s always the possibility that it’s implementation and mark-up will have changed from the time of me writing about it to the time you read this.
Using <img> element method
I’ve already said that the <picture> element method is superior, but it’s worth getting started with the simpler <img> element method before moving to the big leagues…
Let’s get into some code. Take a look at my CodePen “Responsive Bears” which uses picturefill.js and wraps the responsive image solution in a familiar <img> element. Start changing the browser size and watch the fireworks… or watch this GIF in action.
Since the release of Chrome 38, this CodePen demonstration doesn’t work as intended in an up-to-date Chrome browser. Chrome 38 now supports <picture> and <srcset> which is great, but as a result my use of the sizes attribute in this Pen is no longer appropriate for that browser. The Pen still works as intended in Firefox if you want to check it out.October 12, 2014
I’ve embedded the Pen below for easy reference to the code.
Break it down
The basic structure of this method goes:
<img src="fallback url” srcset=“url image-size, url image-size, repeat” sizes=“when/how to show the images from the srcset” alt=“description of image content or functionality” />
The src attribute is your fallback. For browsers that don’t support the srcset (pronounced: source set) attribute, this is the image that is going to be displayed. In my CodePen, I put an image that tells the user that their browser doesn’t support srcset. However, in reality (a.k.a. IRL), you are going to put one of the images from your source set. Its best practice to put the smallest version of the image as to not serve an extra large photo to a mobile device.
This is where the magic happens. srcset is effectively an array where each array element is going to have two values separated by a space. The first value is the url of the image and the second value is the image’s natural size in pixels.
So, let’s say you have three images. One for desktop, one for tablets, and one for mobile. We’ll want the desktop image to display on devices wider than 800px, the tablet image to display on images between 500px and 800px, and the mobile image to display on any devices smaller than 500px. We’ll need to have images appropriate for each size that we can reference in our
srcset attribute. Here’s a table to get us started.
|URL||Display at||Image width needed|
|desktop.jpg||greater than 800px||???|
|tablet.jpg||800px or less||800px|
|mobile.jpg||500px or less||800px|
Our first snag: how large should out largest image be? If you are going to be working with a “wrapper element” or a “container” for your website, then that’s going to be a safe bet for your maximum image size. If you are going to have images that will span the entire page, then it is less clear. I would recommend having a 1366px-width image as the 1366×768 browser displays have become more common. We’re getting ahead of ourselves since our table isn’t complete. The “image sizes needed” should account for images being displayed on a retina screen.
Why do we need more image sizes for retina displays? It’s outside the scope of this tutorial, but retina displays have a different pixel density than “older” screens. Even though retina displays are referred to as 2X, they are actually four times more densely packed with pixels. We only need to think in terms of 2X though because we are only concerned with the image widths.
Our table of required images should now look something more like this:
|URL||Browser size||Image width needed|
|desktop.jpg||greater than 800px||1336px|
|desktop@2X.jpg||greater than 800px||2672px|
|tablet.jpg||800px or less||800px|
|tablet@2X.jpg||800px or less||1600px|
|mobile.jpg||500px or less||500px|
|mobile@2X.jpg||500px or less||1000px|
So now we have six different image sizes ready to be swapped in and out when needed. Is this overkill? Possibly. We could always have the 1000px-width image display for both retina mobile devices and standard tablets. This is something best left to personal preference and comfort with juggling a lot of different image files.
In our code, our
srcset attribute would look like this:
srcset="desktop.jpg 1366w, desktop@2X.jpg 2672w, tablet.jpg 800w, tablet@2X.jpg 1600w, mobile.jpg 500w, email@example.com 1000w"
The units we’ll be using to express width is ‘w’, not px. This is the expression of how many pixels wide these images are when we chose “save for web” in our image making software.
sizes attribute is where you tell the browser how much space you need for this image. If your image is always going to be 100% the width of your browser screen then this attribute will be super simple to write!
The unit ‘vw’ stands for “viewport width”. If your image is always going to be 50% of the viewport, you can write
sizes="50vw". However, it’s more likely that the size of image you will need is going to be influx. With a responsive design, you’ve declared 2, 3 or 4 other breakpoints and at each breakpoint the placeholder for your image element has been changing in size. Luckily, just like the
srcset attribute, the
sizes attribute is an array and will hold all the different scenarios we can throw at it.
I am going to recommend you check out this demonstration from another developer on CodePen to see it in action.
In the above example, the
sizes attribute was written as:
sizes="(min-width: 900px) 33vw,(min-width: 600px) 50vw, 100vw"
What this is saying is “when the browser is at minimum 900 pixels wide, show me an image that is 33% the width of the viewport”. The magic of srcset is that even though 33% is a relative amount, the image with the best fit will be chosen to be swapped in. As long as you correctly identified the sizes of the images back in
srcset it will all work out.
It may seem a little complicated at first, but this method is going to be your preferred method for resolution switching. What does “resolution switching” mean? It is what you are doing when you show the same image, but at different sizes. You aren’t doing any sort of photo cropping – the image has all the same content from one image to the next, just the dimensions are different. To clarify: these GIF examples with the bears is not an example of resolution switching since the actual photo is changing. I just chose to use different photos to make it apparent the images was being replaced.
In my own demo with the bears, I expressed the sizes attribute in super simple terms: sizes=”(-webkit-min-device-pixel-ratio: 2) 50%, 100%”. However, thanks to the Mozilla Developer Network’s write up on resolution I’ve since learned that a better way would be to use the following.
sizes="(min-resolution: 2dppx) 50%, 100%"
The CSS mark-up ‘min-resolution’ is essentially asking: “Is the device I’m on have a physical pixel density of 2.0 or higher?” If yes, then I am going to tell picturefill.js that those breakpoints I stated are only half as big as they need to be when the browser goes to render the pixels. This works with picturefill.js, but this simple approach will not work for browsers that support “srcset” and the gang naitvely (i.e. Chrome 38).
This is a descriptor used for accessibility purposes and and it is best practice to fill this out to help convey either the functionality of the image, e.g. “Link to return to home screen” or more complex information e.g. “graph illustrates steady rise of ocean desalination over the past 5 years”. For a better illustration of how to effectively use the alt-attribute, I recommend you check out WebAIM’s Alt text blunders piece.
So there you have it! That’s how you use picturefill.js along with the simple <img> tag and a couple new friends. As mentioned numerous times, Chrome 38 has adopted the attributes needed to run this solution for responsive images natively. Picturefill.js can be run in other browsers, but using the polyfill has one major unfortunate side effect…
The biggest issue we encounter with the <img> element method in other browsers than Chrome 38 is that the first image in the srcset is loaded and then quickly replaced. It really doesn’t matter how quick the replacement is done, the fact that it was downloaded at all means there was unnecessary bandwidth used. It can be assumed that in time this will no longer be an issue as this responsive images solution is adopted by more browsers.
A real big thanks goes out to Jason Grigsby who helped me understand this issue and convinced me that the <img> method really is teh prefered method and that you should use the <picture> element in only rare cases..
Using <picture> element method
The <picture> method is by far easier to understand than the <img> method (at least in my opinion). However, it’s use is best suited for what’s called “Art Direction” and is far less common on the Internet (for now, at least). Art direction is when responsive images are used to show completely different images at different breakpoints. An easy way to picture this: A landscape shot on your desktop looks great, but when it’s shrunk way down on an iPhone it doesn’t look like much of anything. It would be perhaps more impactful to zoom in on a specific element of the image on an iPhone, or show a different image all together. I recommend this quick article on Imagefly for a simple explanation of resolution switching versus art direction in responsive images.
Below is my CodePen, “Picturefill with picture elements”, which you saw earlier in this post’s first GIFs.
And here’s that GIF again…
Breaking it down, now
The basic structure of this method is going to be as follows:
<picture> <!--[IE 9 fix]> <source srcset=“url_1a, url_1b” media=“breakpoint_1”> <source srcset=“url_2a, url_2b” media=“breakpoint_2”> repeat… <[End IE fix]--> <img srcset=“url_Xa, urlXb” alt=“description of content or functionality” /> </picture>
So right off the bat we more code to think about than in the earlier <img> element method. There’s some new faces which include an Internet Explorer fix/hack, the repeated use of the tag, and the media attribute.
Our new friend the srcset attribute is back, however, because the Internet is a playground, it is being used in a very different way in this method than the previous one. Let’s skip the IE fix for now and get right into the meat.
If you have ever used the <video> or <audio> elements, there are components of the <picture> element that will seem familiar to you. Namely, the <source> tag. w3schools provided the source tag so that browsers would have alternative media resources from which to choose when attempting to display multimedia. When the source attribute was defined, however, it was done so with only <video> and <audio> in mind – hence the IE fix that wraps a <video> tag around the entire solution so that the method will work in Internet Explorer 9.
It’s in the <source> tags where we will define our different image paths and set the breakpoints of when to use them.
In the earlier method we used srcset to define both the image url and the breakpoint. In this method, however, the srcset will be used to define multiple urls for different pixel densities. By adding 2x after the image url, you are telling picturefill to use this particular image if the device’s pixel density is 2.0 or higher. And that’s where there is a lot of power in this use of srcset. You could decide to put any number of urls in this space for a variety of pixel densities 3x, 2.2x, 0.5x… through experimentation I’ve learned that picturefill will take the url that is the the closet match before intruding into the pixel density defined for another url in the same srcset. Very similar behaviour to minimum-width breakpoints.
It is also important to note that providing different pixel densities is optional. You could provide just one image url for each <source> tag, typically a @1X version, and the method will still work. There are pros and cons to doing so, but those will be discussed below in my obligatory musings.
The media attribute is an HTML5 attribute used to perform a media query. Which means that the @media queries you have become familiar with in your responsive web design CSS files will work the same here. Unlike the earlier method, the order of the breakpoints does matter. Picturefill will take the first <source> that matches the user’s device, therefore:
If writing sources from large to small images size, use min-width to express the minimum device width required.
If writing from small to large image sizes, use max-width to express the maximum device width permitted.
Unlike the earlier <img> method, there’s no need to query device pixel density because that is what 2x, 1x, 3x, etc. is used for in our srcset array. Additionally, there’s no need for a sizes attribute to perform a translation for different physical pixel densities. So that’s all quite cool.
<img>, [srcset], & [alt]
This line is your fallback for situations where an appropriate image could not be found based on the <source> tags you provided – which is why our fallback is now being written last instead of first. Even though the alt-attribute is being written with the fallback <img> tag, it is going to be used for any and all images rendered on the page.
As always: Be a champion of the web and fill out your alt text descriptions when a user with a screen reader would benefit from knowing an image’s functionality or to communicate important, complex information.
First and foremost, this method stops unnecessary files from being downloaded to the browser. Check it.
The fallback image download was canceled and the appropriate image was loaded in its place. So right away this method is going to reliably save users bandwidth and load time. Everybody wins.
Planning for the future
The <img> element method and the <picture> element method are both great, but retrofitting your site with responsive images is a daunting task. Even updating your current responsive your code for higher-density devices, such as the iPhone 6’s 3x display, also sounds intimidating.
One way to make implementing responsive images easier on yourself is to use a CMS for your sites. Going to your loops/blocks/whatevers in your CMS and adding to the srcset array, or adding another image url and a 3x declaration, and then regenerating all your images is a much easier undertaking. I have posts in the work on how to implement this technique with WordPress and later Shopify – just need to make the time, dudes!