My interest in responsive images began when I was reading through Medium.com's source, as some nerds are wont to do when bored. Despite the fact that they use font-smoothing: antialiased; ( read this ) and text-rendering: optimizeLegibility; ( a performance pitfall on mobile ; only use for headers), I kept poking around 5 .

Responsive design doesn't just stop at adjusting layout. It also applies to dealing with images. Up until now most web developers would addto their images and call it a day (so-called "fluid/flexible images"). But now with more devices with higher PPIs in use, we can do better.

There's a lot more to cover with HiDPI image assets, but I'd rather point you to Thomas Fuchs' excellent Retinafy ebook than trying to summarize it here.

And of course, we can make a HiDPI favicon.ico. Create both a 16x16 and a 32x32 png and import the into x-icon editor . It will let you export a single .ico with all size variants baked in.

As you read in Developing a responsive, Retina-friendly site (Part 1) , the side effect of using Sass @media bubbling and @content blocks like this retina mixin is you get compiled CSS with lots of extra media queries. The snippet above will end up compiling to five of the same retina media query. I'll leave it as an exercise to the reader about how to rewrite it to save bandwidth.

That's it! HiDPI devices will load the @2x sprite and accordingly set the proper background-position to load the right icon. Regular devices will load the 1x sprite and respective icons.

Finally, here's how this all ties together to display some of the icons in my header:

@mixin retina-sprite { background-image: url($sprite_url_2x); @include bg-size(250px, 225px); // my sprite dimensions, halved } // silly function to divide the width and height to save me 2 seconds // whenever I need to calculate the halved retina background position @function half-pos($w, $h) { @return $w/2 $h/2; }

Then I created a two Sass variables to keep track of the sprite URLs so I didn't have to keep pasting that around for every icon.

And now for some Sass mixins of course.. here's a simple one to just identify high-DPI devices. You'll see it's similar to the image-2x() mixin from earlier but doesn't concern itself with the background image.

After saving and optimizing the sprites, it came time for the chore of writing the CSS and identifying all the icon coordinates. Fortunately a little website called Sprite Cow makes this a bit easier than going around measuring things in Photoshop. Just upload the sprite, select each icon (no need to be accurate, it auto-selects) and it outputs the CSS.

I do it a bit differently from Twitter, they always load the @2x sprite. If you poke around on Twitter's mobile website you'll see there are different sprites for mobile and tablet. The main benefit of this approach is that every icon uses the exact same coordinates!

So back to Photoshop to create two separate sprites : a 2x version and a 1x version. Working with sprites is usually a pain in the ass and working with two would prove to be a nightmare if you don't have a system for keeping it organized. Fortunately, Twitter Spritemaster™ Dave Bedingfield shared a few tips with me:

At least that's what I thought. Even with the correct background-size halving and only using pixel-fitted 2x icons were sized in even numbers (ie 48px by 48px should result in a 24px x 24px 1x icon), it still resulted in blurry icons. I could seemingly get around this by adjusting the background-size 1px smaller than it should need, but that felt wrong and that would make some icons crisp and turn others blurry. 4

I'm not actually using any media queries here to conditionally load a different sprite. I'm always loading the double-sized icon sprite. I wouldn't normally do this but the icons were small enough for bandwidth to not be a concern and the HiDPI version looked good enough on non-HiDPI displays.

// Instead of saving multiple versions of the icon for different states, // I like just having a darker icon and restoring opacity on hover opacity: 0.4; &:hover { opacity: 1; }

But I already know how to make PNG sprites so I went that route. Here's how I hooked up two of them:

However, if you are really interested in getting icon fonts working and don't mind the labor of hinting them for the sizes you'll be using them at, then you can get some pretty impressive results like this . 3

While an icon webfont would be retina-friendly right out of the gate, browsers render fonts differently and it may not look the same everywhere. Notably, icons from webfonts may appear blurry or have color fringing. And if you wanted a gradient on that icon (but why?!), you will need to resort to the typical gradient() , background-clip:text; , and text-fill-color: transparent; mayhem. 2

That mixin only works for individual images so if you have more than a few items to show you'll probably want to avoid that many HTTP requests. For example, the 18 icons I use throughout the site. I could have created my own icon webfont with a nice tool like Font Custom , but decided against it for a few reasons.

That's where media queries come in again. Below is a Sass mixin to do just that, based on this post by 37signals , but modified to remove min--moz-device-pixel-ratio in favor of resolution as recommended by Mozilla. The W3C folks say the same thing about -webkit-device-pixel-ratio .

I wanted icons and my large header avatar to be HiDPI display friendly. Load a background image that is twice as large as you want to display it and use background-size to constrain it to half of the image's width and height. Easy. But we don't want to load the HiDPI image all the time. There's no need to waste bandwidth for regular displays. 1

From here on out I refer to all "retina" displays & related ilk with the device-agnostic HiDPI moniker. Oh and a fun tidbit: the iPhone 4/5 is no longer the highest DPI mobile device. The HTC One's 1920x1080 4.7-inch display gives it a lusty 468ppi.

So you want to do the easiest possible thing to get your website's header logo to look good on HiDPI devices and computers? Doing this in CSS is similar to loading antwice as large as the height and width attributes of that tag.

This is the last part in my three-part series about the design and development of a responsive and so-called Retina-friendly website. While I have covered my development process, media queries for responsive development, and various JS libraries I use, I'm going to talk about images in much more depth in this article. In particular, I'll show how to use HiDPI image assets as well as cover responsive images and some history around that.

I noticed their header image had a unique URL that changed when I resized my browser and reloaded the page. Looking at the image response headers showed nginx as the server and a Via of Amazon Cloudfront. Looks like they're using nginx's image filter module to do the on-the-fly resizing and utilizing it as an origin for Cloudfront, but I digress.

Why should mobile visitors still waste bandwidth loading the same 1000px image you display for desktop visitors? What about desktop visitors tethered to a slow connection? They shouldn't have to wait for the 2x HiDPI version of that large image to load. As you can imagine there are other edge cases mixed in here too. We should loading HiDPI assets for mobile devices that support it and everything in between (depending on the breakpoints) to provide properly sized images and forgoing the old manner of simply downsizing images with CSS.

Technically this is simply adaptive images and that's what you'll find most often when researching this — providing several versions (for example 4 sizes) of the same image and then having the browser select the proper one to load depending on conditions like media query breakpoints6. A true responsive images solution would serve up the exact size needed with the help of server-side image resizing and caching. More on that later.

I want photos on my photos pages to be crisp all the time — something that should be possible given the user's bandwidth, browser width/screen resolution and device pixel ratio. If we know they are on a slow connection, we can load a lower quality version.

Brief History of Responsive Images

<img>

So I read pretty much everything the Internet has to offer about responsive images, including W3C drafts W3C respimg Community Group discussions et cetera. There are several ways to get it working right now but there is. Thetag wasn't built with this in mind.

It all started with folks in 2010 using HTML5 data attributes like data-fullsrc in <img> tags to provide an alternate, high-resolution version of the same image. Some JavaScript would loop through the images and swap in the fullsrc for src if certain conditions like browser width were met. However, clients destined to load the larger image ended up making a request for the first image before the JavaScript kicked in, resulting in additional HTTP requests and bandwidth usage. To counteract this people started using server-side rewrite rules to catch issues like that and load tiny spacer gifs instead so there wouldn't be much of a penalty for the first request. For that to work, it depended on a cookie checked by the server with the client's resolution. It was a mess, but it worked.

<img src="mobile-version.jpeg" data-fullsrc="desktop-version.jpeg">

Then browser advancements introduced the lookahead pre-parser that helped browsers load pages more quickly by fetching all page assets before the HTML finished loading and before JavaScript can run. The browser's image prefetching would break that aforementioned server-side redirect created to reduce the penalties associated with making two requests.

There-in lies the entire problem with getting responsive images to work today:

How do we reconcile a pre-parser that wants to know what size image to download ahead of time with an image technique that wants to respond to its environment once the page layout has been calculated? Jason Grigsby

Where are we now?

img

noscript

Based on those trials and tribulations, developers started coming up with other ways to solve this with thetag. Some folks thought about not even setting a src so there was no prefetching penalty, having JavaScript load in the appropriate image from data attributes, and setting another image tag inside aas a fallback for visitors with JavaScript disabled.

Unfortunately that has SEO implications on its own (no src), can cause ugly page reflowing as the images begin loading, and results in a slower page load from lack of prefetching. Matt Cutts has said that Google tends not to look at content inside of noscript tags due to noscript abuse from spammers.

Then developers started thinking, well what if we extend img to support a sourcelist of source s to choose from? It would be more semantic and utilize media queries to determine the version of the image to load instead of depending on JavaScript.

<img src="default.jpg" srclist="alternativeSizes" alt="Picture of Unicorn"> <sourcelist id="alternativeSizes"> <source src="big.jpg" media="min-width: 600px" width="600" height="400"> <source src="small.jpg" media="max-width: 600px" width="320" height="320"> </sourcelist>

That seems to have evolved into the W3C-proposed srcset attribute for img :

<img src="banner.jpeg" srcset="banner-HD.jpeg 2x, banner-phone.jpeg 100w, banner-phone-HD.jpeg 100w 2x" alt="">

The srcset attribute contains up to three optional URLs and accompanying descriptors for different versions of the image to load. In this example, the 2x descriptor denotes a "maximum pixel density of 2 device pixels per CSS pixel" and 100w means "maximum viewport width of 100 CSS pixels."

Another suggestion around this time was creating a new image format entirely; one that points to alternative resolutions. Googler Ilya Grigorik recently wrote about this kind of solution (content negotiation) at length. It turns out this is also something the W3C seems to be okay with in their recently updated Use Cases and Requirements for Standardizing Responsive Images draft:

The solution MUST NOT require server-side processing to work. However, if required, server-side adaptation can still occur through content negotiation or similar techniques

If all this wasn't confusing enough, Apple decided that people were having too much fun without them and decided to put their hat in the ring with a proposal for CSS4 image-set . It's pretty much the same concept as srcset but in CSS. The problem there is that folks will just start converting their img tags to div s utilizing image-set for background images in CSS, thereby removing alt text ( div s can't have alt text) and throwing all accessibility out the window.

<picture> the future of responsive images

picture

srcset

source

<picture width="500" height="500"> <source media="(min-width: 45em)" srcset="large-1.jpg 1x, large-2.jpg 2x"> <source media="(min-width: 18em)" srcset="med-1.jpg 1x, med-2.jpg 2x"> <source srcset="small-1.jpg 1x, small-2.jpg 2x"> <!-- img is the fallback --> <img src="small-1.jpg" alt=""> <p>Accessible text</p> </picture>

It seems like developers on the W3C and W3C Responsive Images Community Group are leaning towards the proposedelement. In a W3C Working Draft for the picture element , syntax utilizes a simplifiedtag restricted to only carrying information about images for devices with different pixel densities per

This seems to be the current state of responsive images. I probably got the order of some of these thing wrong, so don't hold me to it exactly. The picture element looks promising but has a few limitations in my eyes:

It's rather verbose and will be rather confusing for new developers.

Media queries have no sense for bandwidth available to the browser so this will hypothetically still load a large 2x image on a HiDPI-capable device even if it's on a slow connection.

Media queries can bring some interesting affordances including device pixel ratios and the upcoming luminosity media feature, but bandwidth is not one of them. Aside from a manual bandwidth test, the only hope is either waiting for browsers to get smarter about picking the right image source given what it knows about the available bandwidth or letting sites access something like navigator.connection7.

I'm using foresight.js for now

picture

Foresight.js gives webpages the ability to tell if the user's device is capable of viewing high-resolution images (such as the 3rd generation iPad) before the image has been requested from the server. Additionally, it runs a bandwidth test to see if the user's device should load high-resolution images. Depending on the display and network connectivity, foresight will request the appropriate image for the webpage.

While I'm waiting forto get implemented in all major browsers, I decided to use one of the more popular responsive image solutions:

Foresight can be configured for a number of different responsive image setups ranging from simply swapping out an image with a higher resolution version all the way to requesting an image the exact width of the client's browser. I went with the latter.

Here's the markup for the image itself. Notice how the img tag doesn't have a src specified. It gets added after foresight evaluates the environment and decides what image version to load. (The curly braces are just my Jekyll liquid tags.)

<figure class="feature-image loading"> <img class="fs-img" data-src="{{site.cdn}}{{page.image_lg}}?w=1400" data-width="{{page.photo_width}}" data-height="{{page.photo_height}}" alt="{{page.title}}" /> <figcaption>Example caption goes here</figcaption> <noscript> <img src="{{site.cdn}}{{page.image_lg}}?w=1400" alt="{{page.title}}"/> </noscript> </figure>

Now here's where it gets a little hacky. All foresight images are assigned a CSS class that uses font-family to store an image-set value. As Foresight developer Adam Bradley states, "font-family is one of the few CSS properties which lets you enter free-text in the value and the browsers still consider it valid, which makes the value viewable by the DOM and javascript (yup, even IE doesn't care)."

.fs-img { max-width: 1400px; font-family: 'image-set( url(w=1400|w={requestWidth}) )'; display: none; }

The value I have for image-set() is a little cryptic on its own too. It makes use of foresight's URI Find/Replace functionality. It looks for the string "w=1400" in my image data-src and replaces that with the actual width of the client (this also takes into account HiDPI resolutions).

But what is that query string param in my image URL? Well here's the other hacky part — I'm using a service to on-the-fly resize and cache the image for exactly the request. For example, if your browser is 837px wide, it will change that width param in the URL to 837. If you're on a Retina MacBook Pro, that request will be for a 1674px wide image. I just dump a fairly large image on the service and it does the rest for me. And if I make my browser wider, foresight will automatically request a larger version of the image to swap in!9

On the photos index page, I use foresight differently and only have it swap in 2x high-res images for HiDPI devices. I'll discuss the query string parameters at the end of all those image urls below.

{% for photo in site.photos offset: 1 %} <li> <figure class="in-photo-list"> <a class="photo_item" href="{{site.base_url}}{{photo.url}}" title="{{photo.title}}"> <!-- the fs-thumb class is simply display: none; --> <img alt="{{photo.title}}" class="fs-thumb" data-high-resolution-src="{{site.cdn}}{{photo.image_lg}}?w=333&h=333&fit=crop&q=75&dpr=2" data-src="{{site.cdn}}{{photo.image_lg}}?w=333&h=333&fit=crop&q=75" data-width="333" data-height="333" /> <noscript> <img src="{{site.cdn}}{{photo.image_lg}}?w=333&h=333&fit=crop&q=75" alt="{{photo.title}}"/> </noscript> <figcaption>{{ photo.title }}</figcaption> </a> </figure> </li> {% endfor %}

I do feel a bit guilty about this solution, especially as having any server-side processing is one of the no no's mentioned in the W3C picture proposal. It's just introducing another point of failure. It works pretty well right now and it's not like I can use picture yet.

I'm not using it in blog post articles just yet. As an FYI, if you do decide to use a similar solution for your jekyll posts, you'll need to write a filter for your RSS feed otherwise the images won't load for RSS readers since there's no src and no foresight JavaScript to run.

On-the-fly image resizing and caching with imgix

After I poked around and found out how Medium started doing some of this, I started brainstorming about how I could do it. A number of solutions surfaced from the same nginx image filter setup with Cloudfront to something utilizing the dragonfly gem but at the end of the day I don't want to manage yet another thing.

I'm using imgix, a service from a fellow Y Combinator batchmate. Setup was ridiculously easy. I just had to create AWS IAM user credentials with read-only access for the Amazon S3 bucket where I store my images. Something like this:

{ "Statement": [ { "Effect": "Allow", "Action": [ "s3:GetBucketAcl", "s3:GetObject", "s3:GetObjectAcl", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::YOURBUCKETNAMEHERE", "arn:aws:s3:::YOURBUCKETNAMEHERE/*" ] } ] }

I gave that and its accompanying AWS access key and secret to imgix. Then all I had to do was replace the host for my images to my newly created stammy.imgix.net . The rest of the path remains the same.

# old URL, with Cloudfront CNAME https://turbo.paulstamatiou.com/uploads/2012/12/santa_cruz_beach.jpg new imgix url http://stammy.imgix.net/uploads/2012/12/santa_cruz_beach.jpg

With imgix setup, I can now easily manipulate the image by adding various query string params for their image URL API like w for width, q for quality, dpr for device pixel ratio and so on.

Speeding up my photo posting flow

data-width

data-height

One asterisk when it comes to using foresight is that it needs to know the image aspect ratio for each photo so you must provide it withandattributes in the markup. This can be rather annoying to manually add every time I post a new photograph with Jekyll. I was already manually adding several other attributes for my photo layout, like camera type and post processing software used.

I decided to write up a quick rake task to automate the creation of each new photo post — upload the image to S3, grab any relevant EXIF data, fetch an address if the EXIF has GPS coordinates and finally create the file and open it up in Sublime Text 2 for me to begin writing.10

# usage: rake s3p title="My Photo" slug="my-photo-seo-keywords" desc 'upload photo in _new_photo dir and create new photo post with image URL as image_lg attribute' task :s3p do require 'exifr' require 'open-uri' require 'json' photo_dir = "/Users/Stammy/Documents/_new_photo/" month = Time.new.strftime("%m") year = Time.new.strftime("%Y") upload image(s) to S3 via http://s3tools.org/s3cmd sh "s3cmd put --acl-public --guess-mime-type #{photo_dir}* s3://my-s3-cloudfront-bucket/uploads/#{year}/#{month}/" Dir.chdir(photo_dir) glob goes by alpha images = Dir.glob("*.{jpg}") in case I'm uploading multiple images for a single photo post, only pull exif from first main_img = images.first if EXIFR::JPEG.new(main_img).exif? # used to set data-width/data-height as needed for foresight.js exif = EXIFR::JPEG.new(main_img) camera_model = exif.model.gsub('EOS ','') photo_w = exif.width photo_h = exif.height is_portrait = photo_h > photo_w ? true : false is_mobile = exif.make == "Apple" ? true : false photo_e = exif.exposure_time.to_s photo_f = exif.f_number.to_f photo_mm = exif.focal_length.to_i photo_iso = exif.iso_speed_ratings.to_s photo_pp = exif.software.gsub(" (Macintosh)",'').gsub('Photoshop ','') if !exif.gps.nil? # turn gps coords into address coords = exif.gps.latitude.to_s + "," + exif.gps.longitude.to_s url = "http://maps.google.com/maps/api/geocode/json?latlng=#{coords}&sensor=false" photo_gps = JSON.parse(open(url).read)['results'][0]['formatted_address'] else photo_gps = false end end post_process = photo_pp || "Adobe Lightroom 4" photo_url = "uploads/#{year}/#{month}/#{main_img}" and create the photo file title = ENV["title"] || "New Title" tstamp = Time.new.to_s slug = ENV["slug"].gsub(' ','-').downcase || title.gsub(' ','-').downcase Dir.chdir("/Users/Stammy/code/pstam-blog/") path = File.join('_photos', "#{Time.new.strftime('%Y-%m-%d')}-#{slug}.markdown") data = <<-HTML layout: photo type: photo title: "#{title}" date: #{tstamp} description: "" image_lg: "#{photo_url}" photo_width: "#{photo_w}" photo_height: "#{photo_h}" camera: "#{camera_model}" photo_exposure: "#{photo_e}" photo_iso: "#{photo_iso}" photo_mm: "#{photo_mm}" photo_focal: "#{photo_f}" photo_gps: "#{photo_gps}" post: "#{post_process}" mobile: #{is_mobile} portrait: #{is_portrait} HTML File.open(path, 'w') { |f| f.puts data } puts "new file generated in #{path}" system "sublime #{path}" end

Photo page with extra metadata shown on hover – location, aperture, exposure, ISO, etc

What's next with images?

As I mentioned above, I'm only using responsive images on my photos pages for now. I'd rather not end up with hundreds of blog posts to change when a particular solution to responsive images becomes a standard. That being said, I do use focal-point CSS in some of my blog posts and pages. Focal Point lets you add classes to your images that crop the image when triggered by a media query breakpoint.

This is an attempt to address "art direction" outlined by the W3C in responsive images:

In a responsive design, it is typical to change an image so it can be targeted towards the features of a particular display (or set of displays). Sometimes this means cropping an image.

If the full size of that image was simply scaled down on mobile, Bo would be tiny. Art direction suggests smart cropping of the subject for various devices.

It's just CSS that hides part of the image so that as it gets smaller, it crops in on the area you specify as being the focal point (get it?) of the image. One side effect of this particular implementation is that it hides captions below the image when the media query gets triggered. Focal point is only an aesthetic solution, a true one would address bandwidth concerns as well and load a different, cropped image for smaller displays. Now you can see much more there is to go with this whole "responsive" thing huh? It's just getting interesting.

Let me know what you think!

Comments and feedback keep me going and writing articles like this. Drop me a comment below,if you enjoyed it and shoot me a tweet . I'd love to hear from you.

Note: Be sure to check out part 1 of the development track of this series and the initial design post that started it all.