rawconvert-wasm
v0.1.1
Published
Browser-based RAW photo processing via WebAssembly — supports CR2, CR3, NEF, ARW, DNG, and 1200+ cameras
Maintainers
Readme
rawconvert-wasm
Browser-based RAW photo processing via WebAssembly. Supports CR2, CR3, NEF, ARW, DNG, and 1200+ cameras.
Built on LibRaw compiled to WebAssembly.
See it in action at www.justconvertraw.com. All processing runs locally in the browser, no server, no uploads.
Features
- 1200+ cameras — powered by LibRaw 0.22.1
- Metadata extraction — EXIF, GPS, lens info, timestamps
- Fast thumbnails — extract embedded JPEGs without demosaicing
- Full RAW processing — configurable demosaic, white balance, color space, highlight recovery
- Web Worker support — dedicated
RawConvertWorkerclass for non-blocking processing - TypeScript — full type definitions for all APIs
- Zero dependencies — self-contained WASM binary
- No COOP/COEP — works in iframes and with ad networks (no SharedArrayBuffer/pthreads)
Install
npm install rawconvert-wasmQuick Start
import { RawConvert } from 'rawconvert-wasm';
const rawconvert = await RawConvert.init();
// Load a RAW file (from File API, fetch, etc.)
const response = await fetch('photo.dng');
const buffer = await response.arrayBuffer();
const metadata = rawconvert.load(buffer, 'photo.dng');
console.log(`${metadata.cameraMake} ${metadata.cameraModel} — ${metadata.width}x${metadata.height}`);
// Extract embedded thumbnail (fast — no demosaic)
const thumb = rawconvert.getThumbnail();
const blob = new Blob([thumb.data], { type: 'image/jpeg' });
// Full RAW processing
const image = rawconvert.process({ colorSpace: 'srgb', halfSize: true });
// image.data is Uint8Array with RGB pixels
rawconvert.dispose();API Reference
RawConvert (main thread)
| Method | Returns | Description |
|--------|---------|-------------|
| RawConvert.init(options?) | Promise<RawConvert> | Initialize WASM module (see Asset URLs) |
| load(data, filename?) | ImageInfo | Load RAW file from ArrayBuffer |
| getMetadata() | ImageInfo | Get image metadata |
| getThumbnail() | ThumbnailResult | Extract embedded thumbnail |
| process(options?) | ProcessedImage | Full demosaic + process |
| convert(data, options?) | ProcessedImage | Load + process in one call |
| reset() | void | Reset processor for next file |
| dispose() | void | Free WASM resources |
RawConvertWorker (Web Worker)
Same API as RawConvert, but all methods return Promises and processing happens off the main thread.
import { RawConvertWorker } from 'rawconvert-wasm/dist/worker-client.js';
const worker = await RawConvertWorker.init();
const metadata = await worker.load(buffer, 'photo.dng');
const image = await worker.process();
worker.dispose();Asset URLs
rawconvert-wasm ships three runtime assets in dist/: rawconvert-core.js (the
Emscripten glue), rawconvert-core.wasm, and worker.js. By default both
init() methods look for them next to the importing module, which works when
you serve node_modules/rawconvert-wasm/dist/ as-is.
Bundlers (Vite, webpack, Rollup) rewrite module paths, so copy the assets into
your static directory and point init() at them:
const rawconvert = await RawConvert.init({
coreUrl: '/rawconvert/rawconvert-core.js',
wasmUrl: '/rawconvert/rawconvert-core.wasm',
});
const worker = await RawConvertWorker.init({
workerUrl: '/rawconvert/worker.js',
coreUrl: '/rawconvert/rawconvert-core.js',
wasmUrl: '/rawconvert/rawconvert-core.wasm',
});A postinstall script is the usual way to keep the copies in sync:
// scripts/copy-rawconvert.js
import { cpSync, mkdirSync } from 'fs';
const src = 'node_modules/rawconvert-wasm/dist';
mkdirSync('public/rawconvert', { recursive: true });
for (const f of ['worker.js', 'rawconvert-core.js', 'rawconvert-core.wasm']) {
cpSync(`${src}/${f}`, `public/rawconvert/${f}`);
}If you load rawconvert-core.js yourself with a <script> tag, RawConvert.init()
picks up the resulting createRawConvertCore global and skips loading it again.
Processing Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| colorSpace | 'raw'\|'srgb'\|'adobe'\|'wide-gamut'\|'prophoto'\|'xyz' | 'srgb' | Output color space |
| interpolation | 'linear'\|'vng'\|'ppg'\|'ahd'\|'dcb'\|'dht'\|'aahd' | 'ahd' | Demosaic algorithm |
| outputBps | 8 \| 16 | 8 | Bits per sample |
| halfSize | boolean | false | Half resolution (2x faster) |
| autoWhiteBalance | boolean | false | Auto white balance |
| cameraWhiteBalance | boolean | true | Use camera white balance |
| brightness | number | 1.0 | Brightness adjustment |
| highlightMode | number | 0 | 0=clip, 1=unclip, 2=blend, 3+=rebuild |
| noiseReduction | number | 0 | Wavelet noise reduction threshold |
| medianPasses | number | 0 | Median filter passes |
Supported Formats
CR2, CR3, NEF, NRW, ARW, SRF, SR2, DNG, RAF, ORF, RW2, PEF, SRW, ERF, KDC, DCR, MOS, 3FR, IIQ, RWL, MEF, MRW, X3F, and more.
Browser Compatibility
Chrome 57+, Firefox 52+, Safari 11+, Edge 16+
This is a browser package. dist/ is marked CommonJS so the Emscripten glue
script can be require()d, which means Node.js cannot import dist/index.js
directly — use it from a browser or through a bundler.
Building from Source
git clone --recursive https://github.com/anthonygreco/rawconvert-wasm.git
cd rawconvert-wasm
npm install
npm run install-emsdk
npm run buildLicense
The JavaScript/TypeScript wrapper code is MIT.
LibRaw is dual-licensed under LGPL-2.1 or CDDL-1.0 (your choice). For WASM static linking, CDDL-1.0 is recommended — file-level copyleft on LibRaw source modifications only, no relinking requirement.
Credits
- LibRaw — RAW image processing library
- Emscripten — C++ to WebAssembly compiler
