Skip to main content

Down the Rabbit Hole: Modern Image Formats

While browsing articles about website optimization, I came across a tip to use a modern image format like WebP. The idea is that WebP files are smaller, so pages load faster. At the time, I didn’t really understand why that would work, so I started digging into it, and I ended up going down a rabbit hole.

1. Introduction

Some background: the well-known image formats JPEG, PNG and GIF have been around for 25 years or more. They were designed when storage and processing power were expensive, so each one was built for a specific purpose. JPEG became the standard for everyday photos, PNG for icons and computer-generated images, and GIF for short animations (once the patent on its compression algorithm expired in the early 2000s).

By the early 2010s, computing had become cheap, and older ideas for better image compression finally became practical. New standards were also introduced, mostly without people noticing. Engineers also began to use hardware built into phones and other portable devices to speed up encoding and decoding of images and video. The result was a confusing patchwork of new image and video formats.

In the early 2020s, browsers chose which of these formats to support and which to ignore for the time being. Operating systems and image software, meanwhile, supported whatever suited their own users. HEIC/HEIF is a good example. It is the default image format on Apple devices, which have dedicated hardware to handle it. On other systems it can require paid plugins, or may not work at all. So most apps that display images have to quietly convert whatever users upload into a more widely supported format.

Today, only two modern formats have broad browser support: WebP and AVIF. Both aim to replace all the older formats, offering good quality at small file sizes for every kind of image, from photos to drawings, icons and short animations. AVIF goes a step further by also supporting HDR (high dynamic range).

To sketch out some context, the well-known imge formats such as “jpeg”, “png” or “gif” are with us for 25 years or more; they were put together in a time when both storage and cpu processing power were premiums rather than commodities. That meant accepting compromises and precise product-market fits: “jpeg” became the universal format for everyday pictures, “png” for icons or computer generated images and “gif” was mostly delegated to short animated sequences once the patent covering the corresponding compression algorithm expired in the early 2000s.

2. Exploring New Image Formats

Setting the background aside, using these formats on websites and web apps is fairly simple. You can start by saving your image in the new format (instead of the usual PNG or JPEG) from your image editing software. After that, you upload the pictures and add <img> tags as you normally would.

There are a few catches, though:

  • Tool support can be patchy. Your image editor may not be able to save WebP or AVIF files at all, or it may save them without the best balance of quality and file size.
  • Not every browser supports them. Depending on your audience, some users may be on browsers that don’t support AVIF, or browsers that only support WebP. Sticking with the older, reliable formats is always a valid choice if it suits your use case better.
  • Wider support means more storage. To cover everyone, you need to keep at least two copies of each image: one in the modern format and one in an older, widely supported format. Your users get a better experience, but you pay for the extra storage.

3. Implementation in Web Context

In practice, there are a few ways to support a wide audience:

  • Use the HTML5 <picture> tag with an <img> fallback. The browser picks the first format it supports, and the <img> tag points to the widely supported format as a fallback. The <picture> tag can also choose an image to suit the user’s screen size. The catch is browser compatibility: some browsers may load both the new and old image, which would make page loading slower instead of faster.

    <picture>
      <source srcset="flowers.webp" type="image/webp">
      <img src="flowers.jpg" alt="Flowers">
    </picture>
    
  • Use a JavaScript fallback with an onerror handler. The <img> tag tries the new format first. If that fails, the handler loads the older format instead. The right image does eventually appear, but the experience will probably be sluggish on older browsers.

    <img src="flowers.webp" alt="Flowers" onerror="this.onerror=null;this.src='flowers.jpg';">
    
  • Rewrite the request on the server, based on the browser’s headers. This works in almost all situations: the browser always receives a format it can display, and there’s no risk of it loading two copies of the image. The only downside is that if a user saves the image, the file extension may not match its real format.

    # Frontend:
    <img src="flowers.jpg">
    
    # Backend .htaccess file for Apache httpd:
    RewriteEngine On
    RewriteCond %{HTTP_ACCEPT} image/webp
    RewriteRule "^flowers.jpg" "flowers.webp" [L]
    
    # Nginx configuration:
    if ($http_accept ~* "image/webp") {
        rewrite ^flowers.jpg flowers.webp last;
    }
    
  • Generate images dynamically in the right format. For this you need to check both the browser’s Accept header and whether your server can produce the format. The result isn’t always a smaller file, depending on the image content, so test it before committing to this approach. Here is a PHP example using GD:

    $webp_available = ((imagetypes() & IMG_WEBP) > 0)
        && is_array($_SERVER)
        && array_key_exists('HTTP_ACCEPT', $_SERVER)
        && str_contains($_SERVER['HTTP_ACCEPT'], 'image/webp');
    
    header("Content-Type: image/" . ($webp_available ? 'webp' : 'png'));
    header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
    header("Pragma: no-cache");
    
    if ($webp_available) {
        ImageWEBP($image, null, 100);
    } else {
        ImagePNG($image);
    }
    

4. Server Side: Heavy Lifting

If your image editor doesn’t make good use of the new formats, or saving multiple copies of every picture becomes a chore, giving up can seem like the natural choice. There is help available on the server side, though, depending on your platform:

  • Content management systems often have plugins that do this for you. They create the extra copies automatically. Plugins like this are readily available for WordPress, for example.

  • You can convert images in bulk with a few Linux shell commands:

    # Install the WebP and AVIF command-line tools (run as root)
    dnf install libwebp-tools libavif-tools
    
    # Create WebP copies of all PNG images in the current directory
    find . -maxdepth 1 -type f -name "*.png" -exec sh -c 'cwebp -lossless "$0" -o "${0%.png}.webp"' {} \;
    
    # Create AVIF copies of all JPEG images, including subdirectories
    find . -type f \( -name "*.jpg" -o -name "*.jpeg" \) -exec sh -c 'avifenc "$0" "${0%.*}.avif"' {} \;
    

5. Conclusion

This exploration was an eye-opener, for me at least. My personal view is that for the average website, adopting a new image format brings only a small, not very consequential improvement. There are other use cases where it matters more, though:

  • CDN services (content delivery networks) can convert images transparently, and at their scale the bandwidth savings add up.
  • Photo-sharing sites and social media platforms can use AVIF for its HDR support, giving better-looking images to users with compatible screens, such as recent Apple devices or high-end desktop monitors.