Cloud Four Blog

Technical notes, War stories and anecdotes

The web’s lost opportunity to be on TV

Apple announced the new Apple TV yesterday. As many expected, it didn’t come with Safari. What was unexpected is that it doesn’t appear to have WebKit at all.

The utility of WebKit for app developers seems straightforward. Apps often use embedded web views to display information that it doesn’t make sense to duplicate in native code or for rendering links that people share.


But without webkit available for tvOS, there will be no embedded web views and no third party browsers.

We now have both Apple TV and Android TV without the web, and it’s a bloody shame.

I’m well aware of the argument that people don’t want to browse the web on TVs. I believe the jury is still out on that one, but even if I concede that point, there is still tremendous utility in using web technology for building apps for TVs.

The reason I started researching the web on TVs dates back to the original Google TV Showcase. There was a Vimeo app in that showcase that I loved.

Vimeo couch mode screenshot

I used that app for several months before I accidentally hit a button that converted it from the TV app that I knew into the standard Vimeo web page.

I had inadvertently discovered that the Vimeo app was just a different view of Vimeo’s normal web page. Vimeo calls this couch mode.

Nintendo, Samsung, LG, and others have all built app platforms on top of HTML. For years, Netflix built all of their TV apps on HTML5 before recently going native.

Web apps on TVs can be great experiences. Maybe it is because we don’t notice the lag as much when we’re using remote controls instead of touching the interface directly. Maybe it is because the interfaces for most non-game, TV apps are fairly simple.

Whatever the reason, building TV apps using web technology just seemed to work. Bridging the gap between native and web apps on TVs was easier than it was on mobile.

But year after year at Google I/O when I’d try to talk to people about Chrome on TVs, I wouldn’t get anywhere. Google TV shipped with Chrome, but it was a forked version that the Chrome team wasn’t responsible for and grumbled about.

I remember trying desperately to figure out who to talk to about the browser on Google TV. I was repeatedly and humorously pointed to Chris Wilson. Chris hadn’t working on Google TV in months. It became a running joke between the two of us.

I found this photo of Android TV on the Android TV site. The name of the file was lessbrowsing.jpg.

A couple years later, Google announced that their TV product would be called Android TV. Google touted how the TV would finally be running the same version of Android as phones and tablets. It would be kept up to date.

Except it would no longer have a browser.

At the time, Microsoft showed more interest in the web on TVs than Google. That may still be the case. I haven’t checked in awhile. Firefox OS has recently moved to TVs and Opera still has a TV browser. So all hope isn’t lost.

Back in 2012, I was trying to muster enthusiasm in browser makers for working on the web on TVs. It seemed likely that TVs were going to be the next platform and instead of playing catch up like the web did on phones, we could be ready for the web on TVs from the beginning.

I feared playing catch up again. In retrospect, I should have feared much worse.

The two biggest mobile operating systems are now on TVs. One started with a browser, but no longer has one. The other just shipped without even an embedded web view.

From what I’ve seen, the web on TV could have been a star. What a missed opportunity.

What I’m watching for in the new Apple TV

We’re about an hour away from the Apple event where they will announce the new Apple TV. Here are the things I’m going to be watching for based on my time researching Smart TVs, game consoles, and set top boxes.

How does the remote control work?

Input remains the biggest challenge for all attempts to bring computer smarts to the screens on our walls. While the software and content options for the new Apple TV will matter, if Apple truly revolutionizes TVs, I suspect if will come from an improvement in input.

Historically, improved input has accompanied Apple innovation. The Mac’s mouse. The iPod’s scroll wheel. The iPhone’s touch screen.

The other lesson here is that none of these inputs were wholly Apple inventions. In each case, the input technology had been used by other companies in the past. The iPhone’s touch screen seemed ho-hum until people actually used it and realized how much attention to detail Apple had put into perfecting the input.

So I’ll be surprised if the remote control has some feature that we haven’t seen on remote controls in the past, but I also suspect that if Apple TV is a game changer, it will be because of the remote control.

Is there a pointer? And where is it used?

