Modern Image Formats

JPEG, PNG and GIF work everywhere, but newer codecs give the same quality in fewer bytes. WebP stores a VP8 video key frame (or a lossless bitstream) in a RIFF container; Google measures it 25-34% smaller than JPEG, and 26% smaller than PNG when lossless. AVIF stores an AV1 key frame in a HEIF container and usually beats WebP, at the cost of slower encoding. JPEG XL (ISO/IEC 18181) was designed as a still-image codec from the start: it decodes progressively, supports HDR and wide color gamuts (as AVIF does), and can losslessly repack an existing JPEG about 20% smaller.

Raster image formats for the web
Format Compression Alpha Animation Support, September 2026
JPEG Lossy No No All browsers
PNG Lossless Yes APNG All browsers
GIF Lossless, 256 colors 1-bit Yes All browsers
WebP Lossy or lossless Yes Yes Baseline since 2020 (Safari 14 10 )
AVIF Lossy or lossless Yes Yes Baseline since January 2024 (Edge 121)
JPEG XL Lossy or lossless Yes Yes Safari 17+ (stills only); Chrome 1 , Firefox 555 behind flags

JPEG XL has had a bumpy ride. Chrome removed its flag-only decoder in Chrome 110 (February 2023); Safari 17 (September 2023) shipped still-image support; Chrome 145 (February 2026) brought it back behind a flag with jxl-rs, a memory-safe Rust decoder, and Chrome Platform Status lists default enablement as proposed. Firefox enables it only in Nightly. Until more engines ship it, treat JPEG XL as an optional extra source.

Serve new formats with a fallback chain. The browser takes the first <source> whose type it supports, without downloading the others, so list the smallest format first:

AVIF first, WebP second, JPEG for everyone elseHTMLLive
<picture>
  <source type="image/avif" srcset="elva-480.avif 480w, elva-800.avif 800w, elva-1200.avif 1200w">
  <source type="image/webp" srcset="elva-480.webp 480w, elva-800.webp 800w, elva-1200.webp 1200w">
  <img src="elva-800.jpg" srcset="elva-480.jpg 480w, elva-800.jpg 800w, elva-1200.jpg 1200w"
       sizes="(width <= 600px) 100vw, 600px" width="800" height="533" alt="Girl in a fairy costume">
</picture>

Generate the files at build time with sharp 32,725 (github.com/lovell/sharp (https://github.com/lovell/sharp 32,725 ), Apache-2.0, npm 2,036 install sharp). It wraps libvips, which processes images on demand in small regions across threads rather than loading every pixel into memory; sharp's README puts resizing at typically 4x-5x faster than ImageMagick 3,122 .

build-images.mjs: generating srcset candidates in three formats with sharpJavaScript
import sharp from 'sharp';
import { stat } from 'node:fs/promises';
const source = 'elva-fairy-original.jpg';          // 2592 x 1728 camera JPEG
const formats = {
  jpg:  (img) => img.jpeg({ quality: 80, mozjpeg: true }),
  webp: (img) => img.webp({ quality: 75 }),
  avif: (img) => img.avif({ quality: 50, effort: 4 }),
};
for (const width of [480, 800, 1200]) {
  const row = [`${width}w`.padEnd(6)];
  for (const [ext, encode] of Object.entries(formats)) {
    const file = `elva-${width}.${ext}`;
    await encode(sharp(source).resize({ width })).toFile(file);
    row.push(`${ext} ${((await stat(file)).size / 1024).toFixed(0).padStart(4)} KB`);
  }
  console.log(row.join('  '));
}
console.log('JPEG XL output available:', sharp.format.jxl.output.file);
Output
480w    jpg   25 KB  webp   17 KB  avif   12 KB
800w    jpg   58 KB  webp   34 KB  avif   25 KB
1200w   jpg  109 KB  webp   59 KB  avif   48 KB
JPEG XL output available: false

Here AVIF is about half the size of MozJPEG 5,724 , with WebP in between. Quality numbers differ between codecs, so tune by eye (squoosh.app 33,602 compares side by side). sharp's prebuilt binaries omit JPEG XL; use cjxl from libjxl 3,686 (github.com/libjxl/libjxl (https://github.com/libjxl/libjxl 3,686 ), BSD-3-Clause). To resize on request instead, run imgproxy (Apache-2.0) or Thumbor (MIT), or pay for an image CDN such as Cloudinary 976 or imgix.