# WebP support

TIP

This article has been replaced with the "WebP, AVIF, and JPEG XL support" article.

By now, most web developers have heard about WebP, the modern image format created by Google (opens new window). But even though it supports both lossless compression and transparency, and promises to save up to 50% on file sizes, many web developers find it hard to use in pratice. It can be hard to get images converted to WebP dynamically, and not all browsers support it yet (opens new window). Good news, Imager can help you with the first and web standards with the second.

# Server support

With Imager installed you can test if your server has support for generating WebP images with the craft.imagerx.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 (opens new window), 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 (opens new window), which is available on a wide range of platforms (opens new window). 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 customEncoders config setting 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.imagerx.transformImage(image, imageSizes) %}

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

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 (opens new window), and for IE and older versions, there's the picturefill polyfill (opens new window) 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.imagerx.transformImage(image, imageSizes) %}

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

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

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

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).

TIP

Even though WebP is great for saving bytes, remember that creating twice the amount of images can put a real strain on your server. Make sure you have a good strategy in place to autogenerate as many images as possible.

# Alternative ways to deliver WebP images

Another way to deliver WebP images is by using Imager's clientSupports() 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.imagerx.serverSupportsWebp() and craft.imagerx.clientSupports('webp') %}
    {% set transformedImages = craft.imagerx.transformImage(image, imageSizes, { format: 'webp' }) %}
{% else %}
    {% set transformedImages = craft.imagerx.transformImage(image, imageSizes) %}
{% endif %}

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

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.imagerx.clientSupports('webp') ? "-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.