Improvements that ship under the current API contract. No breaking changes. Existing consumers upgrade without code changes.
- Handle white/single-color images. When the pixel filter strips all pixels (all white, all transparent, single color), return a sensible fallback instead of
null.getColor()should return the dominant remaining color (or the image's actual color if filtering removed everything).getPalette()should return a shorter array rather than crash. - Fix variable scope leak.
src/color-thief.js:120—i = uInt8Array.lengthis missinglet, creating an implicit global. - Add input validation with clear error messages. Throw descriptive errors for: missing/unloaded image elements, tainted canvases (CORS), invalid image sources. Currently these fail silently or throw cryptic browser errors.
- Ship a
dist/color-thief.d.tsanddist/color-thief-node.d.tsalongside the existing JS output. - Add
typesfield topackage.json. - No source rewrite — just hand-authored
.d.tsfiles that match the current API.
Expand what getColor() and getPalette() accept beyond HTMLImageElement:
HTMLCanvasElementImageDataImageBitmap
These are additive — existing code passing <img> elements still works.
Expose the hardcoded thresholds as optional config:
ignoreWhite(defaulttrue, current behavior) — with configurable RGB threshold (default 250)alphaThreshold(default 125) — pixels below this alpha are skippedminSaturation(default 0) — optional minimum saturation filter
Pass as an options object: getColor(image, { quality: 10, ignoreWhite: false }). The current positional args (quality, colorCount) continue to work for backward compat.
- Upgrade ESLint v5 → v9 with flat config.
- Update
ecmaVersionfrom 2018 to current. - Add a CI workflow (GitHub Actions) for automated test runs on PRs.
- Drop the misleading
color-thief.min.jscopy — or actually minify it.
A new major version. Different API surface, new output format, modern JS throughout. Published as a new major version with a migration guide.
- Rewrite all source files in TypeScript.
- Merge the browser and Node implementations into a single codebase with platform-specific adapters for pixel loading. The core algorithm, pixel filtering, and output formatting are shared. Only the "get pixels from image" step differs.
- Single API surface for both platforms. No more class on browser / bare functions on Node.
- Promise-based everywhere.
getColor()andgetPalette()return Promises on both browser and Node. - Drop
getColorFromUrl(),getColorAsync(), andgetImageData(). Image loading is the consumer's responsibility. - Replace
XMLHttpRequestwithfetch()if any internal HTTP calls remain. - Support
AbortController/AbortSignalfor cancellation.
Replace bare [r, g, b] arrays with color objects:
const color = await colorThief.getColor(image);
color.rgb() // { r, g, b }
color.hex() // '#e84d3d'
color.hsl() // { h, s, l }
color.oklch() // { l, c, h }
color.array() // [r, g, b] (backward-compat escape hatch)
color.isDark // boolean
getPalette() returns an array of these objects.
Add a getSwatches() method that classifies palette colors into UI roles:
- Vibrant, Muted, DarkVibrant, DarkMuted, LightVibrant, LightMuted
- Each swatch includes a suggested text color (title and body) for accessibility.
- Inspired by Android's Palette API / node-vibrant, but with OKLCH-based classification for better perceptual accuracy.
- Use
OffscreenCanvas+createImageBitmapto move pixel reading and quantization off the main thread. - Opt-in via config:
getColor(image, { worker: true }). - Fallback to synchronous main-thread processing when Workers or OffscreenCanvas are unavailable.
- Remove
sharpas a hard dependency. It's heavy (native bindings, Docker/CI build issues). - Make image decoding pluggable — consumers bring their own decoder, or use a built-in lightweight default.
- Consider
sharpas an optional peer dependency for users who already have it.
- Ship a
@colorthief/wasmpackage with the quantization algorithm compiled from Rust. - Same API as the pure-JS version — drop-in replacement for the core.
- ~6x performance improvement for the compute-heavy pixel clustering step.
- The main
colorthiefpackage stays pure JS with zero native dependencies.
- Option to perform quantization in OKLCH color space instead of RGB.
- Produces more perceptually distinct palettes — colors that look different to humans, not just mathematically distant in RGB.
- Default remains RGB quantization for performance and backward compat. OKLCH mode is opt-in.
- For each color in a palette, include:
- WCAG contrast ratios against white and black
isDark/isLightboolean- Suggested foreground text color (white or black) for AA compliance
- Make this zero-config — always included in the output, no extra method calls.
- For large images, return an approximate palette immediately from a downsampled pass, then refine progressively.
- API:
getColor(image, { progressive: true })returns an async iterator or observable that emits improving results. - Useful for large images, batch processing, and perceived performance.
- No competitor currently offers this.