Same JPEG XL. 1.8×–4.5× faster to write. 3.5×–5.0× faster to read.
JPEG XL is the image standard that stores map tiles exactly at 8, 16 and 32 bits: elevation, radar, multispectral, masks. TurboJXL writes and reads the same files as the reference library, faster, in memory-safe code, and every file it writes opens in any JPEG XL reader.
Swaps in for libjxl. Native on servers, and in the browser too.
Per core, our effort against libjxl's same effort, on Intel Core Ultra 9 · Windows · AVX2. Geometric means over 256² to 1024² tiles.
The 60-second version
Map tiles are written once and read millions of times.
JPEG XL
The ISO image standard after JPEG. It keeps every value exactly at 8, 16 and 32 bits, which is why GDAL and Cloud-Optimized GeoTIFFs support it for scientific imagery.
libjxl
The reference library, written in C++. Every JPEG XL pipeline links it today, so it is the bar any replacement has to clear, on the same files.
Write once, read always
A tile pyramid is built once from each new image, then read on every map request. Build time is compute you pay for once; read speed is servers you pay for every month.
Faster both ways
TurboJXL writes and reads the same files, faster in both directions, in memory-safe code. No migration: the files stay standard JPEG XL.
Watch it
Race it yourself, in this tab.
The same decoder compiles to WebAssembly with no fork of libjxl in the loop. Race TurboJXL against libjxl's WebAssembly build on the same tiles in your own browser, geospatial types at 256² and 512², lossless or visually lossless, and see the real per-tile decode on your machine.
What it means for the bill
Faster builds, fewer servers, no migration.
Codec time is machine time. Here is the pooled result, translated.
Illustrative, from the measured Intel figures. A pilot measures yours.
Already at work
The JPEG XL inside Spatial Engine.
Spatial Engine, our imagery engine, converts and reprojects imagery, and TurboJXL is the JPEG XL inside it. Writing lossless JPEG XL at the same setting GDAL uses, it finishes several times sooner in a fraction of the memory. And JPEG XL stores the elevation model in the smallest file of any codec measured on it.
See Spatial EngineSpatial Engine against GDAL 3.13.3, lossless JPEG XL at effort 5 on both sides, median over the whole test set on the same machine; Windows leads. Our files are 3–5% larger than GDAL's at that effort, and the Mac reprojection figure is an upper bound. Size: USGS 3DEP 1 m DEM.
The full board
Every data type, every tile size, both machines.
676 tiles of real geospatial imagery from 256² to 2048², every one round-tripped lossless, written and read by both libraries at the same setting, one core each. Switch machines; the charts below follow.
Write
Faster than libjxl at its own setting, on every data type.
Single-band radar and quicklooks gain the most, 4.1×–4.5×; three-band imagery runs 1.8×–2.0×. Float elevation is the one data type near parity on writing, 1.0×, and it is the one where the file comes out 43% smaller instead.
Encode speedup vs cjxl -e3 · per core
Intel Core Ultra 9 · Windows · AVX2, one thread on both sides. Our effort 3 against cjxl -d 0 -e 3. In-process
encode calls, cjxl's own --num_reps figure, median of 3 for ours. Geometric mean of
per-tile ratios per class and size; every tile lossless.
Encode speedup vs cjxl -e7 · per core
Effort 7 against effort 7, the densest setting on both sides. Sizes at this effort are within 9% of cjxl's per class, see below.
Encode throughput at 512² · default effort · MP/s per core
Absolute throughput on the tile size pyramids are built from. Megapixels per second of source pixels, one core.
Read
Faster to read: on its own files, and on libjxl's reader too.
Two questions, kept apart. What a user sees: TurboJXL reading the tile it wrote, against libjxl reading the tile libjxl wrote, 3.5×–5.0× faster. And the reader alone: libjxl reading our file, against our reader on the same file, 1.6×–2.7× in our favour at 256² to 1024².
Decode · pipeline view · default effort · per core
Each side decodes the tile its own encoder produced at effort 3. Planar output for us (the raster API), djxl's own --num_reps figure with output disabled.
Decode · identical bytes · default effort · per core
Both decoders read our effort-3 stream. In 3 of 28 cells djxl is ahead on our bytes (AI masks at 2048², Hyperspectral at 2048², Elevation (DSM) at 2048², down to 0.84×); they are on the chart.
Decode throughput at 512² · default effort · MP/s per core
The three columns behind the two views, at 512². Megapixels per second of decoded pixels, one core.
Size
Close to libjxl's size at its densest. Much faster at its default.
At effort 7, the densest setting, every data type lands within 9% of libjxl's file, complex SAR 16% under it, and writing is still 1.4×–3.1× faster. The default is built for the tile server: it never trades read speed for size, so it spends some bytes on masks and RGB against libjxl's default and buys 3.5×–5.0× faster reads on every request. Complex SAR and float elevation come out 22% and 43% smaller even there.
Size at effort 7 · ours / cjxl -e7
Geometric mean over 256² to 2048². Size does not depend on the thread count, and the AVX2 and NEON boards agree on every ratio here. On the single-band classes efforts 5 and 7 emit the same bytes; 5 is the cheaper setting to reach them.
The default · what the bytes buy
Default effort against cjxl -e3, per core, 256² to 1024².
| Class | Bytes | Decode | Encode |
|---|---|---|---|
| AI masks8-bit · 1 band | +30% | 5.0× | 2.6× |
| SAR quicklook8-bit · 1 band | +9% | 4.5× | 4.5× |
| RGB orthophoto8-bit · 3 bands | +30% | 3.6× | 2.0× |
| SAR detected16-bit · 1 band | +2% | 4.8× | 4.1× |
| Hyperspectral16-bit · 3 bands | +6% | 3.6× | 1.9× |
| SAR complex16-bit · 3 bands | −22% | 3.5× | 1.8× |
| Elevation (DSM)32-bit float · 1 band | −43% | 4.1× | 1.0× |
Bytes are ours over cjxl -e3; decode is our tile against the reference pipeline; encode is per core at the same effort. A tile is encoded once and decoded on every request.
Drops into the pipeline
A library swap. Not a migration.
Same files
Bit-exact both ways with libjxl 0.12.0: our files open exactly in libjxl, and libjxl's open exactly in TurboJXL. GDAL, Pillow and every JPEG XL reader take the output unchanged.
Memory-safe
The parser rules out the buffer-overflow, use-after-free and out-of-bounds bugs that C and C++ image decoders keep shipping, at compile time rather than by audit.
Scales across tiles
Every figure here is per core, because a pyramid is thousands of independent tiles: a server with N cores keeps N in flight, and each one runs at the speed shown.
For the engineers
- - Every raster type through one API: 8, 16 and 32-bit float samples, one band or many, planar in and planar out, so a nodata mask, a SAR band and an elevation surface take the same call.
- - One effort knob, 1 to 9, on the same scale as
cjxl -e, on every surface: the native API, the C API, and the prebuilt library. Efforts 1 to 4 are the fast default; 5 and up learn a context tree and ship it only when it is at least 2% smaller than the default's stream. - - Interop is gated at release against libjxl 0.12.0 in both directions, on AVX2 and NEON, and on the ISO conformance corpus. 784 of 784 default and effort-7 streams from the single-band corpus decode to exact pixels in djxl 0.12.0 (checked at the v1.2.9 tag).
- - Three surfaces: the native API, the
turbojxl.hC API, and a prebuilt library that also exports the libjxl decoder ABI (JxlDecoder*) for existing integrations. - - No C toolchain, no libjxl runtime dependency, no cmake on customer build farms or CI. AVX2, NEON and WASM (relaxed-simd) from one source.
- - ISAs: AVX2 (x86-64), NEON (ARM64), and WASM (relaxed-simd), one source, three SIMD backends; every chart above switches between the Intel AVX2 board and the Apple M3 Max NEON board, same protocol, same corpus.
- - One process-wide thread pool;
TJXL_THREADS=1keeps each encode or decode on the thread that called it, for pipelines that parallelise across tiles.
How this was measured
- Machines: Intel Core Ultra 9 · Windows · AVX2 and Apple M3 Max · macOS · NEON. Measured on TurboJXL v1.2.10 (2026-09-03/04) against libjxl 0.12.0.
- Our effort N against cjxl and djxl at effort N, both in-process, one thread each. 676 tiles of the Bitruvius labeled corpus from 256² to 2048², every one round-tripped lossless. Headlines are geometric means over 256² to 1024².
- Decode is reported two ways and never merged: each library reading the file it wrote, and libjxl reading our file. Size is ours over cjxl's at the same effort; below 1.0 is a smaller file.
- The live race decodes in your browser against libjxl's published WebAssembly build, so its numbers are yours, not ours.
Bring five of your tile sets.
A pilot runs both libraries on your own imagery, on your own machine: same files in, same JPEG XL out. You keep the numbers.
TurboJXL is licensed through the Bitruvius Developer Hub and ships as compiled binaries.
JPEG XL is an ISO/IEC standard (18181). libjxl, cjxl, and djxl are the reference implementation, published by the JPEG XL Project / Google under a BSD-3-Clause license. References here identify the products and formats being compared; no affiliation, sponsorship, or endorsement is implied. TurboJXL is an independent, memory-safe implementation that interoperates with the standard JPEG XL bitstream. The live demo runs the unmodified libjxl decoder under its BSD-3-Clause license. GDAL is an open-source project, named solely to identify the software compared; the elevation sample is USGS 3D Elevation Program data. Apple, Intel, Intel Core and Windows are trademarks of their respective owners and identify the test machines. Performance comparisons reflect our own measurements under the stated methodology; results vary by workload and hardware. Full third-party notices: Attributions.