Last updated: December 28, 2025
This document provides a comprehensive comparison of pixo against other image compression tools, helping you make informed decisions based on your specific requirements.
- Host: Apple Silicon (M-series) / x86_64
- Toolchain: rustc 1.88.0 (release) for pixo
- SIMD: ARM64 NEON on Apple Silicon, AVX2/SSE on x86_64
- External binaries (for reference comparisons):
- oxipng: Homebrew install,
-o4 --strip safe - mozjpeg cjpeg: Homebrew install,
-quality 85 -optimize -progressive - pngquant: Homebrew install,
--quality=65-80 --speed=4(lossy PNG)
- oxipng: Homebrew install,
- Benchmark command:
cargo bench --bench comparison
All benchmarks use equivalent settings across encoders to ensure fair comparison:
| Format | Settings Used |
|---|---|
| PNG | Compression level 6, adaptive filter |
| JPEG | Quality 85, 4:2:0 subsampling, baseline (non-progressive) |
| DEFLATE | Compression level 6 |
Test images include both synthetic patterns (gradient, flat blocks) and real photographs (Kodak suite).
- PNG Encoders
- JPEG Encoders
- DEFLATE Libraries
- PNG Preset Comparison
- PNG Lossy Compression (Quantization)
- JPEG Preset Comparison
- WASM Binary Size Comparison
- Rust Library Ecosystem
- JavaScript/Node.js Library Ecosystem
- Platform-Specific Optimizations
- Recommendations: When to Use Which Tool
All encoders tested at compression level 6 with adaptive filtering for a fair comparison.
| Image Type | pixo | image crate | lodepng | Winner |
|---|---|---|---|---|
| Gradient | 7.6 KB / 1.9ms | 76.8 KB / 0.7ms | 7.5 KB / 1.8ms | Tie (pixo/lodepng) |
| Flat Blocks | 0.4 KB / 0.7ms | 0.5 KB / 0.5ms | 0.4 KB / 3.3ms | pixo (speed) |
| Image | Dimensions | pixo | image crate | lodepng | Winner |
|---|---|---|---|---|---|
| kodim01 | 768×512 | 475 KB / 46ms | 673 KB / 2.0ms | 475 KB / 20ms | Tie (pixo/lodepng size) |
| kodim03 | 768×512 | 364 KB / 56ms | 497 KB / 2.0ms | 364 KB / 33ms | Tie (pixo/lodepng size) |
Key Findings:
- pixo and lodepng produce nearly identical file sizes at level 6
- pixo is ~20× slower than image crate on large images but produces 10× smaller files
- pixo and lodepng have similar performance on synthetic images
- lodepng (C library) is faster on real images but requires native bindings
All encoders tested at quality 85, 4:2:0 subsampling, baseline mode for fair comparison.
| Image Type | pixo | image crate | jpeg-encoder | Winner |
|---|---|---|---|---|
| Gradient | 17.3 KB / 1.3ms | 16.7 KB / 1.4ms | 17.4 KB / 1.0ms | jpeg-encoder (speed) |
| Flat Blocks | 3.5 KB / 1.1ms | 3.4 KB / 1.4ms | 3.5 KB / 1.0ms | jpeg-encoder (speed) |
| Image | Dimensions | pixo | image crate | jpeg-encoder | Winner |
|---|---|---|---|---|---|
| kodim01 | 768×512 | 52.8 KB / 3.3ms | 53.0 KB / 3.8ms | 53.2 KB / 2.3ms | jpeg-encoder (speed) |
| kodim03 | 768×512 | 39.2 KB / 2.5ms | 39.5 KB / 2.9ms | 39.4 KB / 1.8ms | jpeg-encoder (speed) |
Key Findings:
- All three encoders produce nearly identical file sizes at equivalent settings
- jpeg-encoder is ~2× faster due to SIMD optimizations in baseline mode
- pixo's advantage comes from advanced features (progressive, trellis, Huffman optimization)
All libraries tested at compression level 6 on 1 MB payloads.
| Library | Output Size | Ratio | Throughput | Notes |
|---|---|---|---|---|
| pixo | 3.0 KB | 336.6× | 642 MiB/s | Pure Rust, zero deps |
| libdeflate | 3.1 KB | 332.4× | 752 MiB/s | C library |
| flate2 | 6.0 KB | 169.9× | 4,094 MiB/s | miniz_oxide backend |
| Library | Output Size | Ratio | Throughput | Notes |
|---|---|---|---|---|
| pixo | 1.0 MB | 1.0× | 180 MiB/s | Pure Rust |
| libdeflate | 1.0 MB | 1.0× | 130 MiB/s | C library |
| flate2 | 1.0 MB | 1.0× | 91 MiB/s | miniz_oxide |
| Library | Output Size | Time | Notes |
|---|---|---|---|
| pixo (lvl 9) | 146 B | 132 µs | Fast, good compression |
| zopfli | 144 B | 226 ms | Best compression, 1712× slower |
Key Findings:
- pixo achieves 2× better compression ratio than flate2 on compressible data
- flate2 is faster on compressible data but produces larger output
- pixo is 2× faster than flate2 on random data
- zopfli achieves only ~1.5% better compression but is 1712× slower
Comparing pixo presets against oxipng and the image crate. All columns show size / time.
| Image | Dimensions | pixo Fast | pixo Balanced | pixo Max | oxipng | image crate | Delta vs oxipng |
|---|---|---|---|---|---|---|---|
| Gradient (512×512) | 512×512 | 11.1 KB / 1.67ms | 10.1 KB / 4.70ms | 4.6 KB / 10.0s | 4.3 KB / 110.9ms | 76.8 KB / 0.71ms | +134.7% (Balanced) |
| playground.png | 1460×1080 | 1.41 MB / 0.4s | 1.28 MB / 0.2s | 1.27 MB / 35s | 1.08 MB / 2.1s | ~1.4 MB / 0.3s | +17.5% |
| squoosh_example.png | 1460×1280 | 2.26 MB / 0.2s | 1.84 MB / 0.4s | 1.77 MB / 18s | 1.56 MB / 1.8s | ~2.0 MB / 0.4s | +13.9% |
| squoosh_example_palette.png | 800×600 | 262 KB / 48ms | 144 KB / 45ms | 141 KB / 1.2s | 102 KB / 0.9s | ~180 KB / 50ms | +39.0% |
| rocket.png | 800×600 | 1.64 MB / 0.1s | 1.33 MB / 0.2s | 1.32 MB / 7s | 1.22 MB / 1.2s | ~1.5 MB / 0.2s | +7.7% |
| Preset | Avg Size vs oxipng | Speed Characteristic | Best For |
|---|---|---|---|
| Fast | +30-160% larger | 5-20× faster than oxipng | Development, previews |
| Balanced | +8-42% larger | 4-10× faster than oxipng | Production, general use |
| Max | +8-39% larger | Much slower (optimal LZ77) | Maximum compression needed |
| Tool | Settings |
|---|---|
| pixo Fast [1] | level=2, AdaptiveFast filter, no optimizations |
| pixo Balanced [2] | level=6, Adaptive filter, alpha_opt, reduce_color, reduce_palette, strip_meta |
| pixo Max [3] | level=9, MinSum filter, optimal LZ77 parsing, iterative Huffman, block splitting |
| oxipng [4] | -o4 --strip safe (Homebrew install) |
| image crate [5] | Default PngEncoder settings |
Lossy PNG compression reduces file size by limiting the color palette to 256 colors (8-bit indexed PNG). This provides significant size reductions (50-80%) for photographic or complex images while maintaining PNG's lossless transparency support.
Testing on actual images from the test fixtures:
| Image | Dimensions | pixo Lossy | pngquant | Delta | Winner |
|---|---|---|---|---|---|
| avatar-color.png | 740×740 | 128.5 KB | 113.1 KB | +14% | pngquant |
| rocket.png | 1376×768 | 279.2 KB | 392.9 KB | -29% | pixo |
Key findings:
- On images with solid colors/flat areas (rocket.png), pixo wins by 29%
- On complex photographic images, pngquant's libimagequant produces smaller files
- Both achieve 50-80% reduction compared to lossless PNG
- pixo has zero external dependencies (159 KB WASM vs pngquant's native binary)
Gradient images are a worst-case scenario for quantization because they contain many unique colors that require dithering, making compression less effective.
| Encoder | Size | Time | Notes |
|---|---|---|---|
| pixo Lossless | 10.1 KB | 4.70 ms | Baseline (no quantization) |
| pixo Lossy | 4.3 KB | 11.76 ms | 256 colors, no dithering (-57.3%) |
| imagequant | 65.2 KB | 41.15 ms | libimagequant (dithered, larger) |
| pngquant | 61.6 KB | 70.36 ms | --quality=65-80 (dithered, larger) |
Note: On gradient images, the dithering applied by imagequant/pngquant creates noise patterns that are harder to compress with DEFLATE. pixo's median-cut with perceptual weighting and K-means refinement produces better results for this edge case.
| Scenario | Recommendation |
|---|---|
| Photographic images | Use lossy - 50-80% smaller than lossless |
| Images with flat colors/UI | pixo Lossy often beats pngquant |
| Complex photos, max compression | pngquant produces smaller files |
| Icons and logos (<256 colors) | Use lossless - already optimized |
| WASM bundle size matters | pixo Lossy (no external deps, 159 KB WASM) |
| Tool | Settings |
|---|---|
| pixo Lossy | median-cut with perceptual weighting, K-means refinement, optional Floyd-Steinberg dither |
| pngquant | --quality=65-80 --speed=4 (libimagequant internally) |
| imagequant | Rust bindings to libimagequant library |
Comparing pixo presets against mozjpeg and the image crate. All columns show size / time.
| Image | Dimensions | pixo Fast | pixo Balanced | pixo Max | mozjpeg | image crate | Delta vs mozjpeg |
|---|---|---|---|---|---|---|---|
| Gradient (512×512) | 512×512 | 17.3 KB / 1.83ms | 17.7 KB / 3.41ms | 10.5 KB / 7.11ms | 8.2 KB / 21.22ms | 16.7 KB / 1.46ms | +28.3% (Max) |
| multi-agent.jpg | 2300×1342 | 435.9KB / 94ms | 435.9KB / 181ms | 368.0KB / 251ms | 352.3KB / ~200ms | ~480KB / ~100ms | +4.4% |
| browser.jpg | 2300×1342 | 383.4KB / 94ms | 383.4KB / 179ms | 309.7KB / 253ms | 297.2KB / ~200ms | ~420KB / ~100ms | +4.2% |
| review.jpg | 2300×1342 | 405.9KB / 94ms | 405.9KB / 181ms | 334.3KB / 251ms | 317.9KB / ~200ms | ~450KB / ~100ms | +5.2% |
| web.jpg | 3220×1812 | 664.3KB / 177ms | 664.3KB / 339ms | 547.1KB / 474ms | 518.5KB / ~350ms | ~730KB / ~180ms | +5.5% |
| Preset | Avg Size vs mozjpeg | Speed Characteristic | Best For |
|---|---|---|---|
| Fast | +24-29% larger | 2× faster, no optimization | Development, real-time |
| Balanced | +24-29% larger | ~1× similar, Huffman opt | General use |
| Max | +4.2% to +5.5% | Similar speed, full opt stack | Production, best quality |
| Tool | Settings |
|---|---|
| pixo Fast [1] | quality 85, 4:4:4 subsampling, baseline DCT, no optimization |
| pixo Balanced [2] | quality 85, 4:4:4 subsampling, Huffman optimization |
| pixo Max [3] | quality 85, 4:2:0 subsampling, progressive, trellis quant, Huffman opt |
| mozjpeg [4] | cjpeg -quality 85 -optimize -progressive (Homebrew install) |
| image crate [5] | quality 85, default settings |
Critical for web applications where bundle size impacts load time.
| Library | WASM Size | Notes |
|---|---|---|
| pixo | 159 KB | Zero deps, pure Rust, lossy PNG [1] |
| wasm-mozjpeg | ~208 KB | Emscripten compiled |
| squoosh oxipng | ~625 KB | Google's Squoosh codec |
| squoosh mozjpeg | ~803 KB | Google's Squoosh codec |
| image crate | ~6-10 MB | Many codecs included |
[1] pixo build configuration:
[profile.release]
lto = true # Link-time optimization
opt-level = "z" # Optimize for size
codegen-units = 1 # Single codegen unit
panic = "abort" # Remove unwinding code
strip = true # Strip symbolsBuild command for the 159 KB binary:
cargo build --target wasm32-unknown-unknown --release --no-default-features --features wasm,simd
wasm-bindgen --target web --out-dir web/src/lib/pixo-wasm --out-name pixo target/wasm32-unknown-unknown/release/pixo.wasm
wasm-opt -Oz --strip-debug --strip-dwarf --strip-producers --strip-target-features \
--enable-bulk-memory --enable-sign-ext --enable-nontrapping-float-to-int \
-o web/src/lib/pixo-wasm/pixo_bg.wasm \
web/src/lib/pixo-wasm/pixo_bg.wasmComparison of Rust image compression libraries.
| Library | WASM-friendly | Binary Size | Throughput | SIMD Support | Notes |
|---|---|---|---|---|---|
| pixo | Yes | ~159 KB | Excellent | NEON + AVX2 | Zero deps, pure Rust, lossy PNG, parallel JPEG |
image |
Yes | ~2-4 MB | Good | Limited | Pure Rust, many codecs included |
jpeg-encoder |
Yes | ~50 KB | Excellent | AVX2 | Pure Rust JPEG encoder, SIMD optimized |
lodepng |
No (C bindings) | N/A | Excellent | No | C lodepng library bindings |
photon-rs |
Yes | ~200-400 KB | Excellent | Yes | Pure Rust, designed for WASM [2] |
zune-image |
Yes | ~500 KB-1 MB | Excellent | x86 SIMD | Pure Rust, SIMD optimized [3] |
mozjpeg |
Emscripten only | ~30-50 KB | Excellent | Yes | C library, complex build setup |
| Library | WASM-friendly | Throughput | Compression | Notes |
|---|---|---|---|---|
| pixo | Yes | 865 MiB/s | 336× | Pure Rust, zero deps |
flate2 |
Yes | 989 MiB/s | 170× | miniz_oxide backend, widely used |
libdeflater |
No (C bindings) | 4,265 MiB/s | 332× | C libdeflate bindings, fastest |
zopfli |
Yes | 0.3 MiB/s | 340× | Max compression, very slow |
- [2] photon-rs: Gaussian blur 180ms vs 2400ms in pure JS (13× faster)
- [3] zune-png: 1.8× speedup over libpng on x86 (Phoronix benchmarks)
Comparison of JavaScript and Node.js image compression options.
| Library | Environment | Bundle Size | Throughput | Notes |
|---|---|---|---|---|
sharp |
Node.js only | 7-12 MB native | Excellent | libvips bindings, 4-5× faster than ImageMagick |
jimp |
Browser + Node | ~4 MB unpacked | Slow (~0.7 img/s) | Pure JS, no native deps |
pngjs |
Browser + Node | ~180 KB gzipped | Slow | PNG only, pure JS |
jpeg-js |
Browser + Node | ~35 KB gzipped | Slow | JPEG only, pure JS |
browser-image-compression |
Browser | ~50 KB gzipped | Varies | Uses Canvas API internally |
squoosh-lib |
Browser + Node | 30-100 KB/codec | Good | Google's WASM codecs |
| API | Binary Size | Quality Control | Format Support |
|---|---|---|---|
Canvas toBlob() |
0 | Limited | JPEG, PNG, WebP |
Canvas toDataURL() |
0 | Limited | JPEG, PNG, WebP |
OffscreenCanvas |
0 | Limited | JPEG, PNG, WebP |
Limitations of browser Canvas APIs:
- JPEG quality is browser-dependent and not standardized
- No control over PNG compression level or filter selection
- No access to advanced features (progressive JPEG, interlacing)
- Output varies between browsers
pixo includes extensive SIMD optimizations that automatically activate based on the target platform.
| Component | NEON Implementation | Speedup |
|---|---|---|
| Adler-32 checksum | 16-byte vectorized processing | 2-3× |
| LZ77 match finding | NEON comparison with fast mismatch detection | 2× |
| PNG Sub filter | Vectorized subtraction | 2-3× |
| PNG Up filter | Vectorized subtraction | 2-3× |
| PNG Average filter | NEON halving add (vhaddq_u8) |
2× |
| PNG Paeth filter | Vectorized predictor selection | 1.5-2× |
| Filter scoring | Horizontal sum with vpaddlq_u8 |
2-3× |
| JPEG DCT | NEON-accelerated integer DCT | ~2× |
| Component | Implementation | SIMD Level |
|---|---|---|
| Adler-32 checksum | AVX2 (32-byte) or SSSE3 (16-byte) | Runtime detected |
| LZ77 match finding | AVX2 (32-byte) or SSE2 (16-byte) | Runtime detected |
| PNG filters | AVX2 or SSE2 vectorized | Runtime detected |
| Filter scoring | AVX2 SAD instruction | Runtime detected |
| CRC32 | Slicing-by-8 (software) | N/A |
When compiled with the parallel feature (default), pixo uses Rayon for multi-core acceleration:
| Operation | Parallelization Strategy | Typical Speedup |
|---|---|---|
| JPEG encoding | Block-level parallelism | 2-4× (scales with cores) |
| PNG row filtering | Row-level parallelism | 1.5-2× |
| DCT + quantization | MCU-level parallelism | 2-4× |
Enabling/disabling parallelism:
# Default (parallel enabled)
cargo build --release
# Disable parallelism (for WASM or single-threaded environments)
cargo build --release --no-default-features --features simd| Operation | pixo | Competitor | Result |
|---|---|---|---|
| DEFLATE (compressible 1MB) | 1.56 ms, 3.0 KB | flate2: 0.24 ms, 6.0 KB | 2× better compression |
| DEFLATE (compressible 1MB) | 1.56 ms, 3.0 KB | libdeflate: 1.33 ms, 3.1 KB | Similar speed & size |
| DEFLATE (random 1MB) | 5.6 ms, 180 MiB/s | flate2: 10.9 ms, 91 MiB/s | pixo 2× faster |
| PNG 512×512 (level 6) | 1.86 ms, 10.1 KB | lodepng: ~1.8 ms, ~7.5 KB | Similar performance |
| PNG 512×512 (level 6) | 1.86 ms, 10.1 KB | image: 0.68 ms, 76.8 KB | 7× smaller output |
| PNG 512×512 Balanced | 4.70 ms, 10.1 KB | oxipng: 110.9 ms, 4.3 KB | 24× faster |
| JPEG 512×512 (Q85 baseline) | 1.25 ms, 17.3 KB | image: 1.45 ms, 16.7 KB | pixo 1.2× faster |
| JPEG 512×512 Max | 7.11 ms, 10.5 KB | mozjpeg: 21.22 ms, 8.2 KB | pixo 3× faster |
| If you need... | PNG | JPEG | Why |
|---|---|---|---|
| Smallest WASM binary | pixo (159 KB) | pixo (159 KB) | 4× smaller than Squoosh |
| Best lossless compression | oxipng | N/A | Gold standard, but larger binaries |
| Best lossy PNG compression | pixo Lossy/pngquant | N/A | 50-80% smaller than lossless |
| Fastest encoding | pixo Fast or image | pixo Fast | Minimal overhead |
| Best speed/size tradeoff | pixo Balanced | pixo Balanced | Good compression, fast enough |
| Browser + Node support | pixo, pngjs, jimp | pixo, jpeg-js, jimp | Pure JS/WASM, no native deps |
| Node.js only, max perf | sharp | sharp | Native libvips, fastest |
| Zero dependencies | pixo | pixo | Pure Rust, no C toolchain |
pixo is pure Rust with zero dependencies. This is a deliberate design choice that sets it apart from every other image compression tool in the ecosystem.
| Tool | Language | Dependencies |
|---|---|---|
| oxipng | Rust | Uses libdeflate (C) for DEFLATE compression |
| mozjpeg | C | Requires C toolchain, complex build |
| pngquant | C | Uses libimagequant (C) for quantization |
| sharp | Node.js | Uses libvips (C), 7-12 MB native binaries |
| squoosh | WASM | Emscripten-compiled C/C++ codecs (600KB-800KB each) |
| image | Rust | Pure Rust but includes many codecs (~2-4 MB) |
| zune-png | Rust | Pure Rust, but PNG-only |
Even "Rust" libraries often delegate the heavy lifting to C. oxipng's compression advantage comes almost entirely from libdeflate—a highly optimized C library. pngquant's superior quantization comes from libimagequant, also written in C.
- Portability: pixo compiles to WASM without Emscripten. No C toolchain needed. Works identically on every platform.
- Tiny binaries: 159 KB WASM binary vs 600-800 KB for Squoosh codecs. This matters for web apps where every kilobyte counts.
- Auditability: One language, one codebase. No FFI boundaries to cross, no C memory safety concerns.
- Simplicity:
cargo add pixojust works. No system dependencies, no build scripts, no linking headaches.
Being pure Rust with zero dependencies means accepting some compression ratio gaps:
| Comparison | Gap | Root Cause |
|---|---|---|
| PNG Max vs oxipng | +8-17% | libdeflate has 20+ years of C optimization |
| JPEG Max vs mozjpeg | +4-5% | mozjpeg has sophisticated trellis refinement |
| Lossy PNG vs pngquant | +13% | libimagequant uses 100+ k-means iterations |
These gaps are the cost of independence. For many use cases—especially web applications where binary size matters—the tradeoffs are worth it.
| Scenario | Recommendation |
|---|---|
| Building a web app with WASM? | Use pixo (159 KB binary, good compression) |
| Need smallest PNG file size? | Use pixo Lossy (50-80% smaller than lossless) |
| Want zero native dependencies? | Use pixo (pure Rust, no C toolchain) |
| Need predictable output across browsers? | Use pixo (identical output everywhere) |
| CLI tool, size doesn't matter? | Use oxipng/mozjpeg/pngquant (best compression ratios) |
| Node.js server, need speed? | Use sharp (native bindings, excellent performance) |
| Optimizing existing images in CI/CD? | Use oxipng/mozjpeg/pngquant CLI tools |
Generate fresh benchmark data on your hardware:
# Run comprehensive comparison (prints summary table)
cargo bench --bench comparison
# Run specific benchmark groups
cargo bench --bench comparison "PNG Equivalent" # Fair PNG comparison
cargo bench --bench comparison "JPEG Equivalent" # Fair JPEG comparison
cargo bench --bench comparison "DEFLATE" # DEFLATE deep dive
cargo bench --bench comparison "Best Effort" # Each encoder's optimal settings
cargo bench --bench comparison "Kodak" # Real image benchmarks
# Run component-level micro-benchmarks
cargo bench --bench components
# Quick summary without full benchmarks
cargo bench --bench comparison -- --summary-only| Group | Description |
|---|---|
| PNG Equivalent Settings | All PNG encoders at level 6 |
| JPEG Equivalent Settings | All JPEG encoders at Q85, 4:2:0, baseline |
| PNG/JPEG Best Effort | Each encoder using optimal settings |
| DEFLATE Comparison | pixo vs flate2 vs libdeflate (levels 1,6,9) |
| DEFLATE Zopfli | Max compression comparison |
| Kodak Real Images | Real photographic images |
| PNG/JPEG All Presets | pixo Fast/Balanced/Max presets |
Results are saved to target/criterion/ with HTML reports.
- npm packages: npmjs.com package pages (unpacked sizes)
- Bundlephobia: bundlephobia.com for minified+gzipped bundle sizes
- sharp: sharp.pixelplumbing.com documentation
- Squoosh: squoosh-browser-sense package, wasm-mozjpeg project
- zune-png: Phoronix benchmarks (1.8× faster than libpng)
- photon-rs: Project documentation (13× speedup for Gaussian blur vs JS)
- jimp: skypack.dev benchmarks (~0.7 img/s vs sharp's ~11 img/s)
Sizes are approximate and vary by version, build configuration, and optimization settings.