Fast image processing
in Rust.
Resize, convert and optimize JPEG, PNG, WebP, AVIF and more. Rust-powered native addons for Node.js, with WebAssembly for browser Workers.
import { readFile, writeFile } from 'node:fs/promises'
import {
Transformer,
ChromaSubsampling,
} from '@napi-rs/image'
const input = await readFile('./input.jpg')
const webp = await new Transformer(input)
.rotate()
.resize(225)
.webp(75)
const avif = await new Transformer(input)
.rotate()
.resize(225)
.avif({
quality: 70,
chromaSubsampling: ChromaSubsampling.Yuv420,
})
await writeFile('./output.webp', webp)
await writeFile('./output.avif', avif)The speed ratio is from the recorded M1 Max benchmark with a thread pool of 10; savings are from the sample images below. Results depend on your workload.
A measured workload
Historical single-JPEG results · Apple M1 Max · macOS 12.3.1. Pipeline: EXIF rotate → resize(225) → WebP or AVIF. Node.js and package versions were not recorded.
recorded WebP throughput ratio
1.8×
WebP pipeline throughput relative to sharp with UV_THREADPOOL_SIZE=10 in this recorded run.
The script uses one EXIF JPEG, WebP quality 75, and AVIF quality 70 with 4:2:0 chroma. Equal quality numbers do not establish equal visual quality across encoders. These historical results are not a current-release comparison or a prediction for other workloads. Inspect the benchmark and measure your own images, quality targets and hardware.
See the bytes disappear
Compare these sample outputs. Sizes and visible differences depend on the source image and encoder settings.


new Transformer(PNG).webp(75)Lossy

new Transformer(PNG).avif({ quality: 75 })Lossy

pngQuantize({ maxQuality: 75 })Lossy

new Transformer(PNG).avif({ quality: 100 })Quality 100

new Transformer(PNG).webpLossless()Lossless

losslessCompressPng()Lossless

compressJpeg(JPEG, { quality: 75 })Lossy

compressJpeg()LosslessSupported formats
WebP and AVIF decode and encode. HEIC requires macOS or Windows with an OS codec; it is unavailable in the browser. GIF, OpenEXR and farbfeld are not enabled in the published build.
| Format | Decode | Encode | Notes |
|---|---|---|---|
| JPEGrw | ✓ | ✓ | |
| PNGrw | ✓ | ✓ | |
| WebPrw | ✓ | ✓ | |
| AVIFrw | ✓ | ✓ | |
| TIFFrw | ✓ | ✓ | |
| BMPrw | ✓ | ✓ | |
| ICOrw | ✓ | ✓ | |
| TGA | ✗ | ✓ | input is not auto-detected |
| PNMrw | ✓ | ✓ | |
| HEIC / HEIFrw | ✓ | ✓ | macOS / Windows with OS codec only |
| Raw pixelsrw | ✓ | ✓ | RGBA8 input; native-endian output |
| SVG | ✓ | ✗ | rasterize with fromSvg() |
| DDS (DXT1/3/5) | ✓ | ✗ | decode only |
| HDR (Radiance) | ✓ | ✗ | decode only |
14 formats and raw pixel I/O · 10 bidirectional · 3 decode-only
Read the format and platform limitations before choosing a pipeline.
Built-in filters
grayscale, blur, hue-rotate, contrast and more — applied natively in Rust.







One pipeline
Read image bytes, optimize PNG, or resize and convert to WebP and AVIF.
import { readFileSync, writeFileSync } from 'node:fs'
import {
Transformer,
losslessCompressPng,
ResizeFilterType,
ChromaSubsampling,
} from '@napi-rs/image'
const PNG = readFileSync('./input.png')
writeFileSync('out.png', await losslessCompressPng(PNG))
writeFileSync('out.webp', await new Transformer(PNG)
.resize(800, null, ResizeFilterType.Lanczos3)
.webp(75))
writeFileSync('out.avif', await new Transformer(PNG)
.resize(800, null, ResizeFilterType.Lanczos3)
.avif({ quality: 75, chromaSubsampling: ChromaSubsampling.Yuv420 }))PLAYGROUND
Try it in your browser
Try the WASM build in a supported browser — images stay on your device.