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?
- Safari shipped JPEG XL — Apple implemented the format
- Firefox updated their position — Mozilla signaled support pending a Rust decoder
- PDF standardization — JPEG XL designated as the preferred format for HDR in PDF
- Developer demand — Bug upvotes, Interop proposals, and survey data
Why JPEG XL?
- 30-50% better compression than JPEG at equivalent quality
- Lossless JPEG transcoding with ~20% size reduction
- Progressive decoding — images load incrementally
- Modern capabilities — HDR, wide gamut, alpha, animations
- Cross-browser momentum — Safari ships it, Firefox is working on it
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:
- MERGED CL 7320482 — Add JXL infrastructure: enums and build flag
- MERGED CL 7319379 — Add JXL image decoder using jxl-rs
- 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:
- MERGED Roll jxl 0.2.1 → 0.2.2
- MERGED Roll jxl 0.2.0 → 0.2.1
- MERGED Roll jxl 0.1.5 → 0.2.0
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.
- Auto-detection — skips polyfill in Safari 17+ (native support)
- Zero config — just include the script
- Full coverage —
<img>, CSS backgrounds,<picture>, SVG - ~540KB gzipped — compact WASM module
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);
Live Demo
JXL animation support — no browser supports JXL animations natively yet, so this uses the WASM polyfill:
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: