Jeremy Wagner's

Web Development Blog

With occasional rambling diatribes about other stuff.

Using WebP Images

April 25, 2016 (updated September 26, 2016)

This article has been updated to reflect changes in the imagemin API.

We've all been there before: You're browsing a website that has a ton of huge and beautiful images of delicious food, or maybe that new gadget you've been eyeballing. These images tug at your senses, and for content authors, they're essential in moving people to do things.

Except that these images are downright huge. Like really huge. On a doddering mobile connection, you can even see these images unfurl before you like a descending window shade. You're suddenly reminded of the bad old days of dial-up connections.

I thought we moved past this, already.
I thought we moved past this, already.

This is a problem. You need to make sure that these images aren't causing your audience to become impatient. Impatience begets fidgeting. Once your users are fidgeting, they're thinking about moving on. They will leave. Worse yet, they'll go to someplace else. Someplace that's not your website.

Images represent a significant part of content on the web, and for good reason. The written word is a powerful form of expression, but nothing evokes those primal feelings like a good visual can. The problem is walking the tightrope between visually rich content and the speedy delivery of it.

The answer is not a singular one. Many techniques exist for slimming down unruly images, and delivering them according to the capabilities of the devices that request them. Such a topic can easily be its own book, but the focus of this article will be something very specific: Google's WebP image format, and how you can take advantage of it to serve images that have all the visual fidelity your images have now, but at a fraction of the file size. All you need are two things:

  1. Some images to work with.
  2. A desire to learn.

Let's learn about WebP!

What is WebP, and Why Should I Even Care?

WebP is an image format developed and first released by Google in 2010. It supports encoding images in both lossless and lossy formats, making it a versatile format for any type of visual content, and an alternative format to both PNG or JPEG. The results are usually comparable.

Can you tell the difference? (Hint: the WebP version is on the right.)
Can you tell the difference? (Hint: the WebP version is on the right.)

In the above example, the visual differences are almost imperceptible, yet the differences in file size are substantial. The JPEG version on the left weighs in at 56.7 KB, and the WebP version on the right is nearly one third smaller at 38 KB. Not a bad improvement, especially when you consider that the visual quality between the two are pretty much the same.

So the next question (of course) is “what's the browser support?” Not as slim as you might think. Since WebP is a Google technology, support for it is fixed to Chromium-based browsers. These browsers make up a significant portion of users worldwide, however, meaning that nearly 67% of browsers in use support WebP at the time of this writing. If you had the chance to make your website faster for two thirds of your users, would you pass it up?

Thought so.
Thought so.

It's important to remember though that WebP is not a replacement for JPEG and PNG images. It's a format you can serve to browsers that can use it, but you should keep your older images on hand for other browsers. This is the nature of developing for the web: Have your Plan A ready for browsers that can handle it, and have your Plan B (and maybe Plan C) ready for those browsers that are less capable.

Enough with the disclaimers. Let's optimize!

Converting your Images to WebP

If you're familiar with Photoshop, the easiest way to get a taste for WebP is to try out the WebP Photoshop Plugin. After you install it, you'll be able to use the Save As option (not Save For Web!) and select either WebP or WebP Lossless from the format dropdown.

What's the difference between the two? Think of it as being similar to the differences between JPEG and PNG images. JPEGs are lossy and PNG images are lossless. Use regular old WebP when you want to convert your JPEG images. Use WebP Lossless when you're converting your PNGs.

When you save images to the WebP Lossless format with the Photoshop plugin, you're given no prompts to tweak settings or anything. It just takes care of everything. When you choose regular old WebP for your lossy images, you'll get something like this:

Lots going on here, huh?
Lots going on here, huh?

The settings dialogue for lossy WebP gives more flexibility for configuring the output. You can adjust the image quality by using a slider from 0 to 100, set the strength of the filtering profile to get lower file sizes (at the expense of visual quality, of course) and adjust noise filtering and sharpness.

