Denne artikkelen er bare tilgjengelig på engelsk

Using WebP images in Craft with Imager

Start using Google's awesome image format today, and make your users (and Google PageSpeed Insights) happy!

During last week's Straight Up Hangout about images in Craft 3, Andrew Welch (performance conscious as always) brought up the topic of WebP support in the upcoming version of Craft. Whether Craft 3 implements support or not, the good news is that you can start delivering WebP images today, if you're using Imager! Many developers seem to dismiss WebP since the format is not supported by all browsers. More good news: you can deliver it to browsers that support it, in a standards-compliant way, without going through any extra hoops and hacks!

The benefits of using WebP

We've established that you can deliver WebP to your users, but why would you? In short, because images delivered in WebP format are smaller and better-looking than images in JPEG, GIF, or PNG. 

WebP was created by Google (read more about it here), and the main benefit of using it is performance. Google says that on average you get images that are 25-30% smaller. In our experience, depending on the content of the image, you get even more most of the time, sometimes as much as 40-50%. 

Image transforms from our own blog shows the massive performance gains by using WebP.

Considering that images make up most of the KBs/MBs in websites today, this can have a massive impact on the page load time and the perceived performance of your site. That makes your users happy!

Also, a nice vanity bonus is that the Google PageSpeed Insights tool will stop complaining about images not being optimised, which always feel good. :-)

WebP also supports transparency, which means it can replace PNGs, while maintaining better compression than JPEG. Also, while JPEG is prone to ugly artifacts when heavily compressed, compression in WebP gives a slightly different result. To me, it looks better, but I guess it's a matter of taste.

This being a tough image to compress for any compression algorithm, WebP still manages to make it ~30% smaller than JPEG. Also, if you look closely at the original, you'll notice that the compression artifacts are much more pronounced in the JPEG version. 

Server support

With Imager installed you can test if your server has support for generating WebP images with the craft.imager.serverSupportsWebp() template variable. Chances are, it doesn't.

The current state of WebP support on web-servers is not very positive. Recent versions of GD in the major distros have WebP support enabled. Unfortunately, as with most things GD, the implementation isn't very good, and is riddled with bugs. And if you're using Imager, you're probably using Imagick to unlock the more powerful features that come with it.

Imagick needs to be compiled with support for WebP, and so far I have yet to see a distro that comes with this by default. If you're the adventurous type, go ahead and compile, it will provide the best and most performant support for WebP of all the alternatives. But, if you don't feel confident about compiling and maintaining a custom version of Imagick, or can't (if you're using something like MAMP on OSX for development), there's actually a very simple and good alternative. 

Google has created a command line tool for converting images to WebP, called cwebp, which is available on a wide range of platforms. On Ubuntu, it's as easy as running apt-get install webp, and on OSX it's available through MacPorts. With cwebp istalled, you need to configure the useCwebp (set it to true) and cwebpPath config settings appropriately.

Using cwebp is our preferred way of adding support for WebP. In theory, it'll result in a little loss of quality, and a bit more time needed to create the transforms. But, after extensive testing, both of these are negligible, compared to the benefits.

Delivering WebP images to your users

Now that you've got WebP support on the server, how do you use it? If you're not familiar with Imager, this is what you'd normally do to output an image with a source set for responsive images:

{# Create transforms #}
{% set imageSizes = [{ width: 600 }, { width: 800 }, { width: 1000 }, { width: 1200 }] %}
{% set transformedImages = craft.imager.transformImage(image, imageSizes) %}

{# Output image #}
<img src="{{ craft.imager.base64Pixel() }}" sizes="100vw" srcset="{{ craft.imager.srcset(transformedImages) }}">

Just your regular, ol' Imager image transform.

As I said, there's a standard-compliant way of delivering WebP images to the users who have support for it, and that's through the use of <picture>. In addition to delivering different images based on art-direction, the main use-case for the picture element is delivering different images based on browser capabilities. The support for the picture element is awesome, and for IE and older versions, there's the Picturefill polyfill which is an industry standard.

The revised code with support for WebP:

{# Create transforms#}
{% set imageSizes = [{ width: 600 }, { width: 800 }, { width: 1000 }, { width: 1200 }] %}
{% set transformedImages = craft.imager.transformImage(image, imageSizes) %}

{# If the server has support for WebP, create transforms #}
{% if craft.imager.serverSupportsWebp() %}
    {% set transformedImagesWebp = craft.imager.transformImage(image, imageSizes, { format: 'webp' }) %}
{% endif %}

{# Output picture element with a separate source for clients that support WebP #}
<picture>
    {% if craft.imager.serverSupportsWebp() %}
        <source sizes="100vw" srcset="{{ craft.imager.srcset(transformedImagesWebp) }}" type="image/webp">
    {% endif %}

    <img src="{{ craft.imager.base64Pixel() }}" sizes="100vw" srcset="{{ craft.imager.srcset(transformedImages) }}">
</picture>

Voila, you're delivering WebP to the users who have support for it!

In this example, the type attribute on the source means that if a browser supports images of type image/webp, it'll use that source set instead of the default one on the img element. Browsers that don't have support will use the default one.

To check if your code is working, open up a browser with WebP support (that's probably Chrome), and one which doesn't (Firefox is a good alternative). Open up the inspector tools, go to the network tab, and load your page (or this article). You'll see that Chrome is loading the file with the webp extension, while Firefox loads our good old jpg (or whatever the original file format was).

Alternative ways to deliver WebP images

Another way to deliver WebP images is by using Imager's clientSupportsWebp() template variable. It'll use the header information sent by the browser to determine if the client supports WebP or not. You could use it like this:

{# If the client has support for WebP, create WebP images, if not, use default #}
{% set imageSizes = [{ width: 600 }, { width: 800 }, { width: 1000 }, { width: 1200 }] %}
{% if craft.imager.serverSupportsWebp() and craft.imager.clientSupportsWebp() %}
    {% set transformedImages = craft.imager.transformImage(image, imageSizes, { format: 'webp' }) %}
{% else %}
    {% set transformedImages = craft.imager.transformImage(image, imageSizes) %}
{% endif %}

{# Output picture element with a separate source for clients that support WebP #}
<img src="{{ craft.imager.base64Pixel() }}" sizes="100vw" srcset="{{ craft.imager.srcset(transformedImages) }}">

Looks tempting, but there's a caveat.

The huge caveat with this is that you're now delivering different markup to different users. That means that if you're using any kind of caching, you need to make sure that the cache varies based on WebP support. With Craft's template caching you could do something like this:

{% cache using key "my-content" ~ (craft.imager.clientSupportsWebp ? "-with-webp") %}  
...
{% endcache %}

If you use Varnish, Fastly, CloudFlare Railgun, or any other kind of front-side cache, I'd strongly recommend against using client sniffing for WebP support, it'll unleash a can of worms on you. 

An alternative way of doing client sniffing is doing it at the webserver level, in your Apache or nginx configuration. I don't really see the benefits of doing this, so I haven't explored this approach.

Summary (and a last word of caution) 

In this article I've shown how you can deliver WebP images to your users with Craft and Imager. It's easily available, and although it might not be appropriate for all kinds of projects, it could make a real difference if you're especially conscientious about performance optimisations.

My only word of caution isn't actually related to WebP – more to using Imager. The most common issue that people contact me about is their servers timing out or running out of memory due to the strain they're putting on their server trying to create too many image transforms. If you add support for WebP, you'll need to create twice as many image transforms, which means you'll spend even more CPU cycles and memory. Web-servers were never intended to be good at image manipulation, and the number of image transforms that we ideally would like in this age of responsiveness is not always achievable. It's important that you find a good balance between the performance gains for the end user, and the cost of creating them. There are several strategies for creating image transforms faster with Imager, but that's a topic for a later blog post! :-)

If you liked this article or have any questions, leave a comment below or let me know on Twitter or in the Craft Slack.

Posted in ⟶

  • Craft