@walujanle/image-converter-wasm
v1.1.0
Published
Rust WebAssembly image converter for browser and Node.js runtimes.
Maintainers
Readme
image-converter-wasm
Rust WebAssembly image converter for browser and Node.js runtimes.
License: AGPL-3.0-only
This package converts images entirely in memory. Input and output are Uint8Array values; the host application owns file picking, downloads, uploads, caching, and any filesystem work.
Installation
npm install @walujanle/image-converter-wasmFeatures
- Format-split WASM modules: JPEG, PNG, WebP, and AVIF are separate WASM binaries. Only the selected output codec is loaded. Each module still carries the full decoder set, so the split saves encoder weight rather than download size.
- ESM and CommonJS entries: Use
importorrequirefrom the package root. - Browser and Node.js support: Browser/bundler builds use adjacent WASM asset URLs. Node.js loads the packaged WASM bytes directly.
- Supported input:
jpg,jpeg,png,webp,heic,heif,avif. - Supported output: JPEG, PNG, WebP, AVIF.
- HEIC/HEIF decode-only support: HEIC/HEIF can be converted from, but not encoded to.
- Pure Rust decoding: HEIC, HEIF, and AVIF input decode without any C dependency.
- Metadata extraction: EXIF, XMP, IPTC, and ICC extraction from in-memory bytes.
- Metadata preservation:
keepMetadataenables EXIF/XMP/IPTC preservation where the output container supports it. ICC color profiles may be retained for color fidelity when available. - Crop and resize: Percent crop, aspect-ratio-aware resize, and smart filter selection.
- Input hardening: Size, pixel-budget, extension, and magic-byte validation run before decode on every function that accepts image bytes.
- Privacy: the
Socialintent removes GPS and device-identity metadata. - Progress callback:
convertImageWithInfocan report stage-based progress.
Quick Start
ESM
import { convertImage, init } from '@walujanle/image-converter-wasm';
await init({ preload: ['Jpeg'] });
const input = new Uint8Array(await file.arrayBuffer());
const output = await convertImage(input, '.PNG', {
format: 'Jpeg',
quality: 82,
resize: true,
targetWidth: 1600,
resizeLockAspectRatio: true,
keepMetadata: true,
});
const blob = new Blob([output], { type: 'image/jpeg' });CommonJS
const fs = require('fs');
const { convertImage, init } = require('@walujanle/image-converter-wasm');
(async () => {
await init();
const input = new Uint8Array(fs.readFileSync('photo.png'));
const output = await convertImage(input, 'png', {
format: 'Jpeg',
quality: 85,
});
fs.writeFileSync('photo.jpg', output);
})();API
init(options?)
Initializes wrapper configuration and optionally preloads format modules.
type OutputFormat = 'Jpeg' | 'Png' | 'WebP' | 'Avif';
type WasmSource = string | URL | Response | ArrayBuffer | ArrayBufferView | WebAssembly.Module;
interface InitOptions {
wasmSources?: Partial<Record<Lowercase<OutputFormat>, WasmSource>>;
preload?: OutputFormat[];
}wasmSources: Overrides the default WASM source per format. Use this when your framework requires WASM files to be served from a public/static directory.preload: Eagerly initializes selected output modules instead of waiting for first conversion.
You can also pass one direct WASM source to init(...); it becomes the default source for every format.
convertImage(fileBytes, ext, options)
Converts one image buffer and returns encoded bytes as Uint8Array.
interface ConvertOptions {
format: OutputFormat;
quality?: number;
pngCompressed?: boolean;
lossless?: boolean;
resize?: boolean;
targetWidth?: number;
targetHeight?: number;
resizeLockAspectRatio?: boolean;
crop?: boolean;
cropTop?: number;
cropBottom?: number;
cropLeft?: number;
cropRight?: number;
keepMetadata?: boolean;
intent?: 'Balanced' | 'Archive' | 'Social';
}Notes:
qualitydefaults to75and applies to JPEG, lossy WebP, and AVIF.losslessapplies to WebP.pngCompressedenables higher PNG compression.intentdefaults to'Balanced'and selects the quality policy:Balancedusesqualityas given.Archivefavours fidelity — 4:4:4 chroma for JPEG, slowest AVIF speed for the best compression, near-lossless WebP atquality >= 95, and maximum PNG compression.Socialfavours upload size — chroma subsampled, fastest AVIF speed,qualitycapped at90, and privacy metadata removed (see below).
- Every field except
formatis optional and falls back to an engine default. - Crop values are percentages from
0to100. Negative,NaN, and out-of-range values are rejected rather than silently ignored. - Extensions are normalized, so
PNG,.png, andpngare accepted when the bytes match the format.
What Social removes
Across EXIF, XMP, and IPTC:
- GPS coordinates, altitude, timestamp, map datum, and the direction/speed/track telemetry.
- Camera and lens serial numbers, including vendor-specific XMP namespaces.
- The camera owner's name and the image unique ID.
- IPTC location datasets: City, Sublocation, State/Province, Country code, Country.
Authorship, copyright, description, and exposure data are preserved. ICC colour profiles are untouched.
Limits
| Limit | Value | | --- | --- | | Max file size | 256 MB | | Max dimension per side | 16384 | | Max pixels per image | 8192 x 8192 | | Conversion timeout | 300 s |
The pixel budget is the binding limit. Exceeding it returns a catchable error rather than exhausting the 32-bit WebAssembly heap.
convertImageWithInfo(fileBytes, ext, options, onProgress?)
Converts one image buffer and returns bytes plus final dimensions.
interface ConversionResult {
bytes: Uint8Array;
width: number;
height: number;
}const result = await convertImageWithInfo(input, 'webp', {
format: 'Avif',
quality: 70,
}, (progress) => {
console.log(Math.round(progress * 100));
});The progress callback receives values from 0 to 1. Progress is stage-based, not a codec-internal byte counter.
getImageDimensions(fileBytes, ext, format?)
Returns image dimensions without running a conversion.
interface ImageDimensions {
width: number;
height: number;
}extractMetadata(fileBytes, ext, format?)
Extracts embedded metadata from an in-memory image buffer.
interface ImageMetadata {
exif?: number[] | null;
xmp?: number[] | null;
iptc?: number[] | null;
icc?: number[] | null;
}getProjectVersion(format?)
Returns the embedded project version string from the loaded WASM module.
const version = await getProjectVersion(); // "v1.0.0"Metadata Support Matrix
This matrix describes output preservation during conversion when keepMetadata: true.
| Output Format | EXIF | XMP | IPTC | ICC | | ------------- | ---- | --- | ---- | --- | | JPEG | Yes | Yes | Yes | Yes | | PNG | Yes | Yes | Yes | Yes | | WebP | Yes | Yes | No | Yes | | AVIF | Yes | Yes | No | No |
Additional details:
- ICC may be retained independently for color profile fidelity.
- AVIF input can expose ICC during metadata extraction when the source has an ISOBMFF
colrprofile box, but AVIF output does not currently embed ICC. - HEIC output is not supported.
Runtime And Bundler Notes
Browser And Bundlers
The ESM wrapper loads format modules dynamically and passes each generated index_bg.wasm URL to the wasm-bindgen initializer. In many bundlers this works without extra configuration.
If your framework does not serve WASM assets from dependencies, copy the needed files from the package formats/<format>/esm/index_bg.wasm paths into your app's public/static assets and pass explicit URLs:
await init({
wasmSources: {
jpeg: '/wasm/image-converter/jpeg/index_bg.wasm',
png: '/wasm/image-converter/png/index_bg.wasm',
webp: '/wasm/image-converter/webp/index_bg.wasm',
avif: '/wasm/image-converter/avif/index_bg.wasm',
},
});Browser conversions should run in browser-capable code paths. For SSR frameworks, call the browser ESM API from client-side components, client-only hooks, or a web worker.
Node.js ESM
No bundler configuration is required in Node.js. The wrapper reads packaged WASM bytes from disk.
import { init, convertImage } from '@walujanle/image-converter-wasm';
await init();
const output = await convertImage(input, 'png', { format: 'Jpeg' });Node.js CommonJS
const { init, convertImage } = require('@walujanle/image-converter-wasm');
(async () => {
await init();
const output = await convertImage(input, 'png', { format: 'Jpeg' });
})();Next.js, Nuxt, SvelteKit, Astro, React, Vue
Use the browser API only from client-side code when the conversion is meant to run in the browser. If the framework cannot resolve package WASM assets automatically, use wasmSources with public/static URLs as shown above.
Node runtime code can use the Node.js ESM or CommonJS examples. Edge runtimes are not a documented target for this package.
License
This package is licensed as AGPL-3.0-only.
The distributed WASM binaries include AGPL/commercial-licensed dependencies such as heic and zenwebp. Applications distributing this package or using it over a network must comply with AGPL-3.0-only obligations or have a separate compatible licensing path.
