Format Guides
Real numbers from a single 1.2 MB source image, so the trade-offs are concrete:
| Operation | Result |
|---|---|
losslessCompressPng (PNG → PNG) |
1.2M → 876K |
pngQuantize({ maxQuality: 75 }) (PNG → PNG) |
1.2M → 244K |
webpLossless() (PNG → WebP) |
1.2M → 676K |
webp(75) (PNG → WebP) |
1.2M → 84K |
avif({ quality: 100 }) (PNG → AVIF) |
1.2M → 584K |
avif({ quality: 75, chromaSubsampling: Yuv420 }) |
1.2M → 112K |
And for JPEG, on a 192 KB source:
| Operation | Result |
|---|---|
compressJpeg() |
192K → 184K |
compressJpeg({ quality: 75 }) |
192K → 104K |
WebP
Two modes:
- Lossy —
webp(qualityFactor), 0–100. The single most effective "make my photos small" lever.75is a great default;84Kfrom a1.2MPNG above. - Lossless —
webpLossless(). Use for graphics/screenshots where you can't tolerate artifacts but still want better-than-PNG sizes.
ts
await new Transformer(png).webp(75) // lossy
await new Transformer(png).webpLossless() // lossless
AVIF
The smallest files at a given quality, at the cost of CPU. Tune with AvifConfig:
quality(0–100, 100 = lossless).75is a strong web default.chromaSubsampling— defaults toYuv444(full chroma). Switch toYuv420for photos to roughly halve the file again (584K → 112Kabove) with little perceptible loss.speed(1–10, default 4) — lower is slower but smaller/better. Raise it for faster encodes when iterating.threads—0matches CPU cores.
ts
import { Transformer, ChromaSubsampling } from '@napi-rs/image'
await new Transformer(png).avif({
quality: 75,
chromaSubsampling: ChromaSubsampling.Yuv420,
speed: 4,
})
In the browser (Playground) AVIF encoding is single-threaded; on Node it uses the thread pool.
PNG
Two distinct tools:
losslessCompressPng(oxipng) — pixel-perfect, no quality loss. Strips redundancy, tries filters, recodes the IDAT. Best for graphics you must keep exact. Setstrip: trueto drop non-critical chunks (metadata) for extra savings.pngQuantize(built-in) — lossy palette quantization. Dramatically smaller (1.2M → 244K) for images that tolerate a 256-color palette. TuneminQuality/maxQuality; lowermaxQuality= smaller.
ts
await losslessCompressPng(png, { strip: true })
await pngQuantize(png, { maxQuality: 75 })
For encoding a decoded image to PNG (not in-place compression), Transformer.png() takes PngEncodeOptions: compressionType (Default/Fast/Best) and filterType.
JPEG
Transformer.jpeg(quality)— encode a decoded image to JPEG. Default quality 90.compressJpeg(bytes, opts)— MozJPEG re-compress existing JPEG bytes. Default quality 100 (near-lossless); setquality: 75for real savings.optimizeScans(defaulttrue) makes progressive files smaller.
ts
await new Transformer(input).jpeg(82) // encode at q82
await compressJpeg(jpegBytes, { quality: 75 }) // recompress in place
Picking a format
Photographs → AVIF (smallest) or WebP lossy (faster, universal support)
Graphics / UI / PNG → losslessCompressPng (exact) or pngQuantize (smaller, lossy)
Existing JPEGs → compressJpeg({ quality }) to shrink without re-decoding pipelines
Need it everywhere → WebP lossy — broadest browser + tooling support of the modern formats
See the API Reference for every option, and the Playground to compare formats on your own image.