High-performance image processing for Bun and Node.js - resize, convert, compress with HEIC, WebP, AVIF, PNG, JPEG support
npm install imgkit> [!IMPORTANT]
> Package Renamed: bun-image-turbo is now imgkit
> ``bash`
> npm uninstall bun-image-turbo && npm install imgkit
>
> The API is 100% compatible - just update your imports.
High-performance image processing for Bun and Node.js
Built with Rust and napi-rs for maximum speed




$3- 950x faster metadata extraction | $3- Fast Thumbnails (shrink-on-load) |
`bashBun (recommended)
bun add imgkit
Verified Package Managers
| Package Manager | Status | Tests |
|-----------------|:------:|------:|
| Bun | ✅ | 87 pass |
| npm | ✅ | 40 pass |
| yarn | ✅ | 40 pass |
| pnpm | ✅ | 40 pass |
Quick Start
`typescript
import {
metadata,
resize,
crop,
smartCrop,
dominantColors,
transform,
toWebp,
thumbhash,
toTensor,
imageHash,
imageHashDistance
} from 'imgkit';// Load image
const buffer = Buffer.from(await Bun.file('photo.jpg').arrayBuffer());
// Get metadata (ultra-fast, header-only parsing)
const info = await metadata(buffer);
console.log(
${info.width}x${info.height} ${info.format});// Crop to aspect ratio (zero-copy, ultra-fast)
const squared = await crop(buffer, { aspectRatio: "1:1" });
// Smart crop - finds the most interesting region automatically
const smartThumb = await smartCrop(buffer, { aspectRatio: "1:1" });
// Perfect for social media thumbnails!
// Extract dominant colors for UI theming (like Spotify)
const colors = await dominantColors(buffer);
console.log(colors.primary.hex); // "#3498DB"
// Perfect for auto-theming UI based on images!
// Resize with shrink-on-decode optimization
const resized = await resize(buffer, { width: 200 });
// Full pipeline: crop → resize → output
const youtube = await transform(buffer, {
crop: { aspectRatio: "16:9" },
resize: { width: 1280 },
sharpen: 5,
output: { format: 'WebP', webp: { quality: 85 } }
});
// Generate ThumbHash placeholder (better than BlurHash)
const { dataUrl } = await thumbhash(buffer);
// Use directly: 
// ML Tensor conversion (first JS package with native SIMD!)
const tensor = await toTensor(buffer, {
width: 224, height: 224,
normalization: 'Imagenet',
layout: 'Chw', batch: true
});
// Shape: [1, 3, 224, 224] - Ready for PyTorch/ONNX!
// Perceptual hashing for duplicate detection
const hash1 = await imageHash(buffer, { algorithm: 'PHash' });
const hash2 = await imageHash(otherBuffer, { algorithm: 'PHash' });
const distance = await imageHashDistance(hash1.hash, hash2.hash);
// distance < 5 = very similar images!
`
API Reference
Function
Description
Async
Sync
metadata()Get image dimensions, format, color info ✅ ✅
resize()Resize image with multiple algorithms ✅ ✅
crop()Crop image region (zero-copy, ultra-fast) ✅ ✅
smartCrop()Content-aware crop (saliency detection) ✅ ✅
dominantColors()Extract dominant colors for UI theming ✅ ✅
thumbnail()Fast thumbnail with shrink-on-load ✅ ✅
transform()Multi-operation pipeline with crop support ✅ ✅
toJpeg()Convert to JPEG ✅ ✅
toPng()Convert to PNG ✅ ✅
toWebp()Convert to WebP ✅ ✅
blurhash()Generate BlurHash placeholder ✅ ✅
thumbhash()Generate ThumbHash placeholder ✅ ✅
toTensor()Convert to ML tensor (SIMD-accelerated) ✅ ✅
imageHash()Generate perceptual hash for similarity ✅ ✅
imageHashDistance()Compare perceptual hashes ✅ ✅
writeExif()Write EXIF metadata ✅ ✅
stripExif()Remove EXIF metadata ✅ ✅
> Note: All functions have sync variants:
metadataSync(), resizeSync(), etc.
Image Placeholders
ThumbHash NEW
BlurHash
`typescript
const { dataUrl, hash } = await thumbhash(buffer);
// Ready-to-use data URL
element.style.backgroundImage = url(${dataUrl});
// Or store the compact hash (~25 bytes)
await db.save({ thumbhash: hash });
`
`typescript
const { hash } = await blurhash(buffer, 4, 3);
// Returns base83 string
console.log(hash);
// "LEHV6nWB2yk8pyo0adR*.7kCMdnj"
`
Advantages:
- Alpha channel support
- Aspect ratio preserved
- Better color accuracy
- Smoother gradients
Advantages:
- Widely supported
- Compact string format
- Good for simple images
Supported Formats
Format
Read
Write
Notes
JPEG ✅ ✅ TurboJPEG with SIMD acceleration
PNG ✅ ✅ Adaptive compression
WebP ✅ ✅ Lossy & lossless modes
HEIC/HEIF ✅ — macOS ARM64 only
AVIF ✅ — Via libheif
GIF ✅ ✅ Full support
BMP ✅ ✅ Full support
TIFF ✅ — Read-only
Platform Support
Platform
Architecture
Status
HEIC
macOS ARM64 (Apple Silicon) ✅ ✅
macOS x64 (Intel) ✅ —
Linux x64 (glibc) ✅ —
Linux x64 (musl/Alpine) ✅ —
Linux ARM64 ✅ —
Windows x64 ✅ —
Windows ARM64 ✅ —
Performance Benchmarks
Metadata Extraction
| Format | imgkit | sharp | Speedup |
|--------|----------------:|------:|:-------:|
| WebP | 0.004ms | 3.4ms | 950x |
| JPEG | 0.003ms | 0.1ms | 38x |
| PNG | 0.002ms | 0.08ms | 40x |
Image Processing
| Operation | imgkit | sharp | Speedup |
|-----------|----------------:|------:|:-------:|
| 50 Concurrent Ops | 62ms | 160ms | 2.6x |
| Transform Pipeline | 12.2ms | 19.1ms | 1.6x |
| 1MB JPEG → 800px | 12.6ms | 20.3ms | 1.6x |
WebP Resize
| Source → Target | imgkit | sharp | Speedup |
|-----------------|----------------:|------:|:-------:|
| 800x600 → 200px | 3.1ms | 4.3ms | 1.40x |
| 1600x1200 → 200px | 6.4ms | 8.0ms | 1.24x |
| 4000x3000 → 400px | 32.4ms | 33.1ms | 1.02x |
Thumbnail Generation (Shrink-on-Load)
| Source → Target | imgkit | sharp | Speedup |
|-----------------|----------------:|------:|:-------:|
| 2205x1240 → 200px | 9.1ms | 10.9ms | 1.2x |
| 11384x4221 → 200px | 100ms | 119ms | 1.2x |
Concurrent (100 images, 2205x1240 JPEG):
| Method | Total | Per Image | vs Sharp |
|--------|-------|-----------|:--------:|
| thumbnail | 167ms | 1.7ms | 1.8x |
| fastMode | 157ms | 1.6ms | 1.9x |
| sharp | 295ms | 2.9ms | baseline |
Examples
Basic Usage
`typescript
import { metadata, resize, toWebp } from 'imgkit';const input = Buffer.from(await Bun.file('input.jpg').arrayBuffer());
// Get image info
const info = await metadata(input);
console.log(info); // { width: 1920, height: 1080, format: 'jpeg', ... }
// Create thumbnail
const thumb = await resize(input, { width: 200, height: 200, fit: 'Cover' });
// Convert to WebP
const webp = await toWebp(input, { quality: 85 });
await Bun.write('output.webp', webp);
`
HEIC Conversion (macOS ARM64)
`typescript
import { metadata, transform } from 'imgkit';const heic = Buffer.from(await Bun.file('IMG_1234.HEIC').arrayBuffer());
// Check format
const info = await metadata(heic);
console.log(info.format); // 'heic'
// Convert to JPEG
const jpeg = await transform(heic, {
output: { format: 'Jpeg', jpeg: { quality: 90 } }
});
`
EXIF Metadata for AI Images
`typescript
import { writeExif, toWebp } from 'imgkit';const webp = await toWebp(aiGeneratedImage, { quality: 90 });
const withMetadata = await writeExif(webp, {
imageDescription: 'A sunset over mountains',
artist: 'Stable Diffusion XL',
software: 'ComfyUI v1.0',
copyright: '© 2024 Your Name',
userComment: JSON.stringify({
prompt: 'sunset over mountains, golden hour, 8k',
seed: 12345,
steps: 30
})
});
`
ML Tensor Conversion
`typescript
import { toTensor } from 'imgkit';const buffer = Buffer.from(await Bun.file('photo.jpg').arrayBuffer());
// PyTorch/ONNX (CHW layout, ImageNet normalization)
const tensor = await toTensor(buffer, {
width: 224,
height: 224,
normalization: 'Imagenet', // ResNet, VGG, EfficientNet
layout: 'Chw', // Channel-first for PyTorch
batch: true // Add batch dimension
});
// Shape: [1, 3, 224, 224] - Ready for inference!
const float32Data = tensor.toFloat32Array();
// TensorFlow.js (HWC layout)
const tfTensor = await toTensor(buffer, {
width: 224, height: 224,
normalization: 'ZeroOne',
layout: 'Hwc'
});
`Built-in normalizations:
Imagenet, Clip, ZeroOne, NegOneOne
Perceptual Hashing (Duplicate Detection)
`typescript
import { imageHash, imageHashDistance } from 'imgkit';// Generate perceptual hash
const hash1 = await imageHash(image1Buffer, { algorithm: 'PHash' });
const hash2 = await imageHash(image2Buffer, { algorithm: 'PHash' });
// Compare similarity (0 = identical, lower = more similar)
const distance = await imageHashDistance(hash1.hash, hash2.hash);
if (distance < 5) {
console.log('Images are very similar (likely duplicates)');
} else if (distance < 10) {
console.log('Images are somewhat similar');
} else {
console.log('Images are different');
}
// Available algorithms:
// - PHash: DCT-based, most robust (recommended)
// - DHash: Gradient-based, fast
// - AHash: Average-based, fastest
// - BlockHash: Block-based, balanced
`Use cases: Duplicate detection, content moderation, reverse image search, near-match finding.
Fast Thumbnails (Shrink-on-Load)
`typescript
import { thumbnail, thumbnailBuffer } from 'imgkit';const buffer = Buffer.from(await Bun.file('photo.jpg').arrayBuffer());
// Generate thumbnail with metadata
const result = await thumbnail(buffer, {
width: 200,
format: 'Webp',
quality: 85,
});
console.log(
${result.width}x${result.height});
console.log(Shrink-on-load: ${result.shrinkOnLoadUsed});// Simple buffer-only API
const thumb = await thumbnailBuffer(buffer, { width: 200 });
// Fast mode for maximum speed (2x faster)
const fast = await thumbnailBuffer(buffer, {
width: 200,
fastMode: true,
});
`Use cases: Image galleries, preview generation, CDN thumbnails, batch processing.
Smart Crop (Content-Aware)
`typescript
import { smartCrop, smartCropAnalyze } from 'imgkit';const buffer = Buffer.from(await Bun.file('photo.jpg').arrayBuffer());
// Smart crop to square (Instagram)
const square = await smartCrop(buffer, { aspectRatio: '1:1' });
// Smart crop to landscape (YouTube)
const youtube = await smartCrop(buffer, { aspectRatio: '16:9' });
// Smart crop to portrait (Stories/TikTok)
const portrait = await smartCrop(buffer, { aspectRatio: '9:16' });
// Analyze without cropping (get coordinates)
const analysis = await smartCropAnalyze(buffer, { aspectRatio: '1:1' });
console.log(
Best crop at: ${analysis.x}, ${analysis.y});
console.log(Size: ${analysis.width}x${analysis.height});
console.log(Score: ${analysis.score});
`Use cases: Social media thumbnails, profile pictures, e-commerce products, automatic galleries.
HTTP Image Server
`typescript
import { resize, toWebp } from 'imgkit';Bun.serve({
port: 3000,
async fetch(req) {
const url = new URL(req.url);
if (url.pathname === '/resize') {
const imageUrl = url.searchParams.get('url');
const width = parseInt(url.searchParams.get('w') || '400');
const response = await fetch(imageUrl!);
const buffer = Buffer.from(await response.arrayBuffer());
const resized = await resize(buffer, { width });
const webp = await toWebp(resized, { quality: 85 });
return new Response(webp, {
headers: { 'Content-Type': 'image/webp' }
});
}
return new Response('Not Found', { status: 404 });
}
});
`
Technology Stack
| Component | Technology | Benefit |
|-----------|------------|---------|
| JPEG Codec | TurboJPEG | SIMD acceleration (SSE2/AVX2/NEON) |
| Resize Engine | fast_image_resize | Multi-threaded with Rayon |
| WebP Codec | libwebp | Google's optimized encoder |
| HEIC Decoder | libheif-rs | Native Apple format support |
| Placeholders | thumbhash + blurhash | Compact image previews |
| Tensor Conversion | Native Rust + Rayon | SIMD-accelerated ML preprocessing |
| Perceptual Hash | image_hasher | Duplicate detection & similarity |
| Node Bindings | napi-rs | Zero-copy buffer handling |
Development
`bash
Clone repository
git clone https://github.com/nexus-aissam/imgkit.git
cd imgkitInstall dependencies
bun installBuild native module (requires Rust 1.70+)
bun run buildBuild TypeScript
bun run build:tsRun tests
bun run test # Local tests
bun run test:packages # Package manager tests
bun run test:all # All tests
``Documentation · npm · GitHub · Issues