Package Exports
- quickpix
- quickpix/decode
- quickpix/easy
- quickpix/encode
- quickpix/fallback
- quickpix/metadata
- quickpix/pipeline-worker
- quickpix/pipeline-worker.js
- quickpix/resize-worker
- quickpix/resize-worker.js
- quickpix/utils
Readme
quickpix
High-performance image resize for browsers and Node.js. Rust/WASM accelerated with pure JS fallback. Zero dependencies.
npm install quickpixQuick Start — High-Level API (QuickPixEasy)
For most use cases, QuickPixEasy is all you need.
Blob/File in → resize → Blob out, in a single call.
import { QuickPixEasy } from "quickpix";
const qp = new QuickPixEasy({
filter: "lanczos", // resize filter (default: bilinear)
outputMimeType: "image/jpeg",
outputQuality: 0.85,
preserveMetadata: true, // preserve EXIF/ICC/IPTC metadata
autoRotate: true, // auto-correct EXIF orientation (default: true)
});Resize Blob/File
const input = document.querySelector('input[type="file"]');
const file = input.files[0];
// Exact dimensions
const resized = await qp.resizeBlob(file, 1200, 800);
// Width only — height auto-calculated (aspect ratio preserved)
const resized2 = await qp.resizeBlob(file, 1200, null);
// Height only — width auto-calculated
const resized3 = await qp.resizeBlob(file, null, 800);
// Max dimension — longest side fits within limit
const resized4 = await qp.resizeBlob(file, null, null, { maxDimension: 4096 });
// resizeFile is an alias for resizeBlob
const resized5 = await qp.resizeFile(file, 1200, null);Create Thumbnails
Automatically preserves aspect ratio. The longest side fits within maxDimension.
// 6000x4000 image → 200x133 thumbnail
const thumbnail = await qp.createThumbnail(file, 200);
// Also accepts Canvas, ImageData, HTMLImageElement
const thumb2 = await qp.createThumbnail(canvasElement, 150);Draw to Canvas
const canvas = document.getElementById("preview");
canvas.width = 800;
canvas.height = 600;
await qp.resizeToCanvas(file, canvas, { filter: "lanczos" });Batch Parallel Processing
Processes multiple images concurrently using a Web Worker pool. Each image runs the full decode→resize→encode pipeline in a separate worker.
const results = await qp.batchResize([
{ source: photo1, maxDimension: 800 },
{ source: photo2, width: 600, height: 400 },
{ source: photo3, maxDimension: 200 },
]);
// results = [Blob, Blob, Blob]Metadata Preservation
By default, Canvas API strips all EXIF, ICC profiles, and other metadata.
Set preserveMetadata: true to re-inject the original JPEG metadata into the output.
// Preserve metadata (EXIF date, GPS, camera info, ICC color profile, etc.)
const withMeta = await qp.resizeBlob(photo, 1200, 800, {
preserveMetadata: true,
outputMimeType: "image/jpeg",
});
// Strip metadata (default — better for privacy)
const stripped = await qp.resizeBlob(photo, 1200, 800, {
preserveMetadata: false,
});EXIF Orientation Auto-Correction
Smartphone photos store rotation info in EXIF.
With autoRotate: true (default), images are automatically corrected to the right orientation.
// autoRotate: true (default) — portrait photos display correctly
const rotated = await qp.resizeBlob(phonePhoto, 800, 600);
// autoRotate: false — keep original pixel orientation
const raw = await qp.resizeBlob(phonePhoto, 800, 600, { autoRotate: false });Fit Modes
// contain (default): fit within 800x600, preserve aspect ratio
const a = await qp.resizeBlob(photo, 800, 600, { fit: "contain" });
// cover: fill 800x600 completely, preserve aspect ratio (may crop)
const b = await qp.resizeBlob(photo, 800, 600, { fit: "cover" });
// fill: stretch to exactly 800x600, ignore aspect ratio
const c = await qp.resizeBlob(photo, 800, 600, { fit: "fill" });Cleanup
qp.destroy(); // terminate worker pool and release resourcesOptions Reference
| Option | Default | Description |
|---|---|---|
filter |
"bilinear" |
Resize filter (nearest, bilinear, box, hamming, lanczos) |
maxWorkers |
navigator.hardwareConcurrency |
Max worker pool size |
idleTimeout |
30000 |
Auto-terminate idle workers (ms) |
outputMimeType |
"image/png" |
Output image format |
outputQuality |
0.92 |
JPEG/WebP quality (0–1) |
useWasm |
true |
Enable WASM acceleration |
preserveMetadata |
false |
Preserve EXIF/ICC/IPTC metadata |
autoRotate |
true |
Auto-correct EXIF orientation |
workerURL |
null |
Optional override: worker URL or factory for environments that cannot resolve import.meta.url worker imports |
requireWorker |
false |
If true, throws when worker pipeline is unavailable instead of falling back to main thread |
wasmPath |
null |
Override WASM module URL |
Low-Level API (QuickPix)
For direct RGBA buffer manipulation or fine-grained control.
import { QuickPix } from "quickpix";
const qp = new QuickPix({ useWasm: true, filter: "lanczos" });
const src = new Uint8ClampedArray(640 * 480 * 4);
const out = await qp.resizeBuffer(src, 640, 480, 320, 240, {
filter: "lanczos",
});
console.log(out.width, out.height, out.data.length); // 320 240 307200Bundler-safe worker setup (Next, Vite, Rollup, esbuild, webpack, Turbopack)
QuickPix and QuickPixEasy now try worker imports from multiple internal candidate paths first, so most projects work without workerURL.
import { QuickPix, QuickPixEasy } from "quickpix";
const qp = new QuickPix();
const qpe = new QuickPixEasy();If your bundler still fails to locate workers, pass workerURL in manually and keep requireWorker: true for hard-fail behavior:
import { QuickPix, QuickPixEasy } from "quickpix";
import resizeWorkerURL from "quickpix/resize-worker.js?url";
import pipelineWorkerURL from "quickpix/pipeline-worker.js?url";
const qp = new QuickPix({
workerURL: resizeWorkerURL,
requireWorker: true,
});
const qpe = new QuickPixEasy({
workerURL: pipelineWorkerURL,
requireWorker: true,
});Example bundler templates are in examples/bundlers.
Supported Filters
| Filter | Description | Speed | Quality |
|---|---|---|---|
nearest |
Nearest neighbor | Fastest | Low |
bilinear |
2x2 linear interpolation (default) | Fast | Medium |
box |
Box average | Medium | Medium |
hamming |
Hamming window | Slow | High |
lanczos |
Lanczos3 sinc-based | Slowest | Best |
Metadata Module
Standalone module for direct EXIF/ICC/IPTC manipulation.
import { readOrientation, extractSegments, injectSegments } from "quickpix/metadata";
const buffer = await file.arrayBuffer();
const orientation = readOrientation(buffer);
const segments = extractSegments(buffer);
const restored = await injectSegments(resizedJpegBlob, segments);Install & Build
npm install quickpix
# Development (build from source)
npm install
npm run build:wasm # Rust → WASM (requires wasm-pack)
npm run test:js
npm run test:rustBenchmarks
npm run bench # performance
npm run bench:compare # vs pica
npm run bench:memory # memory profiling
npm run bench:native # vs sharp (libvips)
npm run bench:quality # quality comparisonBrowser Compatibility
| Feature | Chrome 69+ | Firefox 105+ | Safari 16.4+ |
|---|---|---|---|
| Pipeline worker (optimal) | Yes | Yes | Yes |
| JS fallback | All browsers | All browsers | All browsers |
Automatically falls back to main-thread processing when OffscreenCanvas is unavailable.
License
MIT