Format Guides
Use the support table to check input and output formats. Compare output size and visual quality on your own images: a quality value of 75 is specific to an encoder, not a shared quality scale across formats or libraries.
WebP
- Lossy:
webp(qualityFactor)accepts 0–100 and defaults to 90. Try 75–80 as a starting point, then inspect the output. - Lossless:
webpLossless()preserves decoded pixels. Useful for graphics and screenshots; compare its size with PNG on your inputs.
await new Transformer(png).webp(75)
await new Transformer(png).webpLossless()
Both WebP decoding and encoding are supported. The API works with still images and does not expose animation controls.
AVIF
AVIF supports both input and output. Encoding uses libavif with the AOM codec. It can produce compact files, but the quality/size/time trade-off depends on the input and settings.
import { Transformer, ChromaSubsampling } from '@napi-rs/image'
const avif = await new Transformer(png).avif({
quality: 75,
alphaQuality: 90,
chromaSubsampling: ChromaSubsampling.Yuv420,
speed: 5,
})
quality: 0–100, default 80. Quality 100 alone does not guarantee a pixel-identical RGB/RGBA round trip: color conversion, chroma subsampling and alpha quality also matter.alphaQuality: 0–100, default 90; controlled independently of color quality.chromaSubsampling:Yuv444by default.Yuv420reduces chroma resolution, which can reduce size but may affect colored edges and text. Compare it visually before choosing it.speed: 1–10, with 5 used by the 1.15.0 implementation. Higher values favor encoding speed over compression efficiency. Set it explicitly for reproducible comparisons.threads: defaults to the CPU count;0requests automatic selection by the codec. Tune this together with the number of images processed concurrently.
The published decoder produces 8-bit pixels, and RGB/RGBA encoding converts higher-bit-depth inputs to 8-bit. Do not use this path when you need to preserve 10/16-bit color precision. In the browser, use the async Worker integration; it supports threaded WASM and is not a main-thread script.
PNG
losslessCompressPng: optimize existing PNG bytes with oxipng.strip: trueremoves non-critical chunks, including metadata.pngQuantize: lossy palette quantization, including alpha. TuneminQuality/maxQualityand inspect the result. Smaller output is not guaranteed for every input.Transformer.png(): encode a transformed image to PNG withPngEncodeOptions.
import { losslessCompressPng, pngQuantize } from '@napi-rs/image'
await losslessCompressPng(png, { strip: true })
await pngQuantize(png, { maxQuality: 75 })
JPEG
Transformer.jpeg(quality): encode a decoded image to JPEG; default quality 90.compressJpeg(bytes): lossless coefficient optimization of an existing JPEG via MozJPEG; default quality 100. This preserves the compressed image's coefficients, not its original file bytes or all metadata.compressJpeg(bytes, { quality: 75 }): decode and re-encode a JPEG with the image crate's JPEG encoder. This is a lossy path;optimizeScansapplies to the default MozJPEG coefficient-optimization path.
await new Transformer(input).jpeg(82)
await compressJpeg(jpegBytes)
await compressJpeg(jpegBytes, { quality: 75 })
JPEG XL
JPEG XL input (.jxl, as a bare codestream or in the JPEG XL container) is decoded on every platform, including browser/WASM builds, by the bundled pure-Rust jxl-rs decoder. There is no JPEG XL encoder, so there is no .jxl() output method: decode JPEG XL and encode to another format. metadata().format is 'jxl'.
import { Transformer } from '@napi-rs/image'
const webp = await new Transformer(jxlBytes).resize(800).webp(80)
| Behavior | Result |
|---|---|
| Orientation | Applied while decoding, so pixels are already upright. metadata().orientation is undefined and .rotate() without an angle changes nothing |
| Color | Converted to sRGB when the file uses another color space (Display P3, Rec.2100, an embedded ICC profile); the ICC profile is not kept |
| HDR | PQ and HLG input is tone mapped to SDR sRGB with libjxl's own method: Rec. 2408 from the intensity target down to 250 nits for PQ, and the HLG OOTF down to 80 nits. Colors outside sRGB are desaturated towards gray instead of clipped. 203 nits comes out at about 74% of sRGB white. The curve is smooth and neutral on gray, and integer and float files give the same pixels. HDR that comes with an embedded ICC profile is converted by that profile |
| Bit depth | Up to 8 bits per sample decodes to 8-bit; 9–16 bit and floating-point input decode to 16-bit |
| Grayscale | Stays grayscale, unless its color space needs conversion; then it decodes to RGB. Gray PQ and HLG input stays grayscale |
| Alpha | Straight (unpremultiplied); premultiplied files are converted |
| Animation | Only the first frame is decoded |
| Metadata | EXIF and XMP boxes are not read: metadata(true).exif is undefined |
| Limits | CMYK files are rejected. Files whose decoded pixels would need more than 512 MiB are rejected before decoding, like other formats. The decoder also rejects images of about 134 megapixels or more (about 100 megapixels with alpha) |
JPEG files recompressed with cjxl decode to pixels; the original JPEG bytes are not reconstructed.
HEIC and HEIF
HEVC-in-HEIF input (.heic / .heif) and HEIC output are available only on macOS and Windows, using OS codecs. The package does not bundle a HEVC codec. Linux, Android, FreeBSD and browser/WASM builds reject HEIC operations.
import { Transformer } from '@napi-rs/image'
const jpeg = await new Transformer(heicBytes).rotate().jpeg(80)
const heic = await new Transformer(pngBytes).heic({ quality: 80 })
| Behavior | macOS (ImageIO) | Windows (WIC) |
|---|---|---|
| Codec requirement | OS ImageIO HEIC support | HEIF Image Extensions and HEVC Video Extensions available to WIC |
| Decode | 8-bit → RGBA8; 10-bit → RGBA16 | Always RGBA8, including 10-bit sources |
| Encode bit depth | 8 or 10; 16-bit input defaults to 10 | 8 only; bitDepth: 10 rejects |
| Encode alpha | OS encoder behavior | Flattened to opaque |
| Quality | Default 80; 90–100 share a 0.9 ceiling; no lossless mode | Default 80; maps 0–100 to the OS encoder's 0–1 scale |
Do not assume the Windows extensions exist on server or CI hosts; missing codecs cause an error. HEIC input is normalized to sRGB without retaining an ICC profile. HDR gain maps are not composited. Use .rotate() for orientation tags where the decoder has not already applied them.
Choosing an output
| Need | Start with |
|---|---|
| Photo delivery | Compare WebP and AVIF at your target size and visual quality |
| Exact decoded graphics | Compare PNG optimization and lossless WebP |
| Smaller PNG with acceptable palette loss | pngQuantize |
| Keep JPEG output | compressJpeg for existing JPEGs; Transformer.jpeg after transforms |
| HEIC output on a supported OS | Check the installed codec and bit-depth limits above |
Use the playground to compare common formats, and the API Reference for options. Browser display support and this library's codec support are separate considerations.