Client-side image compression using Web Workers (with a WASM-ready architecture).
npm install @thddrew/wasm-image-compressorClient-side image compression using a Web Worker (WASM-ready architecture).
``bash`
npm install @thddrew/wasm-image-compressor
`ts
import { ImageCompressor } from '@thddrew/wasm-image-compressor';
const compressor = new ImageCompressor();
const blob = await compressor.compress(file, {
quality: 75,
mimeType: 'image/jpeg',
});
// Remember to clean up when you’re done (SPA route transitions, etc.)
compressor.dispose();
`
`ts
import { ImageCompressor } from '@thddrew/wasm-image-compressor';
const compressor = new ImageCompressor();
const results = await compressor.compressBatch(
files,
{ quality: 75, mimeType: 'image/webp' },
4,
(index, status) => {
console.log(index, status); // processing | completed | error
},
);
`
The package exports:
- ImageCompressor (class)CompressionConfig
- (type)CompressorOptions
- (type)
Creates (and initializes) a Web Worker in the browser.
CompressorOptions:
- wasmUrl?: string
- Optional base URL to load WASM codec binaries from. When provided, the compressor will prefer a module-based worker and attempt to encode using WASM codecs (with an automatic fallback to the OffscreenCanvas encoder if WASM loading/encoding fails).
If you provide wasmUrl, you must host the codec .wasm files at that base URL. You can copy them from these installed package paths:
- @jsquash/jpeg: node_modules/@jsquash/jpeg/codec/enc/mozjpeg_enc.wasm@jsquash/webp
- : node_modules/@jsquash/webp/codec/enc/webp_enc.wasm (and optionally webp_enc_simd.wasm)@jsquash/png
- : node_modules/@jsquash/png/codec/pkg/squoosh_png_bg.wasm
If you want a ready-made, pinned CDN base URL, this repo publishes those files via jsDelivr:
- https://cdn.jsdelivr.net/gh/thddrew/wasm-image-compressor-codecs@v1.0.0/codecs
Example:
`ts
import { ImageCompressor } from '@thddrew/wasm-image-compressor';
const compressor = new ImageCompressor({
wasmUrl: '/codecs', // where the .wasm files are hosted
});
`
Compresses a single File off the main thread and returns a Blob containing the encoded image bytes.
CompressionConfig:
- quality?: number0–100
- Range: 75
- Default: 0
- Note: in the current worker implementation, is treated as “not provided” and will fall back to 75.mimeType?: 'image/jpeg' | 'image/png' | 'image/webp'
- 'image/jpeg'
- Default: width?: number
-
- Reserved for future resizing. Currently not applied by the worker.
Notes:
- The returned Blob uses config.mimeType if provided; otherwise it uses 'image/jpeg'.Worker
- This API is browser-only (requires , Blob, createImageBitmap, and OffscreenCanvas).
Compresses a list of files concurrently (default maxConcurrency is 4).
- Returns an array aligned with the input files array.null
- Any file that fails to compress becomes in the returned array.onProgress
- is called with the file’s index and status: 'processing' | 'completed' | 'error'.
Terminates the worker, revokes the internal Blob URL, and clears pending requests. Call this when the compressor is no longer needed.
Benchmark results comparing WASM codec mode vs OffscreenCanvas-only mode (tested on macOS, Chrome 120+):
- Input: 2.0 MB JPEG (2,045,670 bytes)
- Quality: 75%
- Format: JPEG
- Iterations: 20 measured runs (plus 1 warmup)
- Environment: Single image, sequential compression
| Mode | Median time | P95 time | Output size | Compression ratio |
|------|-------------|----------|-------------|-------------------|
| WASM (MozJPEG) | ~792ms | ~803ms | 710 KB | 65% reduction |
| OffscreenCanvas (native) | ~219ms | ~222ms | 992 KB | 52% reduction |
WASM mode advantages:
- Better compression: ~40% smaller files at the same quality setting
- Consistent results: Same output across browsers
- Advanced codecs: Access to MozJPEG, optimized WebP encoders, etc.
OffscreenCanvas mode advantages:
- Faster: ~3.6x faster compression (219ms vs 792ms median)
- No setup: No need to host .wasm files
- Native optimization: Browser's encoder is highly optimized
The WASM path has additional overhead:
1. Data copying: getImageData() copies pixel data from GPU → CPU (~2.4MB for an 800×600 image)
2. WASM function calls: Crossing the JavaScript/WASM boundary
3. Codec implementation: MozJPEG prioritizes compression quality over raw speed
The native browser encoder works directly with GPU textures and is heavily optimized, making it faster despite potentially larger output files.
- Use WASM mode when: file size matters more than speed (e.g., bandwidth-constrained uploads, storage optimization)
- Use OffscreenCanvas mode when: speed is critical and file size is acceptable (e.g., real-time previews, batch processing with time constraints)
The library automatically falls back to OffscreenCanvas if WASM fails to load or encode, so you can safely default to WASM mode for best compression.
- Browser-only: requires Worker, Blob, createImageBitmap, and OffscreenCanvas.Blob URL
- Bundler-friendly worker: the worker is embedded as a string and started via a , so consumers typically don’t need special worker config.wasmUrl
- WASM: when is provided, the compressor will attempt to load WASM codec binaries and use them for encoding (with a fallback to OffscreenCanvas` encoding).