The current Apple TV is limited to d-pad interactions—up, down, left, right. There are a lot of interactions that need the ability to select an arbitrary point on the screen instead of navigating to that point by successive d-pad button presses.

The most obvious need is in games. The rumors are strong that the new Apple TV will focus on games. The remote control has been described as Wii-like in its ability to detect motion.

The question is whether or not there will be any interfaces where you may see a pointer on the screen. I highly suspect app developers will build apps that include pointers, but do any of the Apple apps themselves include a pointer. And if so, where is it used and how does it work?

Where is web technology used? Is there a browser?

I have little doubt that the new TV operating system will support embedded web views. Web views are critical for many apps.

So the big question is whether or not Apple will include a browser as well. I’ve explored some of the arguments for and against a browser in the past.

And whatever other surprises Apple has in store.

I’ve been looking forward to today’s announcement since I started researching the web on TVs in 2012. I can’t wait to see what it looks like when Apple is no longer treating TVs as a hobby.

Responsive Images 101, Part 9: Image Breakpoints

I’ve dreaded writing this installment of the Responsive Images 101 series. Selecting image breakpoints is something everyone will face, and frankly, I have no good answers for you.

But sooner or later, we will all face the image breakpoints koan. We might as well start now.

What are responsive image breakpoints?

In our responsive layouts, breakpoints refer to the viewport sizes at which we make changes to the layout or functionality of a page. These typically map to media queries.

Responsive image breakpoints are similar, but slightly different. When I think about image breakpoints, I’m trying to answer two questions:

  • How many image sources do I need to provide to cover the continuum of sizes that this image will be used for?
  • Where and when should the various image sources be used?

The answers to these questions lead to different breakpoints than the criteria we use to select breakpoints for our responsive layouts. For our layouts, we follow Stephen Hay’s advanced methodology: We resize the browser until the page looks bad and then BOOOOM, we need a breakpoint.

With the exception of art direction, the main reason why we need multiple image sources has nothing to do with where the images look bad. We want to provide multiple image sources because of performance concerns, different screen densities, etc.

So we can’t simply reuse our responsive layout breakpoints for our images. Or I guess we can, but if we do so, we’re not really addressing the fundamental reasons why we wanted responsive images in the first place.

Image breakpoints for art direction is relatively easy

In situations where the image falls under the art direction use case, the art direction itself will often tell us how many image sources we need and when they should be used.

If you think back to the Nokia browser site example, we can tell when the image switches from landscape to portrait mode. When that switch occurs, we know we’re going to need a new source image.

However, this may only be part of the picture. What if one of the art directed images covers a large range of sizes. We may find that we still need to have multiple sources that don’t map to the art direction switch.

You can see an example of this in the Shopify homepage that we looked at in Part 8.

Shopify home page animated

Despite the fact that the image only has one major art direction change—from the full image to the cropped one—Shopify still provided six image sources to account for file size and display density.

  <source srcset="homepage-person@desktop.png, homepage-person@desktop-2x.png 2x"       
          media="(min-width: 990px)">
  <source srcset="homepage-person@tablet.png, homepage-person@tablet-2x.png 2x" 
          media="(min-width: 750px)">
  <img srcset="homepage-person@mobile.png, homepage-person@mobile-2x.png 2x" 
       alt="Shopify Merchant, Corrine Anestopoulos">

So knowing that an image falls under the art direction use case can give us some clues, but it doesn’t answer all of our questions about the necessary image breakpoints.

What about resolution switching breakpoints

This is where things really get tricky. At least art direction provides us with some hints about how many image sources might be needed.

So long as we’re downscaling flexible images, they will always look good. We can’t rely on them looking bad to tell us when we need to change image sources.

Let’s take a look at a resolution switching example:

Photo of Michelle Obama at three sizes

In this example, we have a photo of Michelle Obama where the image in the page is 400 x 602 pixels for the current viewport size. The largest size that the image is ever displayed at is 2000 x 3010 pixels. That large file is 250K.

