Responsive Images: Clown Car Technique

Adaptive images are the current hot topic in the Adaptive Design and Responsive Web Design conversations. Why? Because no one likes any of the solutions. New elements and attributes are being discussed as a solution for what is, for most of use, a big headache.

We have foreground and background images. We have large displays and small displays. We have regular resolution and high resolution displays. We have good bandwidth and low bandwidth connections.

Some choose to “waste bandwidth” (and memory) by sending high-resolution images to all devices. Others send regular resolution images to all devices which look less crisp on high resolution displays.

When it comes to background images, we have media queries. This makes it (almost) simple to send the right size and resolution images based on the device pixel ratio, size and / or orientation of the screen.

Proposed solutions with new technology

With content images, it’s a bit more difficult. Most believe that there is no mechanism for the <img> tag to download the right size and resolution image. Polyfills have been created. Services have been formed.

The <picture> element leveraging the semantics of the HTML5 <video> elements, with its support of media queries to swap in different source files was proposed:

<picture alt="responsive image">
     <source src=large.jpg media="(min-width:1600px), (min-resolution: 136dpi) and (min-width:800px) ">
     <source src=medium.jpg media="(min-width:800px), (min-resolution: 136dpi) and (min-width:400px) ">
     <source src=small.jpg>
        <!-- fallback -->
        <img src=small.jpg alt="responsive image">

Another method, using a srcset attribute on the <img> element has also been proposed. The above would be written as:

 <img alt="responsive image"
           srcset="large.jpg 1600w, large.jpg 800w 1.95x, medium.jpg 800w, medium.jpg 400w 1.95x">

Possible Solutions with Existing Tech: SVG

What many people don’t realize is we already have the technology to serve responsive images. We have had browser support for responsive images for a long, long time! SVG has supported media queries for a long time, and browsers have supported SVG for … well, not quite a long time, but still. Most browsers support media queries in the SVG (test your browser). The main issue is terms of mobile is old Androids lack of support until 3.0..

We can use media queries within SVG to serve up the right image. The beauty of the "Clown Car" technique is that all the logic remains in the SVG file. I’ve called it the "Clown Car" technique since we are including (or stuffing) many images (clows) into a single image file (car).

When you mark up your HTML, you simply add a single call to an SVG file.

<img src="awesomefile.svg" alt="responsive image">

Now isn’t that code simple?

The magic is that SVG supports both media queries and rasterized images.

In our SVG file, using the <image> element, will include the all the images that we may need to serve, and include all the media queries.

Here is the code for one of the SVG files:

<svg xmlns="" xmlns:xlink="" width="300" height="329">
  <title>The Clown Car Technique</title>
    image {display: none;}
    #small {display: block}
     @media screen and (min-width: 401px) and (max-width: 700px) {
        #medium {display: block}
        #small {display: none}
      @media screen and (min-width: 701px) and (max-width: 1000px) {
        #big {display: block}
        #small {display: none}
     @media screen and (min-width: 1001px)  {
      #huge {display: block}
      #small {display: none;}
    <image id="small"  height="329" width="300" xlink:href="images/small.png" />
    <image id="medium" height="329" width="300" xlink:href="images/medium.png" />
    <image id="big"    height="329" width="300" xlink:href="images/big.png" />
    <image id="huge"   height="329" width="300" xlink:href="images/huge.png" />

Unfortunately, when this file is used, all 4 PNGs are retrieved from the server. To solve this issue, we can use background images instead:

<svg xmlns="" viewBox="0 0 300 329" preserveAspectRatio="xMidYMid meet">
 <title>Clown Car Technique</title>
  svg {
    background-size: 100% 100%;
    background-repeat: no-repeat;
 @media screen and (max-width: 400px) {
  svg {background-image: url(images/small.png");}
 @media screen and (min-width: 401px) and (max-width: 700px) {
  svg {background-image: url(images/medium.png);}
 @media screen and (min-width: 701px) and (max-width: 1000px) {
  svg {background-image: url(images/big.png);}
 @media screen and (min-width: 1001px) {
  svg {background-image: url(images/huge.png);}

This version only downloads the images required, thereby solving the multi-HTTP and waste of bandwidth concerns. However, it seems to trigger mor Content Security Policy issues than the previous SVG.

The SVG file has it’s own declared size, but when included in HTML, the media query size is based on the proportions of the DOM node in the HTML –. it reflect thespace provided to it.

Open the first SVG file or the second SVG file in your browser, then grow and shrink your browser window. As the window grows and shrinks the image the SVG displays changes. The image size appears to stay the same — because it is the same. In the SVG, all the images to have the same dimensions. Its when we include the SVG in a flexible layout that the magic happens. You’ll note that the first time you load the second one there may be flickers of white as the browser requests the next required PNG.

When you include the SVG in your HTML <img> with a flexible width, such as 70% of viewport, as you grow and shrink the container, the image responds to the changes. The "width" media query in the SVG is based on the element width in which the SVG is contained, not the viewport width.

I have included the first SVG and the second SVG so you can see SVG with both foreground and background images. These foreground images work perfectly in Opera. In Chrome and Safari, I need to open the SVG file first, after which the HTML file containing the foreground SVG image works perfectly*. In Firefox, the SVG works. Firefox supports SVG and supports SVG as background image, but blocks the importing of external raster images due to their content security policy (CSP).

The content security policy does make sense: you don’t want a file to be pulling in untrustworthy content. SVG technology is supported. It is just being prevented from pulling in external raster image. Firefox prevents it altogether. Safari and Chrome work if the SVG is preloaded. We can work with the browser vendors to get this CSP lifted.

The browsers do all support the media queries with the SVG. They all support SVG as foreground or content images. They all support SVG as background images. The support just isn’t all the same.

Responsive SVG for foreground images works. It simply works right out of the box. For background images, it works, but I am still tweaking the code to make the background responsive (in Chrome it works if background-size is declared, but Opera and Safari are using the declared SVG size as the image size… still working on combinations that work everywhere.)

The drawback of the first SVG is that the SVG file is making 4 http requests. So, while we are displaying the right image, we are not saving bandwidth. In the second, the raster images are background image. Only the required image is downloaded.

Another pro for this technique: similar to how we separate content from presentation from behavior: this method enables us to also separate out images — all the logic is in the SVG image instead of polluting our CSS or HTML.

With <object> tag: up Next

<object> can take care of Content Security Policy drawback we see with <img> that disallows the importing of images or script into an <img> file. <object> allows both. I am working on that angle now.

The main question is: should we attempt this with <object>, or should we get browser vendors to change their content security policy?


* Interestingly, the SVG works best when the raster images are pulled cross domain rather than same origin. You would think, with CSP, it would be the exact opposite.