Optimizing images with WebP

vinhlh
vinh.rocks
Published in
4 min readFeb 23, 2017

WebP lossless images are 26% smaller in size compared to PNGs. WebP lossy images are 25–34% smaller than comparable JPEG images at equivalent SSIM quality index.

For the first time, I read this, I thought if I have some JPEG images, just put them into a WebP compressor, TADA!!, the size will decrease 25–34%.

Then I decided to crawl some banner images in our popular pages, and tried this miracle thing asap.

cwebp -m 6 -pass 10 -mt -lossless ${file} ${newFile}

  • -pass 10 for maximizing the amount of analysis pass,
  • -m 6 for the slowest compression method in order to get the best compression,
  • -mt multithreading for some speed improvements.

Easy! Let’s see the result!!

Wtf is going on!!! Most of compressed images was getting bigger.

You can check them on http://vinhlh.github.io/tests/webp/compare_lossless.html

Do some experiments

It’s not simple and exact like that.

What they said is WebP LOSSY images, not lossless images!

Okay, the quality of my images could be very high, and could be a reason. Yelp team faces the same thing.

Let’s do lossy compression!

cwebp -m 6 -pass 10 -mt -q jpeg_like ${qualityFactor}

Another question came to my mind, How big is this quality factor number should I set?

I tried to google and read more, then I found SSIM, a good metric to know the similarity between two images.

Okay, let’s do some experiments with quality factor = 50, 75, 90. The results of 50, 70 don’t look good to me, it’s so blurry. With 90, the difference seems to be very small (checked with a Macbook and a DELL E-series screen), the SSIM values are 0.9487292, 0.9744244, 0.9692074… Looks good!

How about other eyes? Luckily, I found Ameen, he’s a designer, and he has a Mac screen. The feedback is: nothing noticeable!

Awesome!

You can check the results of 90% lossy compression at https://vinhlh.github.io/tests/webp/compare.html

(WebP helps to reduce 39% of total size, 6 compressed images are little bigger, it’s expected).

Implementation

There are two methods to implement WebP in your website: client side and server side. But the recommended and really better way is server side implementation, using Content Negotiation.

What makes it better? It requires no changes in your application.

Okay, what’s Content Negotiation?

Whenever a client send a Accept header to the server, example, Chrome in this case:

accept: image/webp,image/*,*/*;q=0.8

The server will know the client supports WebP, then it will deliver WebP content to the client.

content-type: image/webp

Otherwise, the server will response the original image (could be jpg/png/gif).

content-type: image/gif

Then, how to implement this Content Negotiation

I could have to build a service to convert my images to WebP. But luckily, we already had Thumbor, an awesome service for managing images, and it also support converting WebP!

If I have an image

https://my-cdn.io/awesome.jpg

All I have to do to have a WebP image is

https://my-thumbor.com/unsafe/filters:format(webp)/https://my-cdn.io/awesome.jpg

So, what we have to do next is configuring our CDNs (could be nginx/ Akamai/ …) to point to WebP images whenever the browser supports WebP.

If:

  • Request has Accept Header, and starting with image/webp (I will explain later).
  • And File Extension is one of PNG/JPG/JPEG (We don’t have GIF here, because Thumbor doesn’t support Animated GIF yet).

Then

Why Vary Header? It tells the browser that: You should take the value in this header (Accept) into your Cache Key.

It means if you have two requests to a same URL but with different Accept headers, the the result of first request will be cached, and used for the second one (in case the resource is indicated to be cached on the browser).

So these two requests should be considered with two cache keys. But, why?

For this trick:

On Google Chrome, The image in img tag, requests to the server with

accept:image/webp,image/*,*/*;q=0.8

header.

But if you open this image on a new tab, this header will be:

accept:

accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8

And one thing which I guessed is: whenever you clicks Save Image As on Google Chrome, it will send a request with Accept header like when you open this image on a new tab.

TADA! It wouldn’t be image/webp* (starting with image/webp) anymore. Then the server will response the original image, not WebP one.

Wow, WebP looks like readable when users save the image. But actually, the saved image is the original one (png/ jpg).

My result

The total size of banner images in a production page decreases ~55%, from 1.8MB to 835KB.

Some important notes:

--

--