How can I automatically generate responsive images from a single hi-res asset? I want to be able to upload a single high resolution image, and output a &lt;picture&gt; element that outputs it with a few different sizes and resolutions automatically (using Picturefill to sort out which one actually shows up to the browsers). How can I do that?
You can do this using image transforms.

There are a couple things you’re going to need to consider though:


Not every image will necessarily be high-res (e.g. a computer screenshot taken on a non-retina screen)
You probably don’t want to output images that are larger than their original size


For #1, we need some way of identifying images that are 1X vs. 2X. You could do this a couple different ways:


Give your assets a Lightswitch field that identifies if it’s a 2X image
Use a consistent naming convention for 2X image filenames, e.g. “filename@2x.ext” (to borrow Retina.js’s convention)


I personally prefer the second. (That’s what we do on buildwithcraft.com, except we use “.2x” instead of “@2x”.)

We can tackle #2 with some logic in the template that only uses a transform when the resulting size is going to be smaller than the original image.

It’s the combination of these two problems where things get interesting: You need to figure out the image’s CSS pixel size (based on whether it’s 1X or 2X) before you start determining whether it needs a transform, and how big to make the transform, etc.

Here’s what we came up with for Craft’s documentation (such as the image on the asset transforms page):


Create a macro called srcset() which will be responsible for outputting a &lt;source&gt; or &lt;img&gt; tag’s srcset= attribute and value, and optionally the width= and height= attributes as well. It should accept the following arguments:

image - an AssetFileModel
maxCssWidth - the maximum “CSS pixel” width that we want to output the image at
includeDimensions - whether the macro should also output the width= and height= attributes.

Create a &lt;picture&gt; element with nested &lt;source&gt; and &lt;img&gt; elements per the Picture spec, using our new srcset() macro to write out the srcset= attributes.


Here’s what the macro looks like:

{% macro srcset(image, maxCssWidth, includeDimensions) %}
    {%- spaceless %}
        {# Is this a 2X image? #}
        {% set is2x = image.filename matches '/@2x\\.\\w+$/' %}

        {# Determine the CSS width for the image, if it were output at 100% #}
        {% set cssWidth = (is2x ? round(image.width / 2) : image.width) %}

        {# Determine the image size that 1X screens should get #}
        {% set width = (cssWidth &gt; maxCssWidth ? maxCssWidth : cssWidth) %}

        {# Determine the image size that 2X screens should get #}
        {% set width2x = (width * 2) &gt; image.width ? image.width : (width * 2) %}

        {# Output the srcset= attribute value #}
        srcset="{{ image.getUrl(image.width != width ? { width: width }) }}, {{ image.getUrl(image.width != width2x ? { width: width2x }) }} 2x"

        {#- Output the width= and height= attributes if needed #}
        {%- if includeDimensions %} width="{{ width }}" height="{{ image.getHeight({ width: width }) }}"{% endif %}
    {% endspaceless -%}
{% endmacro %}


Adjust that {% set is2x = ... line based on however you’re identifying 2X images. For example if you’re going the Lightswitch route instead, it could be as simple as:

{% set is2x = image.is2x %}


Note that each of the image.getUrl() calls on the {{ output }} line are making sure that the width is different than the actual image width before passing the { width: X } object into getUrl() - that way we’re not having Craft go through the trouble of generating a transform unless we actually need to.

With that macro in place, now we just need to import it in our template, and start using it.

{% from _self import srcset %}

{# ... #}

{% for image in entry.myAssetsField %}
    &lt;picture&gt;
        &lt;!--[if IE 9]&gt;&lt;video style="display: none;"&gt;&lt;![endif]--&gt;
        &lt;source media="(min-width: 1000px)" {{ srcset(image, 1000) }}&gt;
        &lt;source media="(min-width: 800px)" {{ srcset(image, 800) }}&gt;
        &lt;source media="(max-width: 320px)" {{ srcset(image, 320) }}&gt;
        &lt;!--[if IE 9]&gt;&lt;/video&gt;&lt;![endif]--&gt;
        &lt;img {{ srcset(image, 600, true) }} alt="{{ image.title }}"&gt;
    &lt;/picture&gt;
{% endfor %}


If you placed the macro in a different template, replace _self with the path to that template, e.g. "_macros".