Menu

Snippets

Yet another programmer blogging about code

Photographic PNGs with transparency are better as lossy WebP

WebP image files are typically much smaller than their JPEG and PNG originals, with virtually no loss of fidelity. But a photographic PNG with a transparency can really show how much better WebP can be.

WebP images are making a real difference to page load times for image-heavy websites. Images are usually at least 25% smaller when converted to WebP, sometimes significantly better than that, and WebP has pretty good browser support these days. But something I’ve noticed with automatic image compression programs is that they tend to use lossy WebP compression for JPEG, and lossless compression for PNG, with no option. Lossy compression means that the WebP will look pretty much the same as the original, but not exactly; lossless means there will be no difference.

In general, that’s a good split. JPEG images are typically used for photographs and artistic images with colour transitions, whereas PNG images are typically used where the are solid colours and especially text / fine details that must not be corrupted by compression artefacts. Preserving that split between lossy and lossless compression makes a lot of sense in that context.

Photos with transparent backgrounds

But there’s a scenario where photographs can be encoded as PNG images instead of JPEG: transparent backgrounds! If your lovely product photographs need to sit on top of a textured website background and allow the background to show through, you’ll need to use PNG because the old JPEG format does not support transparent backgrounds!

Aside: JPEG 2000 does support transparent backgrounds. Sadly, most web browsers don’t support JPEG 2000, and it’s also a bit of a bugger to automate, so it isn’t really used much on the web. And if you’ve ever wondered why so many online shops go for a white background, this is one of the biggest reasons: it’s easy to match the white background of a JPEG image to the white background of a web page, but it can be frustrating with other colours due to colour profile differences, so white it is!

OK, so now you get that nice, smallish JPEG image and convert it to a PNG and give it a transparent background and compress it as much as you can with optipng and pngout and whatever else you can throw at it and Oh My Fairy Godmother it’s still huge and so much bigger than when it was a JPEG! What now?

Lossy WebP to the rescue

It turns out that this is exactly where WebP shines the brightest. Here’s a nice wine bottle image with a transparent background. At a resolution of 1468 x 5139 and with full 24-bit colour and an alpha channel (the transparent background), it’s a massive 4.6MB. Converting that to a lossless WebP image shaves about 32% off the size, to give us a 3.1MB image — still pretty big. But converting it to a lossy WebP image slashes 95% off the size, leaving a lightweight 236KB image!

  • original PNG: 4.6MB
  • lossless WebP: 3.1MB
  • lossy WebP: 236KB

Yes, you read that right, 95% reduction in size, from 4.6MB to 236KB. How is that possible? Because photographs are totally the wrong type of image to store as PNG, and we’re only doing it because of the transparent background. Lossy compression is the saviour of photographic images on the web because they can be so much smaller with little to no apparent loss of fidelity for the viewer. WebP gives us that magical compromise of lossy compression for the image, and lossless compression for the alpha channel, allowing us to have the best of JPEG and PNG with even better compression.

Bottle image as PNG with transparent background
Bottle image as PNG with transparent background

Automating it with EWWW Image Optimizer

So now the trick is getting our automated compression tools to let us have lossy WebP compression for PNG images. I’ve been doing that for some of my clients who have a stock of product images screaming out for this, using EWWW Image Optimizer. It’s a terrific plugin with some awesome options, and the best bit is a filter hook that lets me change the WebP compression modes.

The filter hook is ewww_image_optimizer_bypass_webp, and it allows developers to bypass the normal WebP processing that EWWW does to provide our own conversion. I use it to run exactly what EWWW does for JPEG images, whenever it is about to convert PNG images to WebP.

Update: EWWW Image Optimizer now does this for you! Define EWWW_IMAGE_OPTIMIZER_LOSSY_PNG2WEBP as true in your wp-config.php file and PNG images will be converted to lossy WebP images. Yay! Since version 5.1.0.

/**
* maybe process PNG as lossy WebP
* @param bool $bypass
* @param string $filename
* @return bool
*/
add_filter('ewww_image_optimizer_bypass_webp', function($bypass, $filename) {
    $type = ewww_image_optimizer_mimetype($filename, 'i');

    if ($type === 'image/png') {
        $webpfile = $filename . '.webp';
        $nice = ewww_image_optimizer_find_nix_binary('nice', 'n');
        $quality = DEFAULT_WEBP_QUALITY;
        $tools = ewww_image_optimizer_path_check($j = false, $o = false, $g = false, $p = false, $q = false, $w = true);
        $tool = $tools['CWEBP'];
        $cmd = sprintf("$nice $tool -q $quality -metadata none -quiet %s -o %s 2>&1",
            ewww_image_optimizer_escapeshellarg($filename),
            ewww_image_optimizer_escapeshellarg($webpfile));
        exec($cmd, $cli_output);

        $orig_size = ewww_image_optimizer_filesize( $filename );
        $webp_size = ewww_image_optimizer_filesize( $webpfile );
        ewwwio_debug_message( "webp is $webp_size vs. $type is $orig_size" );

        if ( !is_file( $webpfile ) ) {
            ewwwio_debug_message( 'lossy webp file creation failed' );
        }
        elseif ( $orig_size < $webp_size) {
            ewwwio_debug_message( 'webp file was too big, deleting' );
            unlink( $webpfile );
        }
        else {
            // Set correct file permissions.
            $stat  = stat( dirname( $webpfile ) );
            $perms = $stat['mode'] & 0000666; // Same permissions as parent folder, strip off the executable bits.
            chmod( $webpfile, $perms );

            $bypass = true;
        }
    }

    return $bypass;
}, 10, 2);

Note that this is not something you would do on every website. Typically, PNG images are used where lossless compression makes more sense, so forcing such images to lossy might make things worse. But when the stock of PNGs on a website are largely product photographs with transparent backgrounds, this can make a huge difference in page load times on the majority of modern web browsers. Except Safari, who are still holding out against WebP!