WebP image optimization with Lanczos resampling on edge runtime
npm install @sorabito-takano/wasm-image-optimizationWebAssembly-based image optimization library with a custom minimal resize core (OpenCV runtime removed for smaller wasm size) using extracted high-quality Lanczos resampling logic from pillow-resize. Primary target is WebP (auto lossless for PNG/WebP inputs, lossy otherwise) with optional JPEG output and a pass-through ("none") mode that returns the original bytes (useful when only resizing info or EXIF-based orientation handling is needed).
- Frontend
- Next.js (Multithreading support)
- React Router (Multithreading support)
- Backend
- Cloudflare Workers
- Deno Deploy
- Node.js (Multithreading support)
- Input formats (auto-detected): JPEG / PNG / WebP
- Output formats:
- webp – High-quality Lanczos resize using pillow-resize implementation, auto lossless for PNG/WebP sources, lossy otherwise
- jpeg – Always lossy JPEG (RGB → YCbCr), ignores lossless flag
- none – Returns original bytes untouched (width/height/EXIF orientation still processed)
https://next-image-convert.vercel.app/

Image conversion (all functions return Promises):
``ts
optimizeImage({
image: ArrayBuffer | Uint8Array | string,
width?: number,
height?: number,
quality?: number, // 0-100 (default 100)
format?: "webp" | "jpeg" | "none" // default: webp
}): Promise
optimizeImageExt({
image: ArrayBuffer | Uint8Array | string,
width?: number,
height?: number,
quality?: number,
format?: "webp" | "jpeg" | "none"
}): Promise<{
data: Uint8Array,
originalWidth: number,
originalHeight: number,
width: number,
height: number
}>
`
`ts`
waitAll(): Promise
waitReady(retryTime?: number): Promise
close(): void
launchWorker(): Promise
setLimit(maxWorkers: number): void // (exported but previously undocumented)
For PWA applications or custom deployment scenarios, you can configure the WebAssembly binary location:
`ts
import { setWasmUrl, setWasmBinary } from 'wasm-image-optimization/vite';
// Option 1: Set custom WASM URL (useful for PWA Service Worker caching)
setWasmUrl('/assets/libImage.wasm');
// Option 2: Provide pre-loaded WASM binary
const wasmBinary = await fetch('/assets/libImage.wasm').then(r => r.arrayBuffer());
setWasmBinary(wasmBinary);
// Reset to default behavior
import { resetWasmConfig } from 'wasm-image-optimization/vite';
resetWasmConfig();
`
Available in: vite, next, workers, node entry points.
For Vite usage include the plugin (adds proper asset copying and worker wiring):
- vite.config.ts
`ts
import wasmImageOptimizationPlugin from "wasm-image-optimization/vite-plugin";
export default defineConfig(() => ({
plugins: [
wasmImageOptimizationPlugin(),
//wasmImageOptimizationPlugin("build/client/assets") // optional: assetsPath
],
}));
`
Automatic host architecture detection (x86_64 / arm64) is handled by the helper script:
`bashAutomatic architecture detection and build
pnpm build:wasm:auto
$3
`bash
Build environment setup
pnpm docker:build-envStart development environment
pnpm docker:devRun make in Docker
pnpm docker:makeOpen shell in Docker
pnpm docker:shell-devClean up Docker resources
pnpm docker:stop
`$3
`bash
For ARM64 (Apple Silicon)
DOCKERFILE=./docker/Dockerfile.arm64 docker compose -f docker/docker-compose.auto.yml run --rm dev make allFor x86_64
DOCKERFILE=./docker/Dockerfile docker compose -f docker/docker-compose.auto.yml run --rm dev make all
`Supported Environments & Entry Points
| Environment / Use Case | Import Path |
|---------------------------------------|----------------------------------------------------|
| Cloudflare Workers / Edge (ESM) |
wasm-image-optimization |
| Next.js (SSR + Web Worker mode) | wasm-image-optimization/next |
| Next.js API Route (explicit) | wasm-image-optimization/next-api |
| Generic ESM / Deno Deploy | wasm-image-optimization |
| Node.js (single thread) | wasm-image-optimization |
| Node.js (multi thread pool) | wasm-image-optimization/node-worker |
| Vite (bundled browser main thread) | wasm-image-optimization/vite |
| Vite / Generic Web Worker (multi) | wasm-image-optimization/web-worker |
| Raw Worker (CJS fallback) | wasm-image-optimization/node |> Multi-thread variants expose
waitReady, waitAll, launchWorker, setLimit, close.Samples
Repository with usage examples:
https://github.com/SoraKumo001/wasm-image-optimization-samples
Behavior Notes
- EXIF orientation is automatically normalized before resizing/encoding.
- WebP encoding switches to lossless when input is PNG or WebP and output format is
webp.
- format: "none" returns the original bytes (useful when you only need metadata or want to defer encoding).
- quality only affects lossy paths (WebP lossy / JPEG). Lossless WebP ignores the numeric quality parameter.Image Processing Details
This library integrates high-quality image resampling algorithms extracted from pillow-resize for superior image quality compared to typical naive or bilinear scaling approaches. OpenCV is no longer shipped to drastically reduce WASM size.
$3
| Aspect | Before (OpenCV based) | Now (Custom minimal core) |
|--------|-----------------------|---------------------------|
| WASM size (typical) | ~1.5 MB | ~ (significantly smaller)* |
| Dependency | OpenCV (core/imgproc subset) | Hand-extracted resize pipeline |
| Filter | OpenCV Lanczos / fallback logic | Direct Lanczos-3 implementation |
| Fallback path | OpenCV INTER_LANCZOS4 | Removed (deterministic path) |
*Exact size depends on build flags; OpenCV code paths, alloc helpers, and unused kernels removed.
#### Key Points
- Only the math & buffer ops needed for Lanczos-3 down/upsampling are compiled.
- No dynamic dispatch / no unused interpolation kernels.
- Deterministic single path (no runtime fallback → smaller + predictable output).
- Implements horizontal + vertical separable filtering with windowed sinc (Lanczos radius=3).
- Designed for future extension (e.g. optional Mitchell / Catmull-Rom) without pulling large frameworks.
#### Why remove OpenCV?
- Large binary footprint (initial builds ~1.5MB → unacceptable for some edge/PWA budgets)
- Only resize + color conversion were used.
- Custom path eliminates build complexity (Python + CMake toolchain for OpenCV JS).
- Faster cold start under Service Worker / edge runtime due to reduced instantiation cost.
#### Quality
Lanczos-3 maintains sharpness while minimizing ringing for photographic sources. For heavy downscales (<25%) you may still apply an external pre-blur if aggressive aliasing in source exists.
> If you previously relied on OpenCV-specific behavior (e.g. other interpolation modes), migrate by calling
optimizeImage with the same resize parameters — behavior is now unified.Roadmap / TODO
- Expose pluggable resize kernels (Mitchell, Catmull-Rom, Lanczos-2).
- Optional AVIF output (investigation phase).
- Update published TypeScript types to include
"jpeg"` (if not already updated in release at read time).---
MIT License