Responsive Images

A quick tutorial on how this site uses basic responsive images

OK, finally time for a technical blog post!

Some background: previous iterations of this site have used what I would call, "server-side responsive images."

  1. Images were requested via JavaScript, and the image URL included the screen dimensions as query string parameters (e.g. getImage?id=123&x=1280&y=720)
    • This JavaScript was also doing the math to optimize for "high-DPI"/"retina" displays
  2. The server would take the image ID and screen resolution, and find and return the highest quality image for it
    • If one didn't exist, it created it on the fly

I'll save you the details of the ImageRetriever and ImageCreator, but suffice to say they're pretty complex. One of the great things about open source, however, is if you actually want to, you can check them out!

Fast forward to open web standards

I only learned quite recently (maybe this past summer) about client-side responsive images, specifically the "srcset" and "sizes" attributes on an "img" tag. Basically, we're going to take all that complex logic that I had written in my server-side ImageRetriever class, and have it baked into the browser.

It took me a while to wrap my head around, using this 10 part guide as a reference, and I since then saw another presentation that wasn't as straightforward as I was hoping, so my goal here is to have a single-page "how-to" for client-side responsive images, using <img srcset="..." sizes="..." />.

So let's dive in!

Here's our image tag: this is the actual code used for my headshot up at the top of every page here.

Let's go through each of the attributes:

  • src: This is the backup/default, in case the user's browser doesn't support responsive images (IE11 and older, stock Android browser before 5.0) -- this is exactly what it would be if you weren't doing anything at all with responsive images
  • srcset: Here is our list of "alternative" images, and their widths (e.g. steve-0100.jpg is 100px wide, steve-0200.jpg is 200px wide, etc.) -- note that these are actual individual files/URLs
  • sizes: Here's where things get a little confusing...this is how big the image should be on various sized displays. Note that this does not actually set the size -- that's CSS' job -- this simply lets the browser make a smart decision about which file it should download.

Now let's look at several examples of what happens on different devices, and why:

  • My Desktop (1920x1200, non-retina)
    1. Check "sizes": are we 767px wide or less? No, so we can expect the image will be displayed 150px wide.
    2. Check "srcset": what's the best image for 150px wide? steve-0150.jpg
    3. There is no #3, it's that simple!
  • My MacBook Pro (1680x1050, "retina")
    1. Check "sizes": are we 767px wide or less? No, so we can expect the image will be displayed 150px wide.
    2. Check "srcset": what's the best image for 150px wide? steve-0150.jpg
    3. Hold up! We're on a "retina" display, which means 2 "physical pixels" per "CSS pixel." So we actually want an image that's 300px wide (150px x 2). Therefore, our best image is steve-0300.jpg
  • My Moto G (360x640, "retina")
    1. Check "sizes": are we 767px wide or less? Yes, so we can expect the image will be displayed 100px wide.
    2. Check "srcset": we're on a retina display, so what's the best image for 200px (100px x 2) wide? steve-0200.jpg
  • Me using Firefox Dev Tools to mimic my Moto G on my desktop (360x640, non-retina)
    1. Check "sizes": are we 767px wide or less? Yes, so we can expect the image will be displayed 100px wide.
    2. Check "srcset": what's the best image for 100px wide? steve-0100.jpg

Pretty straightforward, right?

A little more advanced

Same concept, just a little more going on here -- this is code from the Photography section of this site:

  1. We've got a whole lot more images available (which were all pre-rendered using ImageResizer) to ensure that:
    • Everyone is getting the highest quality image for their display
    • No one is using more bandwidth than they have to (e.g. downloading a huge image on a tiny screen)
  2. We've also got some new/funny stuff going on in the "sizes" attribute:
    • 100vw: if the screen is smaller than 768px, the image will take up the entire width of the screen (100% of the viewport width)
    • The middle two sizes match Bootstrap's small and medium breakpoints, and their expected image sizes account for container padding
    • If all else fails (we're on a large screen) use Bootstrap's large container width, minus padding

Wrapping it up

Obviously some of these values are pretty specific for my site, but hopefully you get the point:

  1. The sizes attribute helps the browser to determine how large the image will be displayed, based on the viewport
  2. The srcset attribute contains all the images and their respective widths
  3. The browser combines the "size" result with the viewport information to determine which image from the "srcset" attribute to download

I hope this was helpful in being a more concise tutorial on responsive images than what else is out there.

I may, in a future post, get into the TagHelper that dynamically generates these img tags.

Thanks for making it this far -- I'd love any feedback you may have!