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

@winrisef/jpegli-wasm

v0.1.0

Published

Browser and Web Worker JPEG encoder powered by Google JPEGli WebAssembly

Readme

@winrisef/jpegli-wasm

A small ESM browser package that encodes sRGB ImageData with JPEGli compiled to WebAssembly.

This is an independent, third-party wrapper. It is not an official Google product and is not endorsed or supported by Google.

The package contains the compiled .wasm; consumers do not need Docker, CMake, or Emscripten. It does not use cjpegli, Emscripten FS, MozJPEG, canvas encoding, @jsquash/jpeg, or a fallback encoder.

Install

npm install @winrisef/jpegli-wasm

Usage

import { encode } from "@winrisef/jpegli-wasm";

const output = await encode(imageData, {
  distance: 1.0,
  chromaSubsampling: "420",
  progressive: true,
});

const blob = new Blob([output], { type: "image/jpeg" });

encode works in browsers and dedicated workers. With Next.js, call it from a Client Component or a browser-only dynamic import. Vite can import it normally. The first call fetches and instantiates jpegli.wasm; later calls reuse that module instance.

For a worker, create a module worker with your bundler:

// main.ts
const worker = new Worker(new URL("./encoder.worker.ts", import.meta.url), {
  type: "module",
});
worker.onmessage = ({ data }) => {
  if (data.error) console.error(data.error);
  else console.log(new Blob([data.output], { type: "image/jpeg" }));
};
worker.postMessage(imageData);

// encoder.worker.ts
import { encode } from "@winrisef/jpegli-wasm";
self.onmessage = async ({ data }) => {
  try {
    const output = await encode(data, { distance: 1, chromaSubsampling: "420" });
    self.postMessage({ output }, { transfer: [output.buffer] });
  } catch (error) {
    self.postMessage({ error: String(error) });
  }
};

API

type ChromaSubsampling = "444" | "422" | "420" | "440";

interface ImageDataLike {
  readonly data: Uint8Array | Uint8ClampedArray;
  readonly width: number;
  readonly height: number;
  readonly colorSpace?: string;
}

interface EncodeOptions {
  readonly distance?: number; // 0..25, default 1.0; lower is higher quality
  readonly chromaSubsampling?: ChromaSubsampling; // default "444"
  readonly progressive?: boolean; // default true
  readonly adaptiveQuantization?: boolean; // default true
  readonly optimizeCoding?: boolean; // default true
}

function encode(
  imageData: ImageDataLike,
  options?: EncodeOptions,
): Promise<Uint8Array<ArrayBuffer>>;

The input must contain exactly width * height * 4 interleaved RGBA bytes. Only sRGB input is supported in this first release; alpha is ignored by JPEG.

Every option maps to JPEGli's pinned API:

  • distance calls jpegli_set_distance.
  • chromaSubsampling sets the JPEG component sampling factors used by JPEGli.
  • progressive selects jpegli_set_progressive_level(2) or level 0.
  • adaptiveQuantization calls jpegli_enable_adaptive_quantization.
  • optimizeCoding sets libjpeg-compatible optimize_coding.

JPEGli also optimizes Huffman tables in progressive mode, even when optimizeCoding is false. To disable that optimization, set both progressive: false and optimizeCoding: false. distance: 0 does not mean lossless JPEG. The wrapper accepts 0..25; this is the supported wrapper range, not a claim that upstream enforces these bounds.

The package requires a browser with WebAssembly SIMD and ES2022 support. TypeScript consumers need TypeScript 5.7 or newer for typed ArrayBuffer views. Encoding itself is synchronous on the calling thread after asynchronous WASM initialization. Use a dedicated Worker for large images to keep the UI responsive. Dimensions must be at most 65500 pixels per side. Memory limits may require much smaller images. Alpha is discarded, not composited onto a background. Failed WASM downloads can be retried by calling encode again.

Reproducible WASM build

  • JPEGli commit: 031a0077f5799a6041004267fc12b956c1f52a20
  • Emscripten: 4.0.10
docker build --target artifacts --output type=local,dest=. .

All C/C++ and WebAssembly compilation happens in the pinned Emscripten Docker image. The image checks out the exact JPEGli commit and its exact submodule revisions. TypeScript compilation is the only native host build step.

Development verification

npm ci
npm run build
npm test
npm run test:package
npm run test:repro
npm pack --dry-run

The browser suite checks JPEG structure, distance-dependent output, progressive/sequential SOF markers, all exposed chroma sampling factors, repeated encoding, and a real dedicated worker.

test:package installs an actual tarball with normal npm lifecycle behavior, type-checks the Blob example, and verifies browser and Worker encoding through Vite and Next.js in development and production. It uses the pinned development dependencies in this repository and keeps generated fixtures under .cache/. test:repro compares two independent no-cache Docker builds and the working WASM/JS artifacts by SHA-256. The Docker build uses linux/amd64 on every host. Development requires Node.js 22.12+, Docker, and Chrome/Chromium (set CHROME_PATH if it is not at a standard location). npm consumers do not need these tools.

Publishing

The package verification workflow runs on pushes and pull requests. The separate publish workflow is manual; it never publishes merely because code was pushed. For GitHub Actions publishing, configure the npm environment and either an npm trusted publisher for publish.yml or an NPM_TOKEN secret authorized for this scope. The workflow requests provenance and publishes a tested tarball.

Local first publication is also supported after authenticating as an npm account authorized for @winrisef:

npm run build
npm test
npm run test:package
npm pack --dry-run
npm publish --access public

Provenance is requested by the CI workflow, not forced on local publication. Never publish until the checks above pass. Updating upstream or Emscripten must also pass npm run test:repro.

Licensing

The wrapper source is MIT licensed. The packaged JPEGli/Highway-derived WebAssembly remains subject to the upstream notices in THIRD_PARTY_NOTICES.md and licenses/, including JPEGli's PATENTS grant. Redistributions must retain those files. See those texts for the controlling terms.