# rawconvert-wasm

Browser-based RAW photo processing via WebAssembly. Supports CR2, CR3, NEF, ARW, DNG, and 1200+ cameras.

[![CI](https://github.com/anthonygreco/rawconvert-wasm/actions/workflows/ci.yml/badge.svg)](https://github.com/anthonygreco/rawconvert-wasm/actions)
[![npm](https://img.shields.io/npm/v/rawconvert-wasm)](https://www.npmjs.com/package/rawconvert-wasm)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

Built on [LibRaw](https://www.libraw.org/) compiled to WebAssembly.

See it in action at [www.justconvertraw.com](https://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 `RawConvertWorker` class 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

```bash
npm install rawconvert-wasm
```

## Quick Start

```typescript
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](#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.

```typescript
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:

```typescript
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:

```js
// 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

```bash
git clone --recursive https://github.com/anthonygreco/rawconvert-wasm.git
cd rawconvert-wasm
npm install
npm run install-emsdk
npm run build
```

## License

The JavaScript/TypeScript wrapper code is [MIT](https://github.com/anthonygreco/rawconvert-wasm/blob/master/LICENSE).

LibRaw is dual-licensed under [LGPL-2.1](https://github.com/LibRaw/LibRaw/blob/master/LICENSE.LGPL) or [CDDL-1.0](https://github.com/LibRaw/LibRaw/blob/master/LICENSE.CDDL) (your choice). For WASM static linking, CDDL-1.0 is recommended — file-level copyleft on LibRaw source modifications only, no relinking requirement.

## Credits

- [LibRaw](https://www.libraw.org/) — RAW image processing library
- [Emscripten](https://emscripten.org/) — C++ to WebAssembly compiler
