Image-Q: Image Quantization Library
Image-Q is a comprehensive TypeScript library for image color quantization, providing various algorithms to reduce the number of colors in an image while preserving visual quality. It supports alpha channels and offers multiple quantization methods, including NeuQuant, RGBQuant, and Xiaolin Wu's algorithms, alongside a broad selection of color distance formulas such as Euclidean, Manhattan, CIEDE2000, and CIE94. The library features both synchronous and promise-based asynchronous APIs, as well as generator-based advanced APIs, making it adaptable for various use cases. It supports both browser (Chrome 7+, Firefox 4+, IE 10+, Opera 11.6+, Safari 5.1+) and Node.js (6.0+) environments. Currently stable at version 4.0.0, the package has a moderate release cadence, with previous major versions introducing significant API changes, particularly around method naming and build outputs. Its key differentiators include a rich set of algorithms and distance metrics, full TypeScript support, and broad platform compatibility. The library is MIT licensed.
Common errors
-
TypeError: iq.PaletteQuantizer.quantize is not a function
cause Attempting to call the synchronous `quantize` method after it was renamed. The `quantize` method is now asynchronous (returns a Promise), and the synchronous version is `quantizeSync`.fixIf you intend a synchronous operation, change the call to `paletteQuantizer.quantizeSync(...)`. If you intend an asynchronous operation, ensure you `await` the call: `await paletteQuantizer.quantize(...)`. -
ReferenceError: require is not defined
cause Trying to use the CommonJS `require()` syntax in an ES Module context (e.g., in a Node.js project with `"type": "module"` or a browser environment without a CJS-aware bundler).fixUse the ES Module import syntax: `import * as iq from 'image-q';` or `import { utils } from 'image-q';`. Ensure your environment or bundler is configured to handle ES Modules. -
TypeError: (0 , image_q__WEBPACK_IMPORTED_MODULE_0__.utils).PointContainer.fromImageData is not a function
cause This Webpack/bundler-specific error often indicates an issue with how sub-modules are imported, especially when a global `iq` object might be expected or tree-shaking is over-aggressive.fixEnsure you are using specific named imports for sub-modules if you're not importing the entire namespace: `import { utils, dist, palette, image } from 'image-q';` rather than relying solely on `import * as iq from 'image-q'` for direct access to sub-modules without namespace qualification (e.g., `iq.utils.PointContainer`).
Warnings
- breaking The synchronous `quantize` method on `PaletteQuantizer` and `ImageQuantizer` classes was renamed to `quantizeSync` to distinguish it from the new promise-based `quantize` method.
- breaking Several color distance class names were updated for clarity. For example, `EuclideanRgbQuantWOAlpha` became `EuclideanBT709NoAlpha`, and `EuclideanRgbQuantWithAlpha` became `EuclideanBT709`.
- gotcha From version 3.0.2, the dedicated CommonJS (CJS) build was removed and replaced by a UMD (Universal Module Definition) build. While `require('image-q')` still functions, its behavior might have changed slightly, as the UMD build bundles all dependencies, potentially leading to larger bundle sizes or different loading characteristics compared to a pure CJS build.
Install
-
npm install image-q -
yarn add image-q -
pnpm add image-q
Imports
- iq
const iq = require('image-q');import * as iq from 'image-q';
- { utils, dist, palette, image }
import { utils, dist, palette, image } from 'image-q'; - iq
var iq = require('image-q');
Quickstart
import {
utils,
dist,
palette,
image,
} from 'image-q';
// Mock ImageData for demonstration. In a real app, you'd get this from a CanvasRenderingContext2D.
const width = 16;
const height = 16;
const data = new Uint8Array(width * height * 4);
for (let i = 0; i < data.length; i += 4) {
data[i] = Math.floor(Math.random() * 256); // R
data[i + 1] = Math.floor(Math.random() * 256); // G
data[i + 2] = Math.floor(Math.random() * 256); // B
data[i + 3] = 255; // A
}
const imageData = { data, width, height };
async function quantizeImage() {
// 1. Convert raw image data to PointContainer format
const inPointContainer = utils.PointContainer.fromImageData(imageData);
// 2. Define a color distance metric (e.g., Euclidean with BT.709 coefficients)
const distanceCalculator = new dist.EuclideanBT709();
// 3. Define a palette quantizer (e.g., NeuQuant to generate a 256-color palette)
const paletteQuantizer = new palette.NeuQuant(distanceCalculator, 256);
const colorPointContainer = await paletteQuantizer.quantize(inPointContainer);
// 4. Define an image quantizer (e.g., Floyd-Steinberg error diffusion dithering)
const imageQuantizer = new image.ErrorDiffusionArray(
distanceCalculator,
image.ErrorDiffusionArrayKernel.FloydSteinberg
);
// 5. Quantize the image pixels against the generated palette
const outPointContainer = await imageQuantizer.quantize(inPointContainer, colorPointContainer);
// 6. Get the result as a Uint8Array (RGBA data)
const quantizedData = outPointContainer.toUint8Array();
console.log('Quantized image data (first 16 bytes):', quantizedData.slice(0, 16));
console.log('Original image dimensions:', `${width}x${height}`);
console.log('Quantized image data length:', quantizedData.length);
}
quantizeImage().catch(console.error);