My big complaint with the WebP Photoshop plugin is two-fold: There isn't a Save for Web interface for it so that I can preview what an image will look like with the settings I've chosen, and I'd have to create a batch process to save out a bunch of images. My second gripe probably isn't a hurdle for you if you like batch processing in Photoshop, but I'm more of a coder, so my preference is to use Node to convert many images at once.

Converting Images to WebP with Node

Node.js is awesome, as you may or may not be able to agree with, depending on your feelings and experience with it. For jack of all tradesmen such as myself, it's less about the fact that it brings JavaScript to the server and more that it's a productivity tool that I can use while I write code. In this article, we're going to use Node to convert your JPEGs and PNGs to WebP images en masse with the use of a Node package called imagemin.

imagemin is the Swiss Army Knife of image processors in Node, but we'll just focus on using it to convert all of our JPEGs and PNGs to WebPs (whew!) Don't fret, though! Even if you've never used Node before, this article will walk you through everything. If the idea of using Node bugs you, you can use the WebP Photoshop plugin and skip ahead.

The first thing you'll want to do is download Node.js and install it. This should only take you five minutes. Once installed, open a terminal/command line window and go to your web project's root folder. From there, just use Node Package Manager (npm) to install imagemin and the imagemin-webp plugin:


npm install imagemin imagemin-webp

This install may take up to a minute. When finished, open your text editor and create a new file named webp.js in your web project's root folder. Type the script below into the file:


var imagemin = require("imagemin"),    // The imagemin module.
    webp = require("imagemin-webp"),   // imagemin's WebP plugin.
    outputFolder = "./img",            // Output folder
    PNGImages = "./img/*.png",         // PNG images
    JPEGImages = "./img/*.jpg";        // JPEG images

imagemin([PNGImages], outputFolder, {
    plugins: [webp({
        lossless: true // Losslessly encode images
    })]
});

imagemin([JPEGImages], outputFolder, {
    plugins: [webp({
        quality: 65 // Quality setting from 0 to 100
    })]
});

This script will process all JPEG and PNG images in the img folder and convert them to WebP. When converting PNG images, we set the lossless option to true. When converting JPEG images, we set the quality option to 65. Feel free to experiment with these settings to get different results. You can experiment with even more settings at the imagemin-webp plugin page.

This script assumes that all of your JPEG and PNG images are in a folder named img. If this isn't the case, you can change the values of the PNGImages and JPEGImages variables. This script also assumes you want the WebP output to go into the img folder. If you don't want that, change the value of the outputFolder variable to whatever you need. Once you're ready, run the script like so:


node webp.js

This will process all of the images and dump their WebP counterparts into the img folder. What benefits you realize will depend on the images you're converting. In my case, a folder with JPEGs totaling roughly 2.75 MB was trimmed down to 1.04 MB without any perceptible loss in visual quality. That's a 62% reduction without much effort!

"Have you ever seen my WebPs?"
"Have you ever seen my WebPs?"

Now that all of your images are converted, you're ready to start using them. Let's jump in and put them to use!

Using WebP in HTML

Using a WebP image in HTML is like using any other kind of image, right? Just slap that sucker into the <img> tag's src attribute and away you go!


<!-- Nothing possibly can go wrong with this, right? -->
<img src="img/myAwesomeWebPImage.webp" alt="WebP rules.">

This will work great— but only for browsers that support it. Woe betide those unlucky users who wander by your site when all you're using is WebP:

WHAT HAPPENED
WHAT HAPPENED

It sucks, sure, but that's just the way front end development is, so buck up. Some features just aren't going to work in every browser, and that's not going to change anytime soon. The easiest way we can make this work is to use the <picture> element to specify a set of fallbacks like so:


<picture>
    <source srcset="img/awesomeWebPImage.webp" type="image/webp">
    <source srcset="img/creakyOldJPEG.jpg" type="image/jpeg">
    <img src="img/creakyOldJPEG.jpg" alt="Alt Text!">
</picture>

This is probably your best best for the broadest possible compatibility because it will work in every single browser, not just those that support the <picture> element. The reason for this is that browsers that don't support <picture> will just display whatever source is specified in the <img> tag. If you need full <picture> support, you can always drop in Scott Jehl's super-slick Picturefill script.

Using WebP Images in CSS

The picture (see what I did there?) gets more complicated when you need to use WebP images in CSS. Unlike the <picture> element in HTML which falls back gracefully to the <img> element in all browsers, CSS doesn't provide a built-in solution for fallback images that's optimal. Solutions such as multiple backgrounds end up downloading both resources in some cases, which is a big optimization no no. The solution lies in feature detection.

Modernizr is a well-known feature detection library that detects available features in browsers. WebP support just so happens to be one of those detections. Even better, you can do a custom Modernizr build with only WebP detection at https://modernizr.com/download, which allows you to detect WebP support with very low overhead.

When you add this custom build to your website via the <script> tag, it will automatically add one of two classes to the <html> element:

  1. The webp class is added when the browser supports WebP.
  2. The no-webp class is added when the browser doesn't support WebP.

With these classes, you'll be able to use CSS to load background images according to a browser's capability by targeting the class on the <html> tag:


.no-webp .elementWithBackgroundImage{
    background-image: url("image.jpg");
}

.webp .elementWithBackgroundImage{
    background-image: url("image.webp");
}

That's it. Browsers that can use WebP will get WebP. Those that can't will just fall back to supported image types. It's a win-win! Except…

What About Users with JavaScript Disabled?

If you're depending on Modernizr, you have to think about users with JavaScript disabled. Sorry, but it's the way things are.

You can't escape it.
You can't escape it.

Yeah, that's how most developers feel about it, but the user who has JavaScript disabled is someone you do have to think about. It's more of a potential issue than you might think it is, so if you're going to use feature detection that can leave some of your users in the dark, you'll need to test with JavaScript disabled. With the feature detection classes used above, JavaScript-less browsers won't even show a background image. This is because the disabled script never gets to add the detection classes to the <html> element.

To get around this, we'll start by adding a class of no-js to the <html> tag:


<html class="no-js">

We'll then write a small piece of inline script that we'll place before any <link> or <script> tags:


<script>
    document.documentElement.classList.remove("no-js");
</script>

This will remove the no-js class on the <html> element when parsed.

So what good does this do us? When JavaScript is disabled, this small script never runs, so the no-js class will stay on the element. This means we can can add another rule to provide an image type that has the widest support:


.no-js .elementWithBackgroundImage{
    background-image: url("image.jpg");
}

This does everything we need. If JavaScript is running, the small inline script is run and removes the no-js class before the CSS is parsed, so the JPEG is never downloaded in a WebP-capable browser. If JavaScript is indeed turned off, then the class is not removed and the more compatible image format is used.

Now that we've done all of this, these are the use cases we can expect:

  1. Those who can use WebP will get WebP.
  2. Those who can't use WebP will get PNG or JPEG images.
  3. Those with JavaScript turned off will get PNG or JPEG images.

Give yourself a hand. You just learned how to progressively use WebP images.

In Closing

WebP is a versatile image format that we can serve in place of PNG and JPEG images— if it's supported. It can yield a substantial reduction in the size of images on your website, and as we know, anything that results in transferring less data lowers page load time.

Are there cons? A couple. The biggest one is that you're maintaining two sets of images to achieve the best possible support, which may not be possible for your website if there's a huge set of imagery that you need to convert over to WebP. Another is that you'll have to manage a bit of JavaScript if you need to use WebP images in CSS.

The takeaway is that the relatively low effort is worth the savings you'll realize, savings that will improve the user experience of your website by allowing it to load faster. Users browsing via mobile networks will benefit especially. Now go forward and WebP to your heart's content!

Thoughts? Let's hear them!