Skip to content

JPEG XL

currentMigrated from docs/images/JXL.mdx at 37c3317fd058; modified for Encode Wiki · CC BY-SA 4.0

JPEG XL (JXL) is the JPEG committee’s modern image format (ISO/IEC 18181). It does lossy and lossless coding well, handles HDR, wide gamut, high bit depth, alpha, layers and animation, decodes quickly, and is the only major format that can recompress an existing JPEG losslessly and give you the identical JPEG back. Browser support was the long-standing blocker; as of October 2026 that is changing quickly (see below).

Use it for archives and photo libraries (especially existing JPEG collections), for lossless images, for high-fidelity delivery, and anywhere you control the decoder. For lowest-bitrate web delivery, AVIF usually still produces smaller files.

Strengths

  • Lossless JPEG recompression. cjxl photo.jpg photo.jxl shrinks a typical JPEG by about 20 % and djxl photo.jxl photo.jpg restores the original file byte for byte.
  • Strong lossless mode, usually well below PNG and lossless WebP on photographs, and it handles 16-bit properly.
  • High-fidelity lossy. It is at its best at medium-to-high quality (distance 0.5–2), with graceful behaviour across a wide quality range.
  • Fast decode, and encoding that scales with effort from near-JPEG speed to very slow.
  • Progressive decoding, up to 32 bits per channel, HDR/wide gamut, many channels, tiled large images (over a billion pixels per side), layers and animation. Royalty-free.

Weaknesses

  • Browser and application support is newer and patchier than for AVIF or WebP.
  • At low bitrates (aggressive web compression) the best AVIF encodes are generally smaller and cleaner.
  • Encoder tooling is essentially one implementation, libjxl. Its CLI and API still change between releases.
PlatformStatus as of 2026-10-08
SafariStill images since Safari 17; animated JXL is not supported.
ChromeDecoding is on by default from Chrome 155 (stable release scheduled 2026-10-06), using the Rust decoder jxl-rs. Chrome 145–154 had it behind #enable-jxl-image-format.
FirefoxNightly only until Firefox 158, which enables it by default (stable scheduled 2026-10-13), also built on jxl-rs.
Edge, Opera, other ChromiumFollow their Chromium version, so expect support to spread over the following weeks.

Sources: Safari 17 notes, Chrome 155 beta and release notes, MDN’s JPEG XL data, Firefox 158 beta release notes and Mozilla’s intent to ship. Until Chrome 155 and Firefox 158 are widely installed, serve JXL through <picture> with an AVIF/WebP/JPEG fallback; image and audio support has the markup and a detection snippet.

cjxl and djxl come from libjxl; most distributions package them.

Terminal window
cjxl input.png output.jxl -d 1

-d 1 and effort 7 are the defaults for PNG input. The two settings that matter:

  • Distance (-d) is the lossy quality target in roughly just-noticeable-difference units. -d 1 is the default and is meant to be visually lossless at normal viewing; -d 0 is mathematically lossless; larger values are smaller and lossier (range 0–25, sensible range 0.5–3). -q is a JPEG-like alternative scale (-q 90 ≈ -d 1); use one or the other.
  • Effort (-e) is encoder time versus density, 1–10, default 7. Higher effort mostly shrinks files slightly and makes the achieved quality more accurate; -e 9–10 are worth it for final lossless archives and rarely for lossy delivery.
TaskCommand
Lossy, visually losslesscjxl in.png out.jxl -d 1
Smaller web deliverycjxl in.png out.jxl -d 2
Losslesscjxl in.png out.jxl -d 0 -e 9
Lossless JPEG recompressioncjxl in.jpg out.jxl
Fresh lossy encode from a JPEGcjxl in.jpg out.jxl -j 0 -d 1
Progressive loadingcjxl in.png out.jxl -d 1 -p
Photo-like grain on smooth imagescjxl in.png out.jxl -d 1 --photon_noise_iso=800
Animation from GIF/APNGcjxl in.gif out.jxl
Decode to PNGdjxl in.jxl out.png
Restore original JPEGdjxl in.jxl out.jpg

A JPEG input is recompressed losslessly by default (--lossless_jpeg=1), keeping the reconstruction data so djxl can emit the original bytes. That is exactly what you want for archiving. If you instead want a new lossy JXL derived from the decoded pixels, pass -j 0 and a distance. Do not pass -d 0 -j 0 to a JPEG and expect the recompression: that is a lossless encode of the decoded pixels, and the file ends up several times larger than the JPEG.

Going the other way, djxl in.jxl out.jpg restores the original JPEG when the reconstruction data is present. djxl -j (--pixels_to_jpeg) forces a decode to pixels and a new, lossy JPEG encode, which is not a restore. Stripping metadata with -x strip=exif makes reconstruction impossible, and cjxl then requires --allow_jpeg_reconstruction=0.

OptionUse
-m 0/1Force VarDCT (0) or modular (1) coding; by default the encoder chooses.
-a DISTANCEAlpha distance; default 0 (lossless alpha).
--keep_invisible=1Keep RGB hidden under fully transparent pixels. It is on for lossless and off for lossy by default.
-p, --progressive_dc=0..2More progressive/responsive decoding; --progressive_dc=1 adds an extra low-resolution pass (2 adds two).
--faster_decoding=0..4Trade some density for lower decode cost on weak devices.
--photon_noise_iso=NSynthesise film-like noise instead of encoding real grain (100 is a little, 3200 a lot).
-x color_space=…Tell cjxl what colour space an untagged raw input is, or override one (sRGB, DisplayP3, Rec2100PQ, …).
--num_threadsWorker threads; default is the machine’s.
--container=1Force the ISO container; usually only needed for metadata boxes.

Raw-pixel inputs (PPM, PFM and similar) carry no colour information; supply it with -x color_space=… or the output will be interpreted as sRGB. Animation inputs (GIF, APNG) are written as JXL animations with their frame timing; GIF input defaults to lossless (-d 0).

  • Against JPEG: clearly smaller at the same quality, and recompressing an existing JPEG saves about 20 % with no loss at all. jpegli narrows the gap for new lossy JPEGs.
  • Against AVIF: AVIF is generally smaller at web-delivery quality; JXL encodes faster, decodes cheaply, holds up better at high quality and is much better in lossless.
  • Against WebP: JXL wins in lossy at medium-to-high quality and in lossless, has no 8-bit/4:2:0 limits, and WebP has the support advantage.

For your own comparisons, SSIMULACRA2 and Butteraugli are the useful metrics, but libjxl is itself tuned with Butteraugli, so look at the images at 1:1 as well.

  • -d and -q are mutually exclusive.
  • -e 10 is far slower than -e 9 for little gain; on camera-resolution images it is only worth trying for lossless archives.
  • Check that the people who need to open the files have a JXL-capable viewer before converting a whole library; browser support is new and desktop and mobile apps vary.
  • Re-encoding lossy JXL or lossy JPEG output with a lossy encoder compounds loss; keep sources.
  • libjxl-tiny is a stripped-down encoder for photographic images meant to guide hardware implementations.
  • Hydrium is a fast, very low-memory streaming encoder in C.
  • zune-jpegxl is a simple Rust encoder with lossless 8- and 16-bit output.
  • jxl-oxide and jxl-rs are spec-conforming Rust decoders; jxl-rs is what Chrome and Firefox use.
  • JPEG is what JXL most directly replaces, and what it can losslessly absorb.
  • AVIF is the AV1-based alternative with better low-bitrate compression.
  • WebP is the older widely supported web format.
  • PNG is what lossless JXL most often replaces.

The table below is the full cjxl parameter reference for libjxl 0.12.0. Reading option tables explains the Default column.

INPUT to —dec-hints
Parameter / aliasesType or accepted valuesDefaultPurpose and constraints
INPUTpathrequiredSource image: JXL, PNG/APNG, GIF, JPEG, PNM/PFM/PAM or PGX.
OUTPUTpathrequired unless —disable_outputOutput .jxl path; an existing file is overwritten.
--distance, -dfloat 0–250 for JPEG/GIF input; 1 otherwiseQuality target in JND units; 0 is lossless, 1 is visually lossless. Excludes —quality.
--quality, -qfloat 0–100unset; input-dependent distance appliesLibjpeg-like quality mapped to distance; 100 is lossless, 90 is visually lossless.
--effort, -einteger 1–10; 11 with expert opt-in7Encoder effort; higher is slower and usually smaller. 11 needs —allow_expert_options.
--version, -Vactionnot requestedPrint the encoder and library version, then exit.
--quietflagfalseSuppress progress and size messages.
--verbose, -vrepeatable flag0 repetitionsMore output; repeat up to four times (-v -v -v -v) to also list advanced options in —help.
--alpha_distance, -afloat 0–250Distance for the alpha channel, 0–25; 0 keeps alpha lossless.
--progressive, -pflagfalseShortcut for progressive_ac, progressive_dc=1, center-first group order, no patches, and Squeeze.
--group_order0 or 1automatic: normally 0; progressive selects 1Codestream group order: 0 scanline, 1 center-first. Center-first suits progressive loading.
--container0 or 1automatic when metadata/reconstruction requires it1 forces the ISOBMFF container; 0 uses it only when metadata or JPEG reconstruction data needs it.
--compress_boxes0 or 11Brotli-compress metadata boxes when transcoding JPEG; 0 stores them uncompressed.
--brotli_effortinteger 0–119Brotli effort for metadata boxes, 0–11; higher is smaller but slower.
--modular, -m0 or 1encoder chooses1 forces Modular mode (lossless, graphics); 0 forces VarDCT (photos).
--lossless_jpeg, -j0 or 11 for JPEG input; disabled for other inputs1 transcodes JPEG input losslessly; set 0 to decode to pixels, required for lossy -d or -q on JPEG input.
--num_threadsinteger -1 or nonnegative-1: machine defaultWorker threads; 0 disables multithreading and -1 uses all cores.
--photon_noise_isononnegative float0Add film-like noise at this ISO (100 light, 3200 heavy); the output differs from the input.
--intensity_targetpositive float, nits0: choose from color encodingPeak display brightness in nits for HDR content; an explicit 0 is rejected, so omit it for automatic.
--dec-hints, -xKEY=VALUE, repeatablenoneSet or strip metadata, e.g. -x color_space=sRGB, -x icc_pathname=FILE, -x strip=exif; repeatable.
—allow_jpeg_reconstruction to —jpeg_reconstruction_cfl
Parameter / aliasesType or accepted valuesDefaultPurpose and constraints
--allow_jpeg_reconstruction0 or 11Store data to restore the original JPEG bytes; 0 gives a smaller file that cannot restore them.
--codestream_level-1, 5 or 10-1: encoder choosesLimit the codestream to level 5 (smaller limits, broadest decoder support) or 10; -1 chooses automatically.
--bufferinginteger -1–3-1: encoder choosesInput buffering: 0 whole image (best compression), 1–2 streamed; use —output_mode instead of 3.
--faster_decodinginteger 0–40Trade size or quality for faster decoding, 0 (off) to 4.
--premultiply-1, 0 or 1-1: preserve input associationForce alpha to straight (0) or premultiplied (1); -1 keeps the input’s.
--keep_invisible0 or 11 for lossless; 0 for lossy1 keeps RGB values under fully transparent pixels; 0 lets the encoder alter them for size.
--center_x-1 or valid x coordinate-1: image midpointHorizontal center for center-first group order; needs —group_order=1 or —progressive.
--center_y-1 or valid y coordinate-1: image midpointVertical center for center-first group order; needs —group_order=1 or —progressive.
--progressive_acflagfalseSplit AC coefficients into several passes so the image refines while loading.
--qprogressive_acflagfalseProgressive AC passes using shifted quantization instead of coefficient splitting.
--progressive_dcinteger -1–2-1: encoder choosesExtra low-resolution DC passes: 0 off, 1 adds a 64x64 pass, 2 adds 512x512 and 64x64.
--resampling-1, 1, 2, 4 or 8-1: input/quality-dependentDownsample color channels by 1, 2, 4 or 8; -1 does so only at very low quality.
--ec_resampling-1, 1, 2, 4 or 8-1: encoder choosesDownsampling factor for extra channels such as alpha; same values as —resampling.
--already_downsampledflagfalseInput is already downsampled; skip encoder downsampling but tell the decoder to upsample.
--upsampling_mode-1, 0 or 1-1: nonseparableDecoder upsampling: -1 smooth, 0 nearest neighbor (pixel art), 1 pixel dots at 4x and 8x.
--epfinteger -1–3-1: encoder choosesDecoder edge-preserving filter strength, 0 (off) to 3; -1 lets the encoder choose.
--gaborish0 or 1encoder choosesEnable (1) or disable (0) the gaborish smoothing filter; unset lets the encoder choose.
--override_bitdepthunsigned integer0: input depthBit depth to write; 0 uses the input’s depth.
--noise0 or 1encoder choosesEnable or disable the encoder’s adaptive noise synthesis; prefer —photon_noise_iso.
--jpeg_reconstruction_cfl0 or 1encoder choosesEnable or disable chroma-from-luma when transcoding JPEG; unset lets the encoder choose.
—num_reps to —responsive
Parameter / aliasesType or accepted valuesDefaultPurpose and constraints
--num_repspositive integer1Encode this many times, for benchmarking.
--streaming_inputflagfalseRead PPM or PGM input incrementally to reduce memory use.
--streaming_outputflagfalseWrite output through a callback as it is produced instead of buffering the whole file.
--output_modeinteger -1–2-1: encoder choosesOutput layout: 0 buffered, 1 streamed with seeking, 2 out-of-order jxlp boxes without seeking.
--disable_outputflagfalseEncode without writing a file, for timing.
--dots0 or 1encoder choosesEnable or disable dot detection and coding; unset lets the encoder choose.
--patches0 or 1encoder choosesEnable or disable repeated-patch detection; unset lets the encoder choose.
--frame_indexing0/1 index stringempty: no explicit index selectionBit string marking which frames go in the frame index box, e.g. 1011.
--allow_expert_optionsflagfalsePermit —effort=11, which is much slower for slightly denser lossless output.
--disable_perceptual_optimizationsflagfalseTurn off the encoder’s perceptual heuristics; listed only at help verbosity 4.
--iterations, -Ifloat -1–100-1: encoder choosesPercent of pixels used to learn modular MA trees; 0 disables the trees.
--modular_colorspace, -Cinteger -1–41-1: encoder choosesModular color transform: 0 none, 1–41 fixed reversible transform (6 is YCoCg).
--modular_group_size, -ginteger -1–3-1: encoder choosesModular group size: 0 128, 1 256, 2 512, 3 1024 pixels per side.
--modular_predictor, -Pinteger -1–15-1: normally 14 below effort 10, 15 at effort 10Modular predictor 0–15 (5 gradient, 6 weighted, 14 mixes 5 and 6, 15 mixes all).
--modular_nb_prev_channels, -Einteger -1–11-1: encoder choosesMaximum previous channels used as MA tree properties, 0–11.
--modular_palette_colorsinteger -1 or nonnegative-1: encoder choosesUse a palette when the image has at most this many colors; 0 skips the palette search.
--modular_lossy_paletteflagfalseUse lossy delta palette; combine with —modular_palette_colors=0. Ignored in progressive mode.
--pre-compact, -Xfloat -1–100-1: encoder choosesUse a global channel palette when sample values are below this percent of the range.
--post-compact, -Yfloat -1–100-1: encoder choosesUse per-group channel palettes when sample values are below this percent of the range.
--responsive, -R-1, 0 or 1-1: normally 0 lossless, 1 lossy; progressive selects 1Apply the Squeeze transform for resolution-progressive decoding; default 0 lossless, 1 lossy.
—help and —
Parameter / aliasesType or accepted valuesDefaultPurpose and constraints
--help, -hactionnot requestedPrint help; add -v for advanced options (up to four times).
--separatornot usedEnd of options; later arguments are file paths even if they start with a dash.