JPEG XL Returns to Chrome: From Obsolete to the Future

⚡ Chromium 🔧 Rust / C++ 👤 Helmut Januschka

How JPEG XL went from "obsolete" to the future of web images - and the honor of being part of its comeback

Live Demo: JXL Animation

Here's a live example of JXL animation support - the implementation shown in the video above. No browser currently supports JXL animations natively, so this uses a WASM polyfill:

Animated JXL Demo

Background

In October 2022, Google removed JPEG XL from Chromium, citing "insufficient ecosystem interest." In November 2025, Chrome's Architecture Tech Leads announced: "We would welcome contributions to integrate a performant and memory-safe JPEG XL decoder in Chromium."

What Changed

Rick Byers' announcement on behalf of Chrome ATLs outlined the ecosystem developments:

The ecosystem evolved between 2022 and 2025.

JPEG XL Features

JPEG XL offers several technical improvements over existing formats:

Implementation Approach

Following Rick's announcement, the implementation work began.

Phase 1: C++ Implementation

The initial approach used the reference implementation - libjxl in C++. Using the previous Chromium JPEG XL code as a blueprint, the implementation was updated to the latest spec:

CL 7170439 - Full implementation with animation support

// Integrated libjxl decoder with Blink's image pipeline
class JXLImageDecoder : public ImageDecoder {
  // Handle standard decoding
  void Decode(const uint8_t* data, size_t length);

  // Animation support
  size_t FrameCount() const override;
  cc::ImageHeaderMetadata MakeMetadataForDecodeAcceleration() const override;
};

Status: Feature complete, demo video here.

The feedback requested using Rust for memory safety.

Phase 2: Rust Implementation

Chromium is moving toward memory-safe code. The pivot to jxl-rs, a pure Rust JPEG XL decoder, aligned with this direction.

CL 7184969 - Rust-based implementation using jxl-rs

// Memory-safe decoder via CXX bindings
pub fn decode_jxl(data: &[u8]) -> Result<DecodedImage> {
    let decoder = JxlDecoder::builder()
        .build()?;
    decoder.decode_to_image(data)
}

The Rust decoder required performance optimization. The jxl-rs community has been working on improvements:

PR #506 improves decode performance through parallel VarDCT decoding and AVX2+FMA SIMD optimizations:

Image Size Before PR #506 After PR #506 C++ libjxl Speedup
bike 2048×2560 265ms 198ms 170ms +34%
progressive 4064×2704 694ms 560ms 450ms +24%
blendmodes 1024×1024 115ms 85ms 266ms +35%

The gap narrowed from 56% slower to 4% slower than C++. PR #509 provides a WebAssembly-based polyfill for browsers without native support.

Current Status

The implementation includes:

Standard image decoding - JPEG XL images render correctly
ICC color profiles - Proper color management
Animations - Multi-frame JXL support
Alpha/transparency - Full alpha channel support
Wide gamut - Display-P3 and other color spaces
HDR support - PQ/HLG transfer functions (PR #491 MERGED)

Requirements for Shipping

Chrome's requirements:

  1. Performant decoder - Performance optimizations are ongoing
  2. Memory-safe implementation - Rust provides memory safety
  3. Long-term maintenance - The jxl-rs community is active

Format Diversity

Different image formats serve different needs:

Having multiple format options allows developers to choose based on their specific requirements.

Acknowledgments

Special thanks to Luca Versari (veluca93) for reviewing and merging upstream PRs and managing jxl-rs releases. The collaboration and responsiveness from the jxl-rs maintainers has been instrumental in making this implementation possible.

Implementation Details

Status: 🚧 In active development

Upstream Work:

Demo:


Links: