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

inspect-image

v0.4.0

Published

Exact image color statistics, palettes, dominant colors and crop suggestions

Readme

inspect-image

Synchronous, deterministic image color analysis for Bun and Node-compatible runtimes.

inspect-image reports dimensions, encoded/decoded byte sizes, RGB + HSL/OkHSL distributions, spatial acutance/activity, frequent integer colors, dominant color, alpha statistics, tiled analyses, and conservative crop suggestions.

Install

bun add inspect-image

Usage

import {readFile} from 'node:fs/promises'

import inspectImage from 'inspect-image'

const result = inspectImage(encodedBytes)
const fromDisk = inspectImage(await readFile('photo.png'))

File access is deliberately outside the library. Read files with the importer’s runtime APIs and pass their bytes to inspectImage().

Inputs

inspectImage() accepts encoded PNG, JPEG, GIF, BMP and TIFF bytes synchronously, or tightly packed raw RGB/RGBA pixels:

inspectImage({
  width: 2,
  height: 1,
  channels: 4,
  data: new Uint8Array([255, 0, 0, 255, 0, 0, 255, 128]),
})

JPEG EXIF orientation is applied before analysis. GIF analysis uses the first frame. TIFF analysis uses the first page. ICC/profile conversion and animation aggregation are intentionally outside the synchronous core.

Result semantics

  • channels is the normalized decoded layout: 3 for RGB and 4 when the source structurally carries alpha. A fully opaque RGBA PNG therefore remains 4-channel and includes an opacity probe.
  • fileSize is the encoded byte count. It is absent only for direct raw-pixel input.
  • bufferSize is the normalized decoded RGB/RGBA byte count.
  • PNG tRNS color-key RGB is preserved instead of being replaced with black by the decoder.
  • Frequent colors count every pixel after rounding the selected HSL space to integer hue/saturation/lightness. Alpha never splits a color group; returned opacity is the group mean.
  • Crop comparison deliberately ignores alpha and requires at least one pair of adjacent/touching full outer edges with the same rounded color.
  • Hue average and median are circular rather than treating 359° and 1° as far apart.

OkHSL conversion is provided by the dependency-free okhsl package. Its RGB inverse intentionally returns integer OkHSL channels, which exactly matches this library’s grouping and crop semantics.

Options

type Options = {
  frequentColorsCount?: number
  hueSaturationThreshold?: number
  colorSpace?: 'okhsl' | 'hsl'
  rows?: number
  columns?: number
  dominantColorAlgorithm?: 'k_means' | 'median_cut' | false
  dominantColorClusters?: number
  maxPixels?: number
}

Defaults:

  • frequentColorsCount: 10 (0 disables frequency collection)
  • hueSaturationThreshold: 1 (pixels with lower saturation are excluded from the hue probe)
  • colorSpace: 'okhsl'
  • rows: 1, columns: 1
  • dominantColorAlgorithm: 'k_means'
  • dominantColorClusters: 5 (accepted range 1–16)
  • maxPixels: 40_000_000

When rows/columns are both 1, probes and colors are returned at the top level. Otherwise base metadata is returned with a row-major tiles array. Uneven dimensions are split with floor boundaries so every pixel belongs to exactly one tile.

Distribution probes

Each probe contains minimum, maximum, average, median and distribution brackets.

Non-hue probes use 8 non-overlapping percentage brackets:

0%, >0% <5%, ≥5% <20%, ≥20% <50%, ≥50% <80%, ≥80% <95%, ≥95% <100%, 100%.

Hue uses 12 circular 30° brackets centered on multiples of 30°: [345°,15°), [15°,45°), …, [315°,345°). The first bracket wraps across 360° → 0°. Pixels below hueSaturationThreshold are omitted from the hue probe.

RGB uses 255 as 100%; saturation/lightness/opacity/acutance/activity use 100. RGB medians are exact. Continuous HSL/opacity/spatial medians use 0.01-unit histograms; default OkHSL inverse values are integers by definition of the okhsl dependency.

Spatial probes use encoded-sRGB luma Y = 0.2126R + 0.7152G + 0.0722B and ignore alpha. activity is each pixel's mean absolute luma difference from its existing orthogonal neighbors, normalized so a black/white difference is 100%. acutance is the ratio of the clipped 3×3 luma range to the clipped 5×5 luma range; pixels whose 5×5 neighborhood is uniform are omitted from that probe. Spatial neighborhoods are clipped only at image boundaries and always cross tile boundaries, so changing rows/columns does not change a pixel's spatial measurement.

Frequent colors

Every pixel contributes. The selected HSL values are rounded to:

  • hue: 0–359
  • saturation: 0–100
  • lightness: 0–100

Ties are deterministic and sorted by the packed integer color key. Opacity is averaged per integer color when alpha exists.

Dominant color

Dominant analysis is deterministic and bounded:

  1. Every pixel contributes to a 5-bit-per-channel RGB population histogram (at most 32,768 buckets).
  2. Each non-empty bucket is represented by its population-weighted mean RGB and converted to Oklab.
  3. Deterministic weighted k-means or weighted median-cut operates on those buckets.
  4. The largest population cluster is represented by its weighted Oklab mean and converted back to the requested HSL space.

This avoids both random sampling bias and the unbounded high-entropy clustering cost seen in exhaustive per-color implementations. Alpha does not affect cluster assignment but is population-averaged for the winning cluster.

Crop suggestions

A crop is returned only when at least one adjacent edge pair (top+left, top+right, bottom+left, bottom+right) is entirely the same rounded color. Opposite-only borders do not qualify. Once qualified, only fully uniform rows/columns of that same color are stripped, making the suggestion conservative rather than blindly bounding every non-border-colored pixel.

Decoder behavior

  • PNG: RGB/RGBA, grayscale, grayscale-alpha, indexed transparency, color-key tRNS, 8/16-bit normalization through pngjs.
  • JPEG: synchronous jpeg-js decode plus EXIF orientations 1–8.
  • GIF: first frame through omggif, preserving transparent-index alpha semantics.
  • BMP: bmp-ts decode, including 32-bit alpha layout.
  • TIFF: first page through utif2, with alpha inferred from samples/extra-sample metadata.

Encoded dimensions are checked against maxPixels as early as each codec permits. The limit is an application guard, not a security sandbox; isolate untrusted decoders with process-level resource limits when needed.

Development

bun run validate

That runs strict TypeScript checking, ESLint, the complete test suite, and the production build.