npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wasmedia/wlibraw

v0.2.0

Published

LibRaw compiled to WebAssembly for browsers

Downloads

491

Readme

wlibraw

LibRaw — decoding of raw camera files (CR2/CR3, NEF, ARW, DNG, RAF, ORF, RW2, and more) — compiled to WebAssembly for the browser. Part of the wasmedia family of single-threaded + SIMD WASM ports that run on any site without SharedArrayBuffer or COOP/COEP headers.

wlibraw handles decoding only: lens correction is wlensfun's job, general image processing is wvips's, and metadata writing is wexiftool's.

What it supports today

  • Full raw decoding for every format LibRaw supports, via libraw_unpack + libraw_dcraw_process.
  • Typed metadata: make/model (LibRaw-canonicalised), raw and output dimensions, ISO, shutter speed, aperture, focal length, timestamp, lens, CFA pattern, per-channel white balance multipliers, and embedded-thumbnail info.
  • Embedded JPEG thumbnail extraction.
  • Decode options matching LibRaw's own defaults: half-size, 8/16-bit output, output color space, demosaic quality, camera/auto white balance, brightness, highlight recovery, flip, four-color RGB.
  • Deterministic cleanup via Symbol.dispose (using file = wlibraw.open(...)).
  • LibRaw's own dcraw_emu command-line tool, run against in-memory files with execute().
  • A Web Worker, @wasmedia/wlibraw/worker, that decodes off the main thread, and WLibRawWorker, a promise client for it.
  • executeFiles(), a file-backed dcraw_emu run (host files under Node, OPFS in a worker) that keeps inputs and outputs out of JavaScript memory.
  • A browser playground (npm run playground).

The full reference is in docs/api.md.

Install / build

Releases are published to npm as @wasmedia/wlibraw, with the prebuilt wlibraw.wasm included, so using it needs no Docker or Emscripten:

npm install @wasmedia/wlibraw

To build it from source instead:

npm install
npm run build:native   # compiles LibRaw + delegates to dist/wlibraw.{js,wasm} via Docker
npm run build:ts       # compiles src/ to dist/*.js
npm test                # or just: npm run build && npm test

npm run build:native requires Docker and GNU Make (not a local Emscripten install — make builds and caches the builder image itself). A from-scratch build of LibRaw and its delegates takes about two minutes on an Apple-silicon Mac, plus a one-time pull of the Emscripten Docker image on first use. Later runs reuse fingerprinted native/sources/.installed-* sentinels and rebuild only what changed. See docs/building.md for the details, including the flags that must never be removed and how to bump LibRaw.

npm test needs npm run build:native to have run at least once. dist/ is git-ignored and the test suite (and src/loader.ts) import dist/wlibraw.js directly; running npm test on a fresh clone without the native build first fails with a clear message telling you to run npm run build:native.

Usage

import { initWLibraw } from '@wasmedia/wlibraw';

const wlibraw = await initWLibraw();

const buf = await fetch('/photo.cr2').then((r) => r.arrayBuffer());
using file = wlibraw.open(buf);

console.log(file.metadata.make, file.metadata.model, file.metadata.isoSpeed);

using image = file.decode({ halfSize: true, useCameraWb: true });
const { data, width, height } = image.toImageData(); // ImageData-compatible

const canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
canvas.getContext('2d')!.putImageData(new ImageData(data, width, height), 0, 0);

file and image are freed automatically at the end of the block via using (Symbol.dispose); call .close() / .free() directly, in a finally, if your toolchain does not support using.

Runtime floor. The library itself does not need native Symbol.dispose: its own code releases handles with try/finally, and it fills in Symbol.dispose when the runtime lacks it, so the handle classes always have a real dispose key. Writing using in your own code is a separate matter: it needs a runtime or transpiler that supports explicit resource management (TypeScript 5.2+ lowers it). The browser checks cover headless Chrome and WebKit (Safari's engine); Firefox has been checked by hand.

decode() may be called only once per opened file — LibRaw cannot re-run libraw_unpack against the same handle. thumbnail() has no such restriction and composes with decode() in either order:

using file = wlibraw.open(buf);
const thumb = file.thumbnail();   // fine before or after decode()
using image = file.decode();

For a one-shot decode with no need to inspect metadata or the thumbnail first, use the convenience wrapper:

const pixels = wlibraw.decode(buf, { halfSize: true }); // { data, width, height, colors, bits }

CLI: execute()

execute(args, files) runs LibRaw's own dcraw_emu sample against in-memory files and returns its exit code, output and any files it wrote:

const data = new Uint8Array(await fetch('/photo.cr2').then((r) => r.arrayBuffer()));
const result = wlibraw.execute(['-h', 'photo.cr2'], [{ name: 'photo.cr2', data }]);
// { exitCode: 0, stdout: '', stderr: '', outputs: [{ name: 'photo.cr2.ppm', data }] }

A failing command is a result, not an exception: exit code 1 is a usage error, and 2 means an input could not be opened or decoded. Each call runs in a scratch directory that is always removed afterwards. See docs/api.md for input-name rules, -Z, and the sticky -v quirk.

For file-backed runs, executeFiles() does the same job against host files (Node) or OPFS files (in a worker), so inputs and outputs are not held in JavaScript memory. See docs/api.md.

Web Worker

A full-size decode can take long enough to freeze a page, so @wasmedia/wlibraw/worker runs it on a worker thread:

import { WLibRawWorker } from '@wasmedia/wlibraw';

// Point at the built file. It must stay next to loader.js and wlibraw.{js,wasm}, as in
// the package's dist/.
const client = new WLibRawWorker(() => new Worker('/dist/worker.js', { type: 'module' }));
const rgba = await client.decode(buf, { halfSize: true }, { format: 'rgba8' });

WLibRawWorker is a promise client for the worker. Each call is one-shot (init, metadata, thumbnail, decode, execute or executeFiles), result buffers are transferred rather than copied, and a failure rejects with a WLibRawError that keeps LibRaw's code and fatal flag. If the worker dies or its module aborts, the client replaces it on the next call. The underlying postMessage protocol is public too, for writing your own client. How a bundler resolves a worker URL inside a package varies from setup to setup, and none has been verified here; whatever you use, the worker must load dist/worker.js with dist/loader.js and dist/wlibraw.{js,wasm} beside it. The protocol is in docs/api.md.

Playground

npm run build          # once
npm run playground     # http://localhost:8000/

The playground decodes a raw file you choose (or the bundled CC0 sample) entirely through the worker. It shows metadata, the embedded preview and a canvas render, with controls for the main decode options, and an Export TIFF button that writes a full-resolution TIFF through executeFiles() to OPFS and offers it as a download (it is disabled, with an explanation, where OPFS is unavailable). The local server listens on 127.0.0.1 only, answers GET and HEAD, and deliberately sends no COOP/COEP headers, so every session checks that wlibraw works without cross-origin isolation. Set PORT to use a different port. Every main pipeline publishes the same page at https://libraw.wasmedia.org/.

Known limitations

  • A wasm abort is still possible, and reports a synthetic code. The native build compiles with -fwasm-exceptions, so LibRaw's own try/catch works and ordinary failures return real LibRaw codes — a truncated file gives LIBRAW_IO_ERROR (-100009), a missing preview LIBRAW_NO_THUMBNAIL (-5), an unrecognised file LIBRAW_FILE_UNSUPPORTED (-2), each with .fatal derived from LibRaw's own threshold. If the module aborts for a reason LibRaw does not convert — out of memory, or a genuine bug — WLibRawFile still catches it and throws a WLibRawError, but with a wlibraw-internal sentinel .code of -100099 and .fatal forced true. Treat that code as "something went wrong below LibRaw", not as a LibRaw enum value.
  • Monochrome sensors (colors === 1) decode correctly through toPixels()/ toUint8Array()/toUint16Array(), and toImageData() replicates the single channel across R/G/B, but no monochrome fixture exists in this repo's test suite (Leica M Monochrom / Phase One achromatic samples aren't available), so that path is covered by a synthetic unit test rather than a real decode.
  • Single-threaded. LibRaw is built without OpenMP, so wlibraw needs no SharedArrayBuffer or cross-origin isolation, but it is slower than a multi-threaded native LibRaw. A full-size decode of the 8 MP test CR2 takes about 0.6 s in Node on an Apple-silicon Mac. Use halfSize or thumbnail() when a preview is enough, and the Web Worker to keep a page responsive.
  • New LibRaw releases are not adopted automatically: a scheduled CI job files an issue when one appears, and the bump is done by hand. The playground is live at https://libraw.wasmedia.org/; it has been checked in Chrome, Firefox and WebKit (Safari's engine, via Playwright); the Safari app itself has not been driven.

Contributing

See CONTRIBUTING.md.

License

This project's licensing is split between the wrapper source and the distributed binary:

  • Wrapper source — the code in src/, native/bindings/, and the build tooling that this project authors — is licensed Apache-2.0, per the LICENSE file.
  • The distributed artifact, dist/wlibraw.wasm — is licensed LGPL-2.1, per LICENSE-libraw. LibRaw is dual-licensed upstream under LGPL-2.1 or CDDL-1.0 (see native/sources/libraw-0.22.2/COPYRIGHT) and is statically linked into this artifact. Apache-2.0 is incompatible with both options for a combined work, so this project distributes the binary under LGPL-2.1.
  • Consumers of the published package are bound by LGPL-2.1, which is what the license field in package.json (LGPL-2.1-only) declares.
  • wopfs — 0.2.0 (MIT) is vendored as JavaScript into dist/vendor/wopfs/ (not statically linked into the .wasm); its notice ships as dist/licenses/wopfs-LICENSE.txt and is linked from the playground footer.

wlibraw.wasm also statically links three other libraries. All three are permissively licensed (none imposes copyleft obligations beyond LGPL-2.1), but two of them require their notices to be carried into copies of the software, including a binary that bundles them — which this project's binary does:

  • zlib-ng — zlib license (see native/sources/zlib-ng-2.2.2/LICENSE.md). Its notice-retention clause is scoped to "any source distribution," so it does not attach to the compiled binary. No obligation beyond LGPL-2.1.

  • mozjpeg / libjpeg-turbo — IJG / Modified BSD / zlib licenses (see native/sources/mozjpeg-4.1.5/LICENSE.md). Its own compliance summary requires that product documentation for a binary that statically links libjpeg-turbo carry the attribution:

    This software is based in part on the work of the Independent JPEG Group.

  • lcms2 — MIT License (see native/sources/lcms2-2.16/LICENSE). Its permission notice applies to "all copies or substantial portions of the Software," with no source-only scoping, so it attaches to this statically-linked binary. The required notice:

    Copyright (c) 2023 Marti Maria Saguer

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

If you intend a different licensing arrangement for this project, review and update this section, LICENSE-libraw, and the license field in package.json together — they currently describe one consistent split.