mrkeyoor.com_
Wed 23 Sept 12:34 UTC
npmTestingupdated 23 Sept 2026

image-ssim review

image-ssim 0.2.0 compares two decoded pixel arrays with the structural similarity index and returns `ssim` plus mean contrast-structure, `mcs`. It accepts grayscale, grayscale with alpha, RGB, or RGBA data with matching dimensions. Our browser build was 2.4 KB minified and 1.3 KB gzipped. Version 0.2.0 is also the only npm release, so there is no recent feature set to migrate to. It does not decode files, resize mismatched images, draw a diff, or supply a test threshold.

Verdict

image-ssim 0.2.0 installed in 0.6 seconds and bundled to 1.3 KB gzipped in our sandbox, but a one-pixel edge window can return `NaN` and the repository has not been pushed since 2017. Keep it only for compatibility with established scores; start new SSIM work with ssim.js.

We installed it

Lab card: what happened when we installed image-ssimScreenshot of image-ssim documentation
Install✓ · 0.6s1 package on disk · 1 MB
ImportESM import works · require() works · CommonJS package
Browser1.3 KBgzipped (2.4 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does image-ssim install cleanly?

Yes. In a fresh container with an empty cache, npm install image-ssim finished in 0.6s, leaving 1 package and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does image-ssim add to a browser bundle?

1.3 KB gzipped (2.4 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does image-ssim work with both ESM and CommonJS?

Yes. Both import 'image-ssim' and require('image-ssim') worked in Node 22 in our run. The package is published as CommonJS.

Does image-ssim include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

image-ssim or ssim.js: which should you use?

ssim.js: Choose it for a maintained SSIM implementation with current algorithms and TypeScript support. image-ssim 0.2.0 installed in 0.6 seconds and bundled to 1.3 KB gzipped in our sandbox, but a one-pixel edge window can return NaN and the repository has not been pushed since 2017.

When should you not use image-ssim?

You are choosing an SSIM package for new work: npm has only version 0.2.0 and GitHub shows no push since May 2017

API stability3/5The entire exported surface in version 0.2.0 is `compare`, the four-value `Channels` enum, an image record, and a result containing `ssim` and `mcs`. Existing calls have had no release churn because npm lists one version. That predictability comes with seven positional arguments and no stated compatibility policy, so any future repair to edge-window math could change saved baselines.
Docs2/5The README says the code runs in browsers and servers, links the SSIM background material, and points to generated API documentation that returned HTTP 200. It provides no ordinary call example, file-decoding recipe, threshold advice, alpha explanation, or warning for mismatched dimensions. The parameter defaults and thrown error are visible in generated docs and source rather than taught as a testing workflow.
Maintenance1/5npm published the sole 0.2.0 release on July 10, 2015, and GitHub records the last push on May 13, 2017. The repository is not archived or marked deprecated, but open issue 2 reproduces non-finite results for a 9 by 9 image with an 8 by 8 window. Its development stack still names Gulp 3, Mocha 2, Browserify 10, and TypeScript 2.
Ecosystem3/5The npm API counted 3,800,217 downloads for August 18 through August 24, 2026, despite only 94 GitHub stars. Raw arrays make the function usable with browser `ImageData` and third-party decoders, and our checks found both CommonJS and ESM import usable on Node 22. The package supplies no decoder, test-runner adapter, report format, worker integration, or diff renderer.

Use it if

  • You already have equal-sized decoded pixels and need the package's exact SSIM and MCS results
  • An existing CommonJS test suite depends on image-ssim 0.2.0 behavior
  • You need a 1.3 KB gzipped comparator inside a browser worker or canvas tool
  • Your inputs use 1, 2, 3, or 4 channels and you can validate dimensions and output yourself
Skip it if

Setup reality

Our install of image-ssim 0.2.0 completed in 0.6 seconds and left 1 package using 1 MB on disk. The package has 0 direct dependencies, 0 peer dependencies, and 0 audit findings. It is CommonJS with no exports map; both require() and ESM import worked in Node 22. Declaration files are bundled, although package metadata does not name a types entry. Our browser build measured 2.4 KB minified and 1.3 KB gzipped.

There are no credentials or config files. The missing setup is image decoding: use a separate PNG, JPEG, canvas, or sharp path to produce raw samples, width, height, and the right channel count. RGBA data declared as RGB shifts the byte stride and ruins every pixel after the first. Alpha contributes as multiplication against black in the source, so composite onto the page background first when transparent assets are judged as rendered.

compare is synchronous and defaults to 8 by 8 windows, K1 0.01, K2 0.03, luminance conversion, and 8 bits per component. All tuning uses positional arguments. Equal dimensions are mandatory. A 9 by 9 image with the default window leaves one sample in the last window; the variance divisor becomes 0 and the current code returns NaN. Check Number.isFinite, pick thresholds from your own approved images, and move large batches off the main event loop.

Patterns

Compare two RGBA arrays compare-rgba-pixels

const { compare, Channels } = require('image-ssim');

const result = compare(
  { data: actual, width, height, channels: Channels.RGBAlpha },
  { data: expected, width, height, channels: Channels.RGBAlpha }
);
console.log(result.ssim, result.mcs);

Both arrays must contain decoded RGBA samples and use the same width and height.

Decode PNG files before comparison decode-png-inputs

const fs = require('node:fs');
const { PNG } = require('pngjs');
const { compare, Channels } = require('image-ssim');

const a = PNG.sync.read(fs.readFileSync('actual.png'));
const b = PNG.sync.read(fs.readFileSync('expected.png'));
const result = compare(
  { data: a.data, width: a.width, height: a.height, channels: Channels.RGBAlpha },
  { data: b.data, width: b.width, height: b.height, channels: Channels.RGBAlpha }
);

`pngjs` is a separate package. image-ssim cannot read compressed PNG bytes itself.

Compare pixels from two canvases compare-canvas-data

const a = ctxA.getImageData(0, 0, canvasA.width, canvasA.height);
const b = ctxB.getImageData(0, 0, canvasB.width, canvasB.height);
const result = compare(
  { data: a.data, width: a.width, height: a.height, channels: Channels.RGBAlpha },
  { data: b.data, width: b.width, height: b.height, channels: Channels.RGBAlpha }
);

A canvas tainted by an image without suitable CORS headers makes `getImageData` throw before image-ssim runs.

Apply a project threshold reject-low-score

const { ssim } = compare(actual, expected);
const minimum = 0.985;
if (!Number.isFinite(ssim) || ssim < minimum) {
  throw new Error(`SSIM ${ssim} is below ${minimum}`);
}

0.985 is an application choice in this example. Calibrate the cutoff with approved and rejected images from your own test suite.

Reject different image sizes cleanly guard-dimensions

function compareSameSize(a, b) {
  if (a.width !== b.width || a.height !== b.height) {
    return { ok: false, reason: `${a.width}x${a.height} != ${b.width}x${b.height}` };
  }
  return { ok: true, result: compare(a, b) };
}

The package throws `Images have different sizes!` when either dimension differs.

Compare packed RGB samples compare-rgb-pixels

const result = compare(
  { data: rgbA, width, height, channels: Channels.RGB },
  { data: rgbB, width, height, channels: Channels.RGB }
);

RGB mode consumes exactly 3 samples per pixel. Remove alpha bytes before declaring this channel mode.

Compare grayscale samples compare-grayscale-pixels

const result = compare(
  { data: grayA, width, height, channels: Channels.Grey },
  { data: grayB, width, height, channels: Channels.Grey }
);

`Grey` consumes 1 sample per pixel; use `GreyAlpha` when each pixel has a second alpha sample.

Composite RGBA onto white composite-transparent-pixels

function onWhite(rgba) {
  const rgb = new Uint8Array((rgba.length / 4) * 3);
  for (let s = 0, d = 0; s < rgba.length; s += 4) {
    const alpha = rgba[s + 3] / 255;
    rgb[d++] = rgba[s] * alpha + 255 * (1 - alpha);
    rgb[d++] = rgba[s + 1] * alpha + 255 * (1 - alpha);
    rgb[d++] = rgba[s + 2] * alpha + 255 * (1 - alpha);
  }
  return rgb;
}

The package's alpha path multiplies color by alpha, which corresponds to black behind transparent pixels. Pre-composite for another background.

Change the SSIM window set-window-size

const result = compare(actual, expected, 4);

The default window is 8. Avoid dimensions that leave a 1-pixel final window because version 0.2.0 can return `NaN`.

Sum RGB channels without weighting disable-luminance-weights

const result = compare(
  actual,
  expected,
  8,
  0.01,
  0.03,
  false
);

The sixth positional argument disables luminance weights. Preserve the 8, 0.01, and 0.03 defaults ahead of it.

Compare 16-bit component values set-bit-depth

const result = compare(
  imageA,
  imageB,
  8,
  0.01,
  0.03,
  true,
  16
);

The last argument changes the dynamic range to 16 bits. You still must decode file bytes into component values before calling `compare`.

Sort comparisons by SSIM rank-image-batch

const ranked = candidates
  .map(({ name, image }) => {
    const { ssim } = compare(image, baseline);
    return { name, ssim };
  })
  .filter(({ ssim }) => Number.isFinite(ssim))
  .sort((a, b) => a.ssim - b.ssim);

`compare` is synchronous and scans the image. Run large batches in workers if blocking the Node event loop would delay other work.

Alternatives

PackageRegistryPick it when
ssim.jsnpmChoose it for a maintained SSIM implementation with current algorithms and TypeScript support
looks-samenpmChoose it when screenshot files, tolerance controls, and diff images belong in one workflow
pixelmatchnpmChoose it for pixel-level comparisons that produce a visible changed-pixel image

More testing guides

pytest · chai · jsdom · vitest · playwright · coverage · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.