We can simply shrink that 2000-pixel image, and it will look good. But it would be unnecessarily large. It would be better if we provided a smaller version like the one 800 x 1204 resolution image that is shown in the example. That image is only 73K.

We can all agree that when the image in the page is only 400 x 602 pixels in size, that providing an image that is 800×1204 and 73K is better than having people download the largest version of the image.

But why stop at 800×1204?

Michelle Obama example with a fourth size at 600px wide

If we provided another image source that was 600×903 pixels wide, it would only be 42K. That saves us 31K (42%) from the 800×1204 image.

Well shoot. A savings of 42% is a big deal. Maybe we should keep going. 500 pixels wide? 450 pixels wide?

Photo of Michelle Obama with examples at 450 and 500 pixels wide

Each smaller image source offers the potential for substantial savings over the previous size. If we keep on this track, we eventually end up with an image source that is the exact size of the image in the page.

So here’s the question that has vexed me about image breakpoints. How do I know when an image source is too big for the size that the image is being used in the page?

The answer is that unless the image source matches exactly the size that the image is being displayed in the page, it is always going to be too big. There is always going to be an opportunity to optimize it further by providing a smaller image.

Why not provide the exact size of the image?

At this point, you may be wondering why we simply don’t provide the exact size of that the image is going to be used in the page.

First, the whole point of flexible images in responsive design is to provide images that scale as the size of the viewport changes. If we provided images that were exactly the size used in the page, we’d likely need to download new images whenever the viewport size changes or the device was rotated.

Second, it is unrealistic to provide images at any size imaginable. Yes, we can dynamically resize images, but when we resize images, the server needs to do that work which slows down delivery of that image to the browser.

For this reason, most larger sites cache images on content delivery networks (CDN). Caching every image size possible on the CDN would be incredibly expensive.

Finally, the browser doesn’t know the exact size of the image in the page when it starts downloading. That’s what got us to new responsive images standards in the first place!

Possible ways to pick image breakpoints

As I mentioned at the beginning, I have no rock solid solutions for how to pick the number of image sources that you need. Instead, I want to describe some different ways of looking at the problem that may help inform your decisions.

Winging it (aka, matching your layout breakpoints)

Someone on your team says, “Hey, how many source images do you think we need for these product photos?”

You ponder for a moment and say, “Hmm… how about three? Small, medium and large.”

Don’t be ashamed if you’ve done this. I’m pretty sure almost every person working on responsive images has done this at some point.

Perhaps your organization still thinks about mobile, tablet and desktop which makes small, medium and large make sense.

Or maybe you take a look at the range that the image will be displayed and make your best guess. Perhaps you simply look at the number of major layout breakpoints and decide to do the same for your image breakpoints.

I completely understand. And this is better than providing one huge image for all viewports.

But it sure would be nice to have more logic behind our decisions.

Testing representative images

If guessing doesn’t seem like a sound strategy, then let’s insert a little science into the art of picking image breakpoints. We can take a look at some representative images and figure out how many breakpoints they need.

The hardest part of doing this is determining representative images, or figuring out if you have any at all.

For some sites, all the photographs may have a particular style dictated by the brand. If that is the case, finding representative images is easy. Pick a few images and then resize them and save them at sizes ranging between the largest and the smallest images until you feel like you’ve got decent coverage.

Of course, if your site has a diversity of image styles, finding representative images can be nearly impossible.

Memory usage influencing the distribution of image breakpoints

Earlier this summer, Tim Kadlec gave a presentation on Mobile Image Processing. In that presentation, he took a look at the memory usage of flexible images in responsive designs.

What Tim showed is that as an image gets bigger, the impact of resizing an image gets larger.

Memory usage of two different images

In the example above, reducing a 600×600 pixel image by 50 pixels in each direction results in 230,000 wasted bytes versus the 70,000 wasted bytes caused by reducing a 200×200 image by 50 pixels in the exact same way.

Knowing this tells us a bit about how we should pick our breakpoints. Instead of spacing out breakpoints evenly, we should have more breakpoints as the image gets larger.

