The past few weeks a lot has been going on around one of the challenges of responsive web design, namely responsive images. The gist of it is pretty straightforward: it’s not a good idea to let a device download a big image if that device is a small-screen device. It’s better to serve that device an image that (approximately) fits its screen.
As it turns out we cannot use the current tools/tags we have to solve this problem, we are in need of a proper solution. Ergo, a community group was brought to life to wrap their heads around the problem.
A current proposal that is getting quite the attention is to introduce a
<picture> element with some various
<source> children, this inspired upon how
<audio> are coded. Key difference though is that you explicitly define the minimum (or maximum) required width along with it.
<picture alt="Alt tag should accurately describe the image represented by all sources, though cropping and zooming may differ."> <source src="mobile.jpg" /> <!-- Matches by default. --> <source src="high-res.jpg" media="min-width: 800px" /> <!-- Overrides the previous source over 800px before any assets are fetched, resulting in a single request. --> <img src="mobile.jpg" /> <!-- Fallback content, in the event the <picture> tag is completely unsupported by the user's browser. --> </picture>
An other proposal that I quite dig is a new image format that acts as a container and can hold several images inside it. Think of it as a .zip with some images inside it (or if you’re familiarized with OS X: a .icns file). The implementation could be done in such a manner that the browser only downloads the wanted size based upon it’s screen size.
Inspection of an
.icns file, image by http://mistermorris.tumblr.com/
Faux Responsive Images
Until one of these ideas — or any other great idea that could possibly provide an answer — is actually implemented, there are some techniques that we can use right now to mimic responsive images, but they’re not all good (hence the need for the community group).
One that caught my eye though — because of its pure simplicity — is Responsive Enhance: Serve the browser a low-res image. If more screen estate is available, let it pull in a high(er) resolution image.
<img id="demo" src="http://dummyimage.com/400x300" alt="Responsive Image" data-fullsrc="http://dummyimage.com/800x600"> <script>responsiveEnhance(document.getElementById('demo'), 400);</script>
Jeremy has a nice explanation on why that’s an acceptable solution (for now) (emphasis mine):
Suppose the small image is 20K and the large image is 60K. That means that desktop browsers are now loading 80K of images (instead of 60). On the face of it, this sounds like really bad news for performance… but because that extra 60K is being downloaded after the page has downloaded, the perceived performance isn’t bad at all. In fact, the experience feels quite snappy.
Agreed, it’s not good to download something twice, yet the advantages (perceived speed) currently outweigh the disadvantages (downloading an image twice + the fact that this won’t work if JS is disabled).
When talking about responsive web design and mobile devices, one of the key aspects that was always pushed forward was context, which has become something vague to define: it’s not because a user is visiting your site via a mobile device that he/she is “on the road”. In the same line, one can also state that:
Small screens don’t necessarily mean slow connections or mobile. Larger screens don’t necessarily mean fast connections or desktop.
And this is the elephant. All the suggested solutions I’ve seen so far only take the screen estate into account, neglecting the connection speed the device has.
To me, a proper solution would also take the connection speed into account: if a device has the space to show a bigger image, and the connection can handle it: enhance.
A Possible Solution
As there’s already a
navigator.onLine property available (demo) I think it’d be fairly easy to extend
navigator with a
speed property. That property in its turn could then easily be mapped/expanded to become available from within media queries, or even an HTML attribute.
<picture> solution could evolve into something like this:
<picture alt="Alt tag should accurately describe the image represented by all sources, though cropping and zooming may differ."> <source src="mobile.jpg" /> <!-- Matches by default. --> <source src="high-res.jpg" media="(min-width: 800px) and (min-speed: 3G)" /> <!-- Overrides the previous source over 800px before any assets are fetched, if the the user has at least a 3G connection. --> <img src="mobile.jpg" /> <!-- Fallback content, in the event the <picture> tag is completely unsupported by the user's browser. --> </picture>
3G value for
min-speed in the example will need some tweaking though. If one would have a
speed with the value
wifi, that could actually mean nothing: he could easily be connected via WiFi to his iPhone which is on
3G. Better solutions could be values in the likes of
fast or actual rates
Does this extension look something to you? Or have we have no need to detect the connection speed of device? It’s clear that I myself think we do need it … so let’s start a discussion 😉
UPDATE #1: As Jan posted below it’s better to let the browsers handle downloading the best size, instead of putting the responsibility in the developer’s hands. If you combine this idea along with the container format (cfr. the
.icns file) then there’d be no actual need for the
<picture>element: just define an
img set the
src to the container image and the browser will cherry-pick the appropriate size based on the screen estate and the connection speed. The rules to decide which on the browser need to fetch will be tricky to define though (there will be some edge cases), but that’s a nut we can definitely crack.
UPDATE #2: Lennart Schoors (from briccs.net) hinted on Twitter that the connection speed is in some (draft of a) W3 spec. Mathias Bynens eventually pointed towards that spec, namely The Network Information API which has a
Connection interface which holds a
bandwidth property. As this can eventually become available, the inclusion in a media query or attribute isn’t that far away.
I think this is something the browser should handle, it can fire off a bunch of HEAD requests to get the actual size of the files, and determine what is acceptable to download, what should be good to fetch once the page is fully loaded, and what is too big for the current connection. Asking developers do determine this for every image is a pain in the ass: sometimes your High Resolution images might be small enough for 3G, so for every image you add you should consider at what speed it should be fetched.
PS: I prefer “bad, ok, good, perfect” over “3G, wifi” or “200kbp, 5kbp, …”. Kind of like Apple does with Location (kCLLocationAccuracyKilometer, kCLLocationAccuracyHundredMeters, kCLLocationAccuracyNearestTenMeters, kCLLocationAccuracyBest, kCLLocationAccuracyBestForNavigation)
I like the “picture” solution you propose. No extra JS,… Clean and simple. Let the browser (renderer,…) handle the type of image which needs to be retrieved. This type of behavior should be handled lower down the stack anyway. And if a solution like this would be a standard one day: all the better!
You’re right in thinking about the context. Screen estate is not the only factor in play here. Just like connection is only one of many factors. I think we shouldn’t be blinded by how fast a single image in a page can be rendered though.
The whole experience whether or not a page is loaded “snappy” isn’t determined entirely by the speed images are loaded. It depends how fast relevant parts of the page are readily available to the user. Sometimes its enough just to get the text of an article before anything else, while a tumblr blog filled with pictures is an entirely different story.
If one wants to determine which parameters should be controlable by a site builder, we need to make sure that those parameters are actually meaningful within the entire frame of browsing and loading a page with all its’ objects.
Speed and estate are two really great parameters. Others could be: “Show this only when within the viewport” or “Load this when just on the fringe of the viewport” (Just freewheeling here)
Leave a comment