Using WebP Images

We've all been there before: You're browsing a website that has a ton of huge 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.

This is a problem, because images represent a significant portion of what's downloaded on a typical website, and for good reason. Images are expressive tools, and they have the ability to speak more than copy can. The challenge is in walking the tightrope between visually rich content, and the speedy delivery of it.

The solution to this dilemma is not one dimensional. 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 post will be 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. Let's begin!

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 media, and a great alternative format to both PNG or JPEG. WebP's visual quality is often comparable to more ubiquitous formats. Below is a comparison of a lossy WebP image and a JPEG image:

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

In the above example, the visual differences are nearly 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 a third smaller at 38 KB. Not too bad, especially when you consider that the visual quality between the two is comparable.

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 Blink-based browsers. These browsers make up a significant portion of users worldwide, however, meaning that nearly 70% of browsers in use support WebP at the time of this writing. If you had the chance to make your website faster for over two thirds of your users, would you pass it up? I think not.

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 older image formats 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 a lot like 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 using the WebP Lossless format with the Photoshop plugin, you're given no prompts. It just takes care of everything. When you choose regular old WebP for your lossy images, though, you'll get something like this:

The WebP Lossy Configuration Dialogue

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 (similar to JPEG), 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 gripe with the WebP Photoshop plugin is two-fold: There isn't a Save for Web interface for it so that you can preview what an image will look like with the settings you've chosen. If you wanted to save a bunch of images, you've have to create a batch process. 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 something like Node to convert many images at once.

Converting Images to WebP with Node

Node.js is awesome, and for jack-of all-trades types 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 build websites. 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 WebP images. 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 a few minutes. Once installed, open a terminal 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

The 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. The 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! 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

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 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 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 gets more complex when you need to use WebP images in CSS. Unlike the 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 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 those users who have JavaScript disabled. Sorry, but it's the way things are. 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 tag:

<html class="no-js">

We'll then write a small piece of inline script that we'll place before any or 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 covers all our bases. If JavaScript is available, the 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 few. 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. Another notable one is that users who save your images to the disk may not have a default program set up to view WebP images.

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!


Cover of Web Performance in Action

Jeremy Wagner is the author of Web Performance in Action, an upcoming title from Manning Publications. Use coupon code csstripc to save 38% off it, or any other Manning book.

Check him out on Twitter: @malchata