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

rosetta-squint

v1.1.1

Published

Cross-language byte-exact perceptual image hashing — decode + hash in one call. Umbrella package combining rosetta-squint-decode + rosetta-squint-hash.

Readme

rosetta-squint — JS/TS

Point at a file path or pass raw image bytes; get back the perceptual hash hex string that every other rosetta-squint port produces for the same input.

Install (Node)

npm install rosetta-squint
import { phash, phashBytes } from "rosetta-squint";

const h1 = await phash("photo.jpg", 8);                      // file path
const h2 = await phashBytes(new Uint8Array(jpegBytes), 8);   // raw bytes in memory
console.log(h1.toString());                                  // "c3f8a1b27d0e4f96"

Install (Browser)

import { phashBytes } from "rosetta-squint/browser";

const resp = await fetch("/photo.jpg");
const bytes = new Uint8Array(await resp.arrayBuffer());
const h = await phashBytes(bytes, 8);
console.log(h.toString());                                   // "c3f8a1b27d0e4f96"

The /browser sub-export omits the path-based functions (which use node:fs) and is otherwise identical. All 10 algorithms are available with the same names plus a Bytes suffix:

averageHashBytes, phashBytes, phashSimpleBytes, dhashBytes, dhashVerticalBytes, whashHaarBytes, whashDb4Bytes, whashDb4RobustBytes, colorhashBytes, cropResistantHashBytes.

Via CDN

Once on npm:

<script type="module">
  // esm.sh auto-builds browser bundles for any npm package
  import { phashBytes } from "https://esm.sh/rosetta-squint/browser";

  const resp = await fetch("/photo.jpg");
  const bytes = new Uint8Array(await resp.arrayBuffer());
  console.log((await phashBytes(bytes, 8)).toString());
</script>

esm.sh handles the @jsquash WASM + libheif-js bundling automatically. Other CDN options (unpkg, jsdelivr) ship the raw dist/ and assume your bundler handles CJS→ESM for the libheif-js and utif2 transitive deps.

Bundler notes

The browser entry uses dynamic import("libheif-js") and import("utif2") which are CommonJS packages. Modern bundlers handle the CJS→ESM interop automatically:

| Bundler | What you need | |---|---| | Vite | works out of the box | | esbuild | works out of the box | | webpack 5+ | works out of the box | | rollup | add @rollup/plugin-commonjs | | Parcel | works out of the box |

WASM (mozjpeg, libwebp) is loaded via WebAssembly.compileStreaming(fetch(url)) in the browser. Most bundlers emit the WASM blobs as static assets and rewrite the import URL — no manual config required for Vite/esbuild/webpack 5+.

The loadWasm helper detects browser vs. Node at runtime and uses the right loader path. The node:fs import in the Node branch is dynamic (await import("node:fs")) so browser bundlers tree-shake it.

API

// Path entry (Node only)
phash(path: string, hashSize: number): Promise<Hash>
phashSimple(path: string, hashSize: number): Promise<Hash>
dhash(path: string, hashSize: number): Promise<Hash>
dhashVertical(path: string, hashSize: number): Promise<Hash>
averageHash(path: string, hashSize: number): Promise<Hash>
whashHaar(path: string, hashSize: number): Promise<Hash>
whashDb4(path: string, hashSize: number): Promise<Hash>
whashDb4Robust(path: string, hashSize: number): Promise<Hash>
colorhash(path: string, binbits: number): Promise<Hash>
cropResistantHash(path: string): Promise<ImageMultiHash>

// Bytes entry (Node + browser)
phashBytes(bytes: Uint8Array, hashSize: number): Promise<Hash>
// ... same suffix-Bytes pattern for every algorithm above ...

// Lower level
decodeFile(path: string): Promise<RgbImage>        // Node only
decodeBytes(bytes: Uint8Array): Promise<RgbImage>  // Node + browser

Cross-port verification

phashBytes(imagehash.png, 8) === "ba8c84536bd3c366" in this package's browser entry, this package's Node entry, the Go squint port, the Java squint port, and the Python rosetta_squint port. Same hex everywhere.

License

BSD-2-Clause.