imgkit
v2.4.0
Published
High-performance image processing for Bun and Node.js - resize, convert, compress with HEIC, WebP, AVIF, PNG, JPEG support
Maintainers
Readme
imgkit
High-performance image processing for Bun and Node.js
Built with Rust and napi-rs for maximum speed
Documentation · API Reference · Examples · Changelog
Highlights
Performance
- 950x faster metadata extraction
- 1.9x faster thumbnail generation
- 2.6x faster concurrent operations
- SIMD-accelerated JPEG codec
- Zero-copy cropping & buffer handling
Features
- Fast Thumbnails (shrink-on-load)
- Native HEIC/HEIF support
- Smart Crop (content-aware)
- Composite / Watermark (blend modes)
- Dominant Colors (UI theming)
- ThumbHash & BlurHash placeholders
- Perceptual Hashing (pHash/dHash)
- ML Tensor Conversion (SIMD)
- EXIF metadata read/write
- Timeout & AbortSignal support
- WebAssembly fallback build
Installation
bun add imgkit # Bun
npm install imgkit # npm
yarn add imgkit # Yarn
pnpm add imgkit # pnpmQuick Start
import { resize, metadata, smartCrop, composite, transform, thumbhash, toTensor } from 'imgkit';
const buf = Buffer.from(await Bun.file('photo.jpg').arrayBuffer());
const logo = Buffer.from(await Bun.file('logo.png').arrayBuffer());
const info = await metadata(buf); // Ultra-fast metadata
const resized = await resize(buf, { width: 200 }); // Resize
const thumb = await smartCrop(buf, { aspectRatio: '1:1' }); // Content-aware crop
const marked = await composite(buf, { // Watermark / overlay
layers: [{ input: logo, gravity: 'southEast', opacity: 0.7 }]
});
const { dataUrl } = await thumbhash(buf); // Placeholder image
const webp = await transform(buf, { // Full pipeline
crop: { aspectRatio: '16:9' }, resize: { width: 1280 },
output: { format: 'webp', webp: { quality: 85 } }
});
const tensor = await toTensor(buf, { // ML-ready tensor
width: 224, height: 224, normalization: 'Imagenet', layout: 'Chw'
});
// Timeout & cancellation (all async functions)
const safe = await resize(buf, { width: 800 }, { timeoutMs: 5000 });
const ac = new AbortController();
await resize(buf, { width: 800 }, { signal: ac.signal });API
| Function | Description | Async | Sync |
|----------|-------------|:-----:|:----:|
| metadata() | Image dimensions, format, color info | ✅ | ✅ |
| resize() | Resize with multiple algorithms | ✅ | ✅ |
| crop() | Crop region (zero-copy) | ✅ | ✅ |
| smartCrop() | Content-aware crop | ✅ | ✅ |
| composite() | Overlay / watermark with blend modes | ✅ | ✅ |
| dominantColors() | Extract colors for UI theming | ✅ | ✅ |
| thumbnail() | Fast thumbnail (shrink-on-load) | ✅ | ✅ |
| transform() | Multi-operation pipeline | ✅ | ✅ |
| toJpeg() / toPng() / toWebp() | Format conversion | ✅ | ✅ |
| blurhash() / thumbhash() | Image placeholders | ✅ | ✅ |
| toTensor() | ML tensor (SIMD-accelerated) | ✅ | ✅ |
| imageHash() / imageHashDistance() | Perceptual hashing | ✅ | ✅ |
| writeExif() / stripExif() | EXIF metadata | ✅ | ✅ |
| codecBackend() | Which backend is loaded & its capabilities | — | ✅ |
All async functions support { timeoutMs?, signal? } for timeout & cancellation. All have sync variants (resizeSync(), etc.).
WebAssembly
imgkit now runs on platforms with no prebuilt binary. Under Node the loader tries every native strategy first and falls back to WebAssembly only if none succeeds, so you keep the fast path automatically and an unsupported platform degrades instead of throwing at import.
The wasm build is compiled from the same Rust source as the native addon, so there is no second implementation to drift out of sync.
Browser & edge support is not finished. The
.wasmmodule and both loaders ship in the package, but the browser entry point is still to be built, soimport 'imgkit'does not yet work in a bundle. See the WebAssembly guide for what is and is not available.
Because the wasm build swaps the C codecs (libjpeg-turbo, libwebp) for pure-Rust ones, a few things differ. Check at runtime rather than guessing:
const b = codecBackend();
// { backend: 'pure-rust', wasm: true,
// shrinkOnLoad: false, webpLossyEncode: false, heic: false }| | Native | WebAssembly |
|---|---|:---:|
| Shrink-on-load (fast thumbnails) | ✅ | ❌ |
| Lossy WebP encode (quality) | ✅ | ❌ lossless only |
| HEIC/HEIF decode | ✅ | ❌ |
| Async runs off-thread | ✅ | ❌ |
| Everything else | ✅ | ✅ |
JPEG quality is honoured on both. Full details, including the timeoutMs
caveat and why the wasm build needs Node rather than Bun, are in the
WebAssembly guide.
rustup target add wasm32-wasip1-threads
bun run build:wasm # outputs to wasm/
bun run test:wasmFormats & Platforms
Formats: JPEG (TurboJPEG/SIMD), PNG, WebP, GIF, BMP, TIFF (read), HEIC/AVIF (macOS ARM64)
Platforms: macOS (ARM64, x64) · Linux (x64 glibc/musl, ARM64) · Windows (x64, ARM64) · WebAssembly fallback (Node, any other platform)
Development
git clone https://github.com/nexus-aissam/imgkit.git && cd imgkit
bun install && bun run build && bun run build:ts && bun testBuilding the native addon needs nasm, cmake and pkg-config (for
libjpeg-turbo). The WebAssembly build needs none of them:
rustup target add wasm32-wasip1-threads
bun run build:wasm && bun run build:ts && bun run test:wasmRust unit tests run under both codec backends:
bun run test:rust # native codecs (libjpeg-turbo + libwebp)
bun run test:rust:pure # pure-Rust codecs (what WebAssembly uses)