@stabilityprotocol.com/phash
v1.0.0
Published
Perceptual hash (pHash) implementation for Node and browser.
Readme
@stabilityprotocol.com/phash
Perceptual hash (pHash) implementation that runs in Node and the browser with no dependencies. Written in TypeScript with full type definitions.
Features
- 🚀 Zero dependencies - Pure TypeScript/JavaScript implementation
- 🔧 Type-safe - Full TypeScript support with comprehensive type definitions
- 🌐 Universal - Works in Node.js and browsers
- 📦 Multiple formats - ESM and CommonJS builds
- 🎯 Well-documented - Complete JSDoc documentation for all functions
- ⚡ Fast - DCT-based perceptual hashing algorithm
Live Examples
🎨 Try the interactive examples - Upload images and see perceptual hashing in action!
Install
npm install @stabilityprotocol.com/phashUsage
TypeScript
import phash, { fromRgba, fromImageData, fromImage, fromFile, PhashOptions } from "@stabilityprotocol.com/phash";
// From RGBA buffer with full type safety
const hash: string = fromRgba(rgba, width, height);
// With options
const options: PhashOptions = {
hashSize: 16, // 16x16 = 256-bit hash
sampleSize: 128 // Resize to 128x128 before DCT
};
const customHash = fromRgba(rgba, width, height, options);
// From ImageData
const hashFromImageData: string = fromImageData(imageData);
// From Image element (browser)
const hashFromImage: string = fromImage(imgElement);
// From File (browser, async)
const hashFromFile: Promise<string> = fromFile(file);
// Using default function
const hashViaDefault: string = phash(rgba, width, height);ESM (browser or Node):
import phash, { fromRgba, fromImageData, fromImage, fromFile } from "@stabilityprotocol.com/phash";
const hash = fromRgba(rgba, width, height);
const hashFromImageData = fromImageData(imageData);
const hashFromImage = fromImage(imgElement);
const hashFromFile = await fromFile(file);
const hashViaDefault = phash(rgba, width, height);CommonJS (Node):
const phash = require("@stabilityprotocol.com/phash");
const hash = phash.fromRgba(rgba, width, height);API
phash(input, width?, height?, options?)- Accepts ImageData-like objects, or RGBA buffers with width and height.
fromRgba(rgba, width, height, options?)fromImageData(imageData, options?)fromImage(image, options?)(browser-only unless you provide a canvas implementation)fromFile(file, options?)(browser-only)algorithms.universal.*access to the current algorithm implementation.
Options
All hashing functions accept an optional options parameter:
interface PhashOptions {
hashSize?: number; // Size of hash in bits per dimension (default: 8 for 64-bit hash)
sampleSize?: number; // Size to resize image before DCT (default: 64)
createCanvas?: (width: number, height: number) => HTMLCanvasElement | OffscreenCanvas;
}hashSize(default: 8) - Controls the hash dimensions. 8 = 8×8 = 64-bit hash, 16 = 16×16 = 256-bit hashsampleSize(default: 64) - Image is resized to this size before computing DCTcreateCanvas(width, height)- Custom canvas factory for environments without DOM or OffscreenCanvas
Development
# Install dependencies
npm install
# Build the library (uses tsup)
npm run build
# Run tests
npm test
# Serve examples locally
npx serve examplesRelease Process
This project uses semantic-release for fully automated versioning and npm publishing.
How It Works
When commits are pushed to the main branch, semantic-release automatically:
- Analyzes commit messages to determine version bump
- Generates release notes and updates CHANGELOG.md
- Updates package.json version
- Publishes to npmjs.com
- Creates a GitHub Release with generated notes
- Commits the version bump back to the repository
Commit Message Convention
Follow Conventional Commits format:
| Type | Version Bump | Example |
|------|--------------|---------|
| fix: | Patch (0.0.X) | fix: resolve edge case in hash calculation |
| feat: | Minor (0.X.0) | feat: add support for WebP images |
| feat!: or BREAKING CHANGE: | Major (X.0.0) | feat!: change API signature |
| docs:, chore:, style:, refactor:, test: | No release | chore: update dependencies |
Examples
# Patch release (0.1.0 → 0.1.1)
git commit -m "fix: correct hash comparison logic"
# Minor release (0.1.1 → 0.2.0)
git commit -m "feat: add grayscale conversion options"
# Major release (0.2.0 → 1.0.0)
git commit -m "feat!: redesign public API
BREAKING CHANGE: fromRgba now requires options parameter"Manual Release
No manual steps needed! Just push to main with properly formatted commits and semantic-release handles the rest.
How It Works
The perceptual hash algorithm:
- Resize - Scale image to a square (default 64×64) using bilinear interpolation
- Grayscale - Convert to grayscale using luminosity method (ITU-R BT.601)
- DCT - Apply 2D Discrete Cosine Transform to extract frequency components
- Reduce - Extract top-left DCT coefficients (default 8×8) containing low-frequency data
- Threshold - Compute median of DCT values (excluding DC component)
- Hash - Create binary hash where each bit = 1 if value > median, else 0
- Encode - Convert binary to hexadecimal string
Similar images produce similar hashes because they share low-frequency visual patterns.
License
MIT