graph showing more breakpoints at large sizes

Unfortunately, while this tells us that we should have more breakpoints at larger sizes, it doesn’t tell us where those breakpoints should be.

Setting image breakpoints based on a performance budget

What if we applied the idea of a performance budget to responsive images? What would that look like?

We’d start by defining a budget for the amount of wasted bytes that the browser would be allowed to download above what is needed to fit the size of the image in the page.

So say that we decided that we had a performance budget of 20K for each responsive image. That would mean that we would need to make sure that the various sources that we’ve defined for the image are never more than 20K apart.

When we do this, we find that the number of image breakpoints change wildly based on the visual diversity of the image and the compression being used.

Let’s take a look at three sample images.

Time Square — 8 Image Breakpoints

Times Square

This image has a lot of visual diversity. The variations in colors and textures means that JPEG’s lossy compression cannot do as much without damaging the image quality.

Because of this, there are eight image breakpoints—set at 20k intervals—between the smallest size of the image (320×213) and the largest size of the image (990×660).

Breakpoint # Width Height File Size
1 320 213 25K
2 453 302 44K
3 579 386 65K
4 687 458 85K
5 786 524 104K
6 885 590 124K
7 975 650 142K
8 990 660 151K

Morning in Kettering — 3 Image Breakpoints

Morning in Kettering

Unlike the Times Square image, this image has a lot of areas with very similar colors and little variation. Because of this, JPEG can compress the image better.

On an image that can be compressed better, our 20K budget goes farther. For this image, we only need three image breakpoints to cover the full range of sizes that the image will be used at.

Breakpoint # Width Height File Size
1 320 213 9.0K
2 731 487 29K
3 990 660 40K

Microsoft Logo — 1 Image Breakpoint

Microsoft Logo

This is a simple PNG8 file. At its largest size (990×660), it is only 13K. Because of this, it fits into our 20K budget without any modifications.

Breakpoint # Width Height File Size
1 990 660 13K

Take a look at the other images on a sample page we created. See how the number of breakpoints vary even through all the images start with the same resolution end points.

Now, I’m not suggesting that you manually decide on image breakpoints for every single image. But I can envision a future where you might be able to declare to your server that you have a performance budget of 20K for responsive images and then have the server calculate the number of image sources on a per image basis.

I’ve written in more detail about performance budgets for responsive images in the past. If you end up implementing this approach, please let me know.

Setting image breakpoints based on most frequent requests

At a recent Responsive Images Community Group (RICG) meeting, Yoav Weiss and Ilya Grigorik discussed a different way of picking image breakpoints based on the most frequently requested image sizes.

For both Yoav, who works at Akamai, and Ilya, who works at Google, one of the problems they see with multiple image sources is storing all of those sources on edge servers where storage is usually more limited and costs are higher.

Not only do companies like Akamai and Google want to reduce the number of images stored at the edge, but the whole purpose of their content delivery networks is to reduce the amount of time it takes for people to render a web page.

Therefore, if they can cache the most commonly requested image sizes at the edge, they will deliver the fastest experience for the majority of their users.

For these organizations, they can tie their image processing and breakpoints logic to their analytics and change the size of the images over time if they find that new image sizes are getting requested more frequently.

When combined with the new HTTP Client Hints feature that Ilya has championed, servers could get incredibly smart about how to store images in their CDNs and do so in a way that requires little to no decision-making by designers and developers.

Humans shouldn’t be doing this

I believe that in a few years time, no one will be talking about how to pick responsive image breakpoints because no one will be doing it manually.

Sure, we may still make decisions for images that fall into the art direction use case, but even then, we’re probably not going to make finite decisions about every image source. We’ll handle the places that require our intervention and let our image processing services handle the rest.

There is a ton of benefit to either picking image sources based on a performance budget or based on the frequency with which different sizes of the image are requested. But either of these solutions are untenable as part of a manual workflow.

In the future, our typical workflow will be that we upload the highest quality image source into our content management system or image processing system and never have to think about it again.

Part 10 of a 9-part series

This started as a 9-part series, but there is always more to discuss when it comes to responsive images. Read for the conclusion of this series where I’ll provide some essential resources and talk about what the future holds for responsive images.

Responsive Images 101 Series
  1. Definitions
  2. Img Required
  3. Srcset Display Density
  4. Srcset Width Descriptors
  5. Sizes
  6. Picture Element
  7. Type
  8. CSS Responsive Images
  9. Image breakpoints
  10. Conclusion

Unpacking Module Bundlers Part 1: What is a module?

This is the first of three posts in a series about JavaScript client-side modules and code packaging.

The JavaScript community has this incredible superpower where standards of practice will emerge not from any formal process, but just from use, and will converge on a remarkably consistent, simple, and well-defined API (or small set of competing APIs). There are a lot of reasons for this — natural selection at work, the widespread practice of learning mostly by sharing code recipes causing effective and simple-to-use code patterns to replicate quickly, and of course 800-pound-gorilla projects like node/npm, AMD, and gulp. Call it the bright side of hipsterism, if you will.

It’s not a perfect world, though — ask any ten JS devs what their major points of pain are and all of them will name client-side modules and code packaging:

It’s one of the areas, like Streams, where the community has done a solid job of defining a de-facto standard API for something and sticking to it, and a rather schizophrenic job of documenting what problems the de-facto standard is solving and how it’s solving them.

It’s easy to find boilerplate and recipes for things like setting RequireJS up to make your client-side code modular and even maybe concatenate and minify it for you, but what actually is a module? How do the different systems compare? When should you use AMD or CommonJS or an ES6 polyfill? Just what exactly is Browserify actually doing? I’d like to explore confusing questions like this in this series.

What’s the problem?

In short: We need to organize and reuse our JavaScript code. But we’re faced with incompatible module syntaxes and various module loaders and tools, and choosing the right one is mysterious.

It’s inevitably necessary in any nontrivial software project to split code into a tree of interdependent segments. And we need to be able to reuse those segments.

But say segment foo relies on segment bar, and bar in turn absolutely needs the segment baz to get its job done. How do we manage that? Does foo then need to directly ensure the presence of baz (and anything baz needs and so on up the whole chain)? That’s crazy-making and untenable, and it’s the reason that it’s not enough just to split your code into separate files and include the right script tags on each page.

But what’s the bigger picture?

The real-life problem is that, as our own Lyza Danger Gardner points out, it’s a lost cause to try and cover every specific tool, interaction, and failure mode of shipping JavaScript to the browser.

Instead, I want to help you understand the context and the pieces involved in modules and packaging. The goal of this first of three articles is to give you a bigger picture of what modules—the building-block segments of our code—are at an abstract level so that we can start putting them together and making them work.

Then what’s a module?

Let’s step back from any particular language, library or standard and think about modules in the abstract for a moment. What, exactly, is a module? In the strict computer science sense, a module is a way of associating a value (most usually a collection of named subroutines) with a name of some type — and that’s it.

To make this actually useful, however, one also needs a mechanism for specifying module dependencies, that is, a way to say “This piece of code needs the values provided by this list of module names in order to be understood”.

These two parts, taken together with some under-the-hood code responsible for connecting a module name to its associated value, is what we mean when we talk about module systems.

For a concrete example, in the world of JavaScript vis-a-vis node.js, a module is a single JavaScript file, and within each module, that module’s dependencies are specified using the require statement to assign the value associated with that module name to a local variable. Like so:

/* module-c.js */
var moduleA = require('module-a');
var moduleB = require('module-b');

Most languages (Java, Python, even C…after a fashion) include some type of module system, but not JavaScript (yet — more on this in an upcoming post), so we have had to come up with our own.

There are a number of different, competing specifications for defining modules—module definition schemes. Examples are AMD, UMD, CommonJS, and node.js.

Fortunately, although there are numerous module schemes, the community has settled on a very simple formal definition of what a module itself actually is that is common across all schemes.

JavaScript Modules, the Formal Definition

As encountered in the wild, JavaScript modules all have the following characteristics:

1. A module is a JavaScript value

Illustration: Modules

A module is a JavaScript value. That’s it. Valid examples:


'Hello world'

{ readSync: function() {...}, 
  write: function() {...}, 
  /* ... */

Read those examples carefully: you really can, in all of the major module definition schemes, create a module whose value is just the integer 3 — or anything else that can be returned from a function in JavaScript. Usually, though, it’s a function or Object.

As we’ll see to be a trend, this all actually isn’t quite true for ES6 modules — but isn’t quite false, either. More on this in a future post.

2. A module is returned by a factory function

Illustration: Factory returning module

A factory function is a bit of code that gets called and returns the value of the module. Usually, this takes the form of a function you supply to the module system that will get called when the value of your module is needed.

That said, it may or may not look like you’re writing a function when you create your module. In AMD, it does — this is the function passed to define— while in node.js, your file (module) is effectively wrapped in a function that returns the module.exports object.

This value, the one returned by the factory function, is the value that users of your module get when they request your module by name from the module system (e.g. in node, this looks like var foo = require('foo-module');).

Each time the module name associated with this module gets requested, the module system is expected to return whatever the value of the module should be by invoking this factory function. Generally this happens once per runtime environment (that is, once per web page load), and the module system caches the value for later use if more things request it.

3. A module is associated with a string name

Illustration: Named module

Every module needs to have a string-valued name, e.g.

  • 'foo'
  • 'jquery'
  • 'underscore'
  • 'bar/baz/quux'
  • './myLocalModule'

Watch out! This is not a filename, though it sometimes looks like one. It’s just an identifier to associate a value with.

Some module loading schemes do apply some level of structure to this — e.g. AMD allows relative pseudo-file-paths (to be explained in a future post, don’t worry).

Modules: putting the pieces together

A JavaScript module is a JavaScript value, returned by a factory function or file, associated with a string-valued name

…and that’s it. All nontrivial browser-side module loading schemes share this definition of a module (but as usual, we’ll see that ES6 modules are the exception), which means the main difference is the syntax for how modules get defined and resolved. This is why the UMD module definition scheme can work — also to be explained shortly.

Recall that a module is not, necessarily, associated with a file on a filesystem or web server. Many module systems do make this association in some way, but it’s not a part of the fundamental concept of a module and it’s important to understand this distinction so that you can understand that one of the important questions about any given module system is how it maps modules to files.

What’s Next:

Code module handling is one of those simple-in-theory, fiddly-in-practice concepts, and nowhere is this more true than in the browser, but hopefully this post has given you a compass by which to navigate some of these subtleties.

In the next part of this series, we’ll explore the landscape of JavaScript module schemes and learn a lot more about putting these ideas into practice, so stay tuned!

Cloud Four team members Tyler Sticka, Erik Jung and Lyza Gardner contributed to this post.

Responsive Images 101, Part 8: CSS Images

Most of the time when people refer to responsive images, they are referring to inline images, not CSS images.

This is because before <picture> and srcset there were no good solutions for inline responsive images. When it comes to CSS images, we could always use media queries. So why worry?

But now it is time to revisit responsive CSS images and look at the solutions anew based on what we’ve learned about inline images.

image-set() for resolution switching

Just like when we’re working with inline images, one of the first questions we’ll need to ask ourselves is whether we’re dealing with the resolution switching or art direction use case.

For resolution switching, we should strive to provide the browser with options and let the browser pick the best possible image. The browser is in a better position to know what image will work best based on user preference, network conditions, etc.

To provide the browser with options, we should use the image-set() syntax.

image-set() syntax repeated below

You may notice some similarity between image-set() and srcset. In fact, srcset was modeled after image-set().

background-image: image-set( "foo.png" 1x, "foo-2x.png" 2x);

Like srcset, image-set’s value contains a comma-separated list of image URIs along with a display density descriptor. If a display density descriptor isn’t provided, it is assumed to be 1x.

However, image-set() does not support width descriptors yet. The plan is to refine image-set() to provide feature parity with srcset.

While most of the examples you will see for image-set() show it applied to background-image, it can be applied to any CSS property that accepts images.

image-set(): The forgotten responsive images standard

image-set() was the first responsive images specific syntax, and as mentioned, it was the foundation for srcset.

However, because we had solutions for CSS responsive images using media queries, image-set() was ignored by nearly everyone. The Responsive Images Community Group didn’t spend much time discussing it. Browsers didn’t prioritize implementing it.

Once we were nearing completion of the <picture> and srcset standards, we looked around and realized that we had neglected image-set(). Work is underway to increase the functionality of image-set() to bring it in line with srcset.

But as of publication, despite being the first responsive images standard, browser support for image-set() is lacking. It is available with a webkit prefix in Chrome, Opera, and Safari. Neither Firefox nor Microsoft have implemented it yet.

So why include it in this Responsive Images 101 series?

Because image-set() is the correct solution for resolution switching. When image-set() is widely supported, we should use it for all of the same reasons we use srcset instead of <picture> with the media attribute when we’re dealing with resolution switching.

Until image-set() is widely supported, you’ll probably end up using the CSS art direction solution for resolution switching.

Art direction

What is the CSS solution for art direction? Media queries.

It’s that simple. In fact, I’m going to assume you know media queries so no syntax sample here.

But as long as I’ve got your ear, make sure your media queries for images don’t overlap or you’ll end up with duplicate downloads. If you have any doubts, check out Tim Kadlec’s Media Query & Asset Downloading Results.

Resolution media queries

If you want to support high density displays in art direction, you’ll probably want to use the new resolution media queries.

Resolution media query syntax. Repeated below.

The resolution media query allows you to apply specific CSS rules to devices that meet the display density that you define.

@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
/* High density stuff here */

(Much thanks to CSS Tricks for this syntax sample.)

The first thing you’ll notice in the syntax above is that we’re including a -webkit prefixed media query. This is for devices that support the old device-pixel-ratio syntax. The only devices that got in the wild with this syntax used the -webkit prefix which is why it is the only one listed.

The syntax going forward is the resolution media query. In our example, we’re using min-resolution, but as you probably guessed, there is a complimentary max-resolution feature that can be used instead.

The resolution media query can check one of three things:

  • dpi — dots per inch
  • dpcm — dots per centimeter
  • dppx — dots per px unit

The first two are fairly straight-forward, but I found dppx confusing. The Mozilla Developer Network documentation defines ddpx thusly:

This unit represents the number of dots per px unit. Due to the 1:96 fixed ratio of CSS in to CSS px, 1dppx is equivalent to 96dpi, that corresponds to the default resolution of images displayed in CSS as defined by image-resolution.

Confused? I certainly was when I first read it.

Here’s the way I’ve begun to think about it, the idea of 1x, 2x, 3x, etc. is based on an imprecision. The value of 1x on some devices is different than others because some devices are 72dpi or 96dpi or whatever.

But from a CSS perspective, these differences don’t matter. The CSS Working Group has decided that there will always be a 1:96 fixed ratio of CSS inches to CSS pixels.

So while 1x might leave things up to interpretation because of 72dpi vs. 96dpi screens, 1dppx will always be what you and I think of as “1x”.

You may be asking yourself, why was 1x sufficient for srcset and image-set, but for min-resolution, it was necessary to use dppx?

I don’t know. All I know is that you can think of 1dppx as 1x, 2dppx as 2x, and so on. At this point, I’ve just accepted the inconsistency and decided to move on with my life. I recommend you do the same. ;-)

Now comes the hard part

Believe it or not, responsive images syntax is the easy part. In Part 9, we’ll discuss the vexing challenge of picking your image breakpoints.

Responsive Images 101 Series
  1. Definitions
  2. Img Required
  3. Srcset Display Density
  4. Srcset Width Descriptors
  5. Sizes
  6. Picture Element
  7. Type
  8. CSS Responsive Images
  9. Image breakpoints
  10. Conclusion