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

Status: โœ… LANDED โ€” JPEG XL is now supported in Chromium Canary 145.0.7632.0+!

๐ŸŽ‰ MISSION ACCOMPLISHED

JPEG XL has officially returned to Chromium.

Available in Canary 145.0.7632.0+ (Enable via chrome://flags/#enable-jxl-image-format)

The Story

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?

Why JPEG XL?

Implementation

Phase 1: C++ (Abandoned)

Initial approach used libjxl in C++. Feature complete with animation support, but feedback requested Rust for memory safety.

ABANDONED CL 7170439 โ€” C++ implementation (superseded by Rust)

Phase 2: Rust (Current)

Pivoted to jxl-rs, a pure Rust decoder. Memory-safe and aligned with Chromium's direction.

MERGED CL 7201443 โ€” Add jxl-rs to third_party (73,908 lines)

Blink integration:

  1. MERGED CL 7320482 โ€” Add JXL infrastructure: enums and build flag
  2. MERGED CL 7319379 โ€” Add JXL image decoder using jxl-rs
  3. MERGED CL 7184969 โ€” Wire up JXL decoder (The final piece!)

The Rust decoder required significant optimization. The jxl-rs community merged 26 PRs in December 2025:

Image Before After C++ libjxl Speedup
bike (2048ร—2560) 265ms 198ms 170ms +34%
progressive (4064ร—2704) 694ms 560ms 450ms +24%
blendmodes (1024ร—1024) 115ms 85ms 266ms +35%
View all jxl-rs contributions (26 PRs) โ†’

SIMD Optimizations:

  • MERGED #585 SIMD table lookup with shuffle
  • MERGED #537 SIMD F32 to U8/U16 conversions
  • MERGED #536 SIMD transfer functions
  • MERGED #535 SIMD upsampling (2x, 4x, 8x)
  • MERGED #533 SIMD min and store_interleaved
  • MERGED #532 SIMD noise convolution
  • MERGED #530 SIMD chroma upsampling
  • MERGED #529 SIMD YCbCr to RGB

Performance:

  • MERGED #600 SinglePropertyLookup for table-based routing
  • MERGED #583 Optimize EPF sigma for modular encoding
  • MERGED #581 Cache default quantization tables
  • MERGED #538 Flattened modular trees
  • MERGED #526 Weighted predictor cache locality
  • MERGED #525 Cache natural coefficient orders
  • MERGED #524 Precompute cosines in spline rendering

API & Integration:

  • MERGED #594 Bump version to 0.2.2
  • MERGED #587 Bump version to 0.2.1
  • MERGED #586 Fix rendering bugs
  • MERGED #574 Add premultiply_output option
  • MERGED #556 FFI-friendly API for Chromium
  • MERGED #540 Benchmark CI stage
  • MERGED #496 Remaining decoder API methods
  • MERGED #495 Preview frame API
  • MERGED #494 Fix spline DISTANCE_EXP
  • MERGED #493 Fix rect bounds check
  • MERGED #492 Remove zerocopy dependency
  • MERGED #491 HDR tone mapping
  • MERGED #533 Add min and store_interleaved

Current Status

โœ… Standard image decoding
โœ… ICC color profiles
โœ… Animations (multi-frame)
โœ… Alpha/transparency
โœ… Wide gamut (Display-P3)
โœ… HDR support (PQ/HLG)

Chromium rolls:


Use JPEG XL Today

Don't want to wait? The jxl-rs-polyfill brings JPEG XL to all browsers now.

One line of code:

<script src="https://cdn.jsdelivr.net/npm/jxl-rs-polyfill/dist/auto.js"></script>

That's it. All .jxl images work everywhere.

More usage examples โ†’

NPM:

npm install jxl-rs-polyfill
import { JXLPolyfill } from 'jxl-rs-polyfill';
const polyfill = new JXLPolyfill();
await polyfill.start();

Programmatic decoding:

import { decodeJxlToPng } from 'jxl-rs-polyfill';
const pngBytes = await decodeJxlToPng(jxlBytes);

โ†’ GitHub: jxl-rs-polyfill


Live Demo

JXL animation support โ€” no browser supports JXL animations natively yet, so this uses the WASM polyfill:

Animated JXL Demo


Acknowledgments

Special thanks to Luca Versari (veluca93) for reviewing PRs and managing jxl-rs releases. The collaboration from jxl-rs maintainers made this possible.


Resources: