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

squoosh-batch

v1.0.0

Published

Batch image compression from the command line, using Squoosh's WebAssembly codecs

Readme

Squoosh batch CLI

Compress a folder of images without opening the web app. It runs the same WebAssembly codecs as batch mode, with the same default settings, so the same image and options give you the same file.

npm install -g squoosh-batch
squoosh-batch photos/

No dependencies — the codecs are WebAssembly and ship inside the package. Node 18.3 or newer.

From a checkout of this repo you can also run it without installing:

node cli/index.js photos/

Usage

squoosh-batch [options] <files or directories...>

  -f, --format <name>    Output format, repeatable or comma-separated.
                         mozJPEG, webP, avif, jxl, oxiPNG, wp2, qoi
                         (default: mozJPEG)
  -o, --out-dir <dir>    Where to write results (default: ./squooshed)
  -q, --quality <n>      Quality for lossy formats, overriding the default
      --max-width <n>    Shrink images to fit this width
      --max-height <n>   Shrink images to fit this height
  -c, --concurrency <n>  Images to work on at once (default: CPUs - 1, max 4)
      --overwrite        Replace existing output files
  -h, --help             Show this

Examples:

# Everything in a folder, to JPEG
node cli/index.js photos/

# Two formats at once, at quality 60
node cli/index.js -f webP -f avif -q 60 -o out photos/*.jpg

# Shrink oversized images on the way through
node cli/index.js -f webP --max-width 2000 hero.png

Directories are scanned one level deep for images. Formats are named exactly as the web app names them, and matched case-insensitively, so -f webp and -f webP both work. With more than one format, results go into a folder per format. Existing files are left alone unless you pass --overwrite.

Images are decoded and encoded across a pool of worker threads, one image per worker, which is the same arrangement batch mode uses in the browser.

Does it match the web app?

For most formats, exactly — same bytes, same checksum. Verified by compressing the same images both ways and comparing SHA-256:

| Format | Matches the web app | | | ------- | ------------------- | ------------------------------------------- | | MozJPEG | Yes | byte-identical | | WebP | Yes | byte-identical | | OxiPNG | Yes | byte-identical | | WebP v2 | Yes | byte-identical | | QOI | Yes | byte-identical | | AVIF | No | a few tenths of a percent different in size | | JPEG XL | No | a few tenths of a percent different in size |

AVIF and JPEG XL differ because the browser runs multi-threaded builds of those two codecs, and only single-threaded builds can load under Node. The number of threads changes how those encoders divide the work, which changes the bytes they emit. Quality is equivalent; the files just aren't identical.

Two formats are missing here: Browser JPEG and Browser PNG. Those encode through a <canvas>, which only exists in a browser. Use MozJPEG and OxiPNG, which are better anyway.

Reading images

The web app asks the browser to decode JPEG and PNG, because browsers already know how. Without a browser this uses the same wasm decoders Squoosh ships for its other formats. The two agree byte-for-byte on JPEG and on PNGs without transparency.

PNGs with semi-transparent pixels are the one exception. A browser stores transparent images with premultiplied alpha and loses a little precision converting back, so a pixel the wasm decoder reads as [136,128,119] comes back from a canvas as [136,127,119]. That difference is invisible, but it's enough to change the compressed bytes. The CLI's reading is the more accurate of the two.

Publishing a release

The package is cli/, and it carries its own copy of the codecs so an installed copy doesn't need the rest of the repo. prepack copies them in, so npm pack and npm publish both pick them up — there's no separate build step to remember.

cd cli
npm version patch      # or minor / major
npm publish

The first publish needs npm login and, since the name is unscoped, an account that can claim squoosh-batch. Check what you're about to ship first:

npm pack --dry-run

That should list index.js, lib/, README.md and about 30 files under vendor/codecs — roughly 3 MB compressed.

cli/vendor is generated and git-ignored; don't commit it.

Keeping the defaults in step

lib/defaults.js copies the default options out of the web app's encoder metadata (src/features/encoders/*/shared/meta.ts), and each block names the file it came from. If you change a default in the web app, change it here too, or the two will quietly stop producing the same files.