Find matching Iconify icons by visual similarity.
Given an SVG, whaticon renders it and compares against indexed icons using perceptual hashing to find the closest matches.
npm install whaticon# Find matches for an SVG file
npx whaticon icon.svg
# Find similar icons to a known icon
npx whaticon --icon lucide:home
# Limit to specific icon sets
npx whaticon icon.svg --prefix lucide,tabler
# Prefer certain icon sets (sorted first at equal similarity)
npx whaticon icon.svg --prefer lucide
# Adjust threshold and limit
npx whaticon icon.svg --threshold 0.9 --limit 5
# Use different index size
npx whaticon icon.svg --index full # 200k+ icons
npx whaticon icon.svg --index core # ~10k icons (faster)
# JSON output
npx whaticon icon.svg --json| Option | Description |
|---|---|
-n, --limit <n> |
Maximum results (default: 10) |
-t, --threshold <n> |
Minimum similarity 0-1 (default: 0.8) |
-p, --prefix <sets> |
Limit to icon sets (comma-separated) |
--prefer <sets> |
Prefer these icon sets (comma-separated) |
-i, --index <variant> |
Index: core, popular (default), full |
-j, --json |
Output as JSON |
| Variant | Icons | Size | Description |
|---|---|---|---|
core |
~10k | ~0.4 MB | lucide, heroicons, tabler |
popular |
~57k | ~1.8 MB | 12 popular sets (default) |
full |
~200k+ | ~6 MB | All Iconify sets |
The popular index is bundled with the package. Other variants are downloaded on first use to ~/.cache/whaticon/.
import { findMatches, loadIndex } from 'whaticon'
import { ensureIndex } from 'whaticon/download'
// Ensure index is available (downloads if needed)
const { namesGz, hashesGz } = await ensureIndex('popular')
const index = loadIndex(namesGz, hashesGz)
// Find matches
const svg = '<svg>...</svg>'
const matches = await findMatches(svg, index, {
limit: 5,
threshold: 0.85,
prefixes: ['lucide', 'mdi'],
prefer: ['lucide']
})
console.log(matches)
// [
// { name: 'lucide:home', similarity: 0.95 },
// { name: 'mdi:home', similarity: 0.92 },
// ...
// ]Find matching icons from an index.
Compute difference hash for an SVG. Returns 128-byte Buffer.
Load index from gzipped binary files.
Ensure index is downloaded, returns gzipped buffers.
Check if index variant is cached locally.
import { buildIndex } from 'whaticon'
import { gzipSync, writeFileSync } from 'fs'
const icons = [
{ name: 'my:icon1', svg: '<svg>...</svg>' },
{ name: 'my:icon2', svg: '<svg>...</svg>' },
]
const { names, hashes } = await buildIndex(icons)
writeFileSync('names.txt.gz', gzipSync(names))
writeFileSync('hashes.bin.gz', gzipSync(hashes))- Render: SVG is rendered to a 33×32 grayscale image using sharp
- Hash: A difference hash (dHash) is computed — comparing adjacent pixels to produce 1024 bits
- Match: Hamming distance between hashes determines similarity
- Index build: ~10,000 icons/sec (sprite sheet batching)
- Search: ~5ms per query (32-bit popcount optimization)
- Index size: ~30 bytes per icon (gzip compressed)
Two gzip-compressed files:
names.txt.gz— icon names, one per linehashes.bin.gz— 128 bytes per icon, concatenated
MIT