v3 quantizes in OKLCH, a perceptually uniform space, so palettes are grouped by how colors actually look to the eye. Quantizing in raw RGB — what v2 did, and still available via colorSpace — clusters colors by numeric distance, which tends to return near-duplicates and miss saturated tones.
import { getPaletteSync } from 'colorthief';
// Perceptual OKLCH — the v3 default
const perceptual = getPaletteSync(img, { colorCount: 6 });
// Raw RGB — the v2 algorithm
const raw = getPaletteSync(img, { colorCount: 6, colorSpace: 'rgb' });
Colors are read in sRGB by default. Pass gamut: 'display-p3' to keep the wide-gamut colors in a Display P3 image instead of collapsing them to sRGB, or 'auto' to upgrade only when an image actually uses colors outside sRGB.
4
Observe a video source
Reactively watch a video source and get palette updates on every frame. Works with <video>, <canvas>, and <img> elements.
Classify palette colors into six semantic roles. Each swatch includes text color recommendations.
import { getSwatchesSync } from 'colorthief';
const swatches = getSwatchesSync(img);
if (swatches.Vibrant) {
header.style.background = swatches.Vibrant.color.css();
header.style.color = swatches.Vibrant.titleTextColor.css();
}
if (swatches.DarkMuted) {
sidebar.style.background = swatches.DarkMuted.color.css();
sidebar.style.color = swatches.DarkMuted.bodyTextColor.css();
}
8
Quality settings
The quality option controls how many pixels are sampled. Lower values sample more pixels (slower, more accurate). Default is 10.
import { getPaletteSync } from 'colorthief';
getPaletteSync(img, { quality: 1 }); // Every pixel
getPaletteSync(img, { quality: 10 }); // Every 10th pixel (default)
getPaletteSync(img, { quality: 50 }); // Every 50th pixel
9
Region extraction
Pass a region to sample only part of the image. Coordinates are fractions of the image size (0–1) measured from the top-left, so the same values work on a thumbnail and the full-size original. Useful when the colors you care about live in a known corner — a product against a backdrop, or the strip of image a caption sits on.
The async API keeps your UI responsive during extraction. It breaks the work into chunks and pauses between them, giving the browser time to handle animations, scrolling, and clicks. This is helpful when processing many images at once or working with very large images.
13,000+ GitHub stars. 7M+ npm downloads per year. Used in production by teams at every scale, from solo devs to Fortune 500s.
Works everywhere
Browser, Node.js, Web Workers. Feed it an <img>, <video>, <canvas>, a file path, or a raw Buffer.
Rich color objects
Every color comes with .hex(), .hsl(), .oklch(), .css(), WCAG contrast ratios, textColor, and isDark/isLight.
Perceptual accuracy
OKLCH quantization produces palettes that are perceptually uniform, colors spaced the way your eyes actually see them, not the way math divides up RGB cubes.
Real-time video
Hook into video and canvas elements to build reactive backgrounds, ambient lighting, and audio visualizers.
Non-blocking async
The async API yields to the browser between processing chunks so your UI never freezes. To move extraction off the main thread entirely, run Color Thief inside your own worker.
CLI included
Run npx colorthief-cli <image> from the terminal to extract colors without writing any code. Great for scripts and quick checks.
How it stacks up
A snapshot comparison with the most common alternatives.
Color Thief
Vibrant.js
img-color-extractor
npm downloads / yr
7M+
~600K
~30K
OKLCH quantization
Yes
No
No
Semantic swatches
Yes
Yes
No
Rich color objects
hex, hsl, oklch, css, contrast
hex, rgb, hsl
hex only
WCAG contrast
Built-in
Partial
No
Video observation
observe()
No
No
Sync & async API
Both
Promises only
Sync only
Node.js support
Yes (via sharp)
Yes
Browser only
TypeScript
Yes
DefinitelyTyped
No
CLI
Yes
No
No
Getting started
The package name is colorthief (not color-thief).
Install
npm install colorthief
Browser
Script tag
Load the UMD build from a CDN. This exposes a global ColorThief object.
<script src="https://unpkg.com/colorthief@3/dist/umd/color-thief.global.js"></script>
<script>
const color = ColorThief.getColorSync(img);
console.log(color.hex());
</script>
The colorthief-cli package bundles everything needed (including sharp for image decoding), so it works immediately with no extra setup.
Commands
$ colorthief-cli photo.jpg
#c94f6e
$ colorthief-cli palette photo.jpg --count 3
#c94f6e
#5a8fa3
#d4a853
$ colorthief-cli swatches photo.jpg
Vibrant #e84393
Muted #a0b4c0
DarkVibrant #8b1a3a
DarkMuted #4a5568
LightVibrant #f6a5c1
LightMuted #d4d8dc
Flags
--json # Full color data as JSON
--css # CSS custom properties
--count 5 # Number of palette colors (2-20)
--quality 1 # Sampling quality (1 = every pixel)
--color-space rgb # Quantization space (rgb or oklch)
Stdin is supported (cat photo.jpg | colorthief-cli -), and multiple files can be passed at once. If you already have colorthief and sharp installed, you can use colorthief as the command name directly.
API
Color Thief v3 exports standalone functions — no class instantiation needed. Every function comes in sync and async variants.
Functions
getColor(source, options?)
Returns: Promise<Color | null>
Extracts the single dominant color from an image. Returns a Color object, or null if extraction fails.
getColorSync(source, options?)
Returns: Color | null
Synchronous version of getColor(). Browser sources only — does not support Node.js, Web Workers, or AbortSignal.
getPalette(source, options?)
Returns: Promise<Color[] | null>
Extracts a multi-color palette. Returns an array of Color objects sorted by population (most dominant first), or null if extraction fails.
getPaletteSync(source, options?)
Returns: Color[] | null
Synchronous version of getPalette(). Browser sources only.
Extracts semantic swatches classified into six roles: Vibrant, Muted, DarkVibrant, DarkMuted, LightVibrant, and LightMuted. Uses colorCount: 16 internally for best classification results.
Reactively watches a video, canvas, or image element and fires a callback with a fresh palette on every frame or change. See observe() for full details.
configure(options)
Returns: void
Globally override the pixel loader and/or quantizer used by all extraction functions.
Factory function to manually create a Color object from RGB values. Useful for building Color objects from data you already have.
r, g, b — RGB values (0–255)
population — Pixel count for this color
proportion — Fraction of total pixels (0–1). Default: 0
Options
All extraction functions accept an options object. The async API supports every option; the sync API omits signal and loader.
Option
Type
Default
Description
colorCount
number
10
Number of colors in the palette (2–20). Used by getPalette and getSwatches.
quality
number
10
Pixel sampling rate. 1 samples every pixel (highest quality, slowest). 10 samples every 10th pixel.
region
object
—
Sample only a sub-rectangle: { x, y, width, height } in normalized 0–1 coordinates from the top-left. A region running past the right or bottom edge is clamped to the image; out-of-range or zero-sized values throw.
colorSpace
string
'oklch'
'oklch' or 'rgb'. OKLCH produces more perceptually uniform palettes.
gamut
string
'srgb'
'srgb', 'display-p3', or 'auto'. Read wide-gamut colors from Display P3 images instead of collapsing them to sRGB. 'auto' upgrades to P3 only when the image actually uses out-of-sRGB colors. Browser only; Node output is sRGB. Falls back to sRGB when P3 canvases are unsupported.
ignoreWhite
boolean
true
Skip pixels that appear white during sampling.
whiteThreshold
number
250
RGB channel value (0–255) above which a pixel is considered white.
alphaThreshold
number
125
Alpha value (0–255) below which a pixel is considered transparent and skipped.
minSaturation
number
0
Minimum HSV saturation (0–1). Pixels below this saturation are skipped.
signal
AbortSignal
—
Cancel a running extraction. Async API only.
Color object
Every extracted color is a rich object with format conversions, accessibility metadata, and WCAG contrast ratios.
Methods
Method
Returns
Description
rgb(gamut?)
{ r, g, b }
RGB values, each 0–255. Defaults to sRGB (gamut-mapped for wide-gamut colors). Pass 'display-p3' for the raw P3 components.
hex()
string
Hex string, e.g. '#e84393'. Always sRGB (gamut-mapped) so existing consumers keep working.
hsl()
{ h, s, l }
Hue 0–360, saturation 0–100, lightness 0–100.
oklch()
{ l, c, h }
Lightness 0–1, chroma 0–0.4, hue 0–360. Reports the true wider chroma for wide-gamut colors.
css(format?)
string
CSS color string. Format: 'rgb' (default), 'hsl', or 'oklch'. For a 'display-p3' color, the default emits color(display-p3 …).
array()
[r, g, b]
RGB tuple as a three-element array. Always sRGB (gamut-mapped).
toString()
string
Hex string. Allows direct use in template literals.
Properties
Property
Type
Description
gamut
string
'srgb' or 'display-p3' — the color space this color was extracted in.
textColor
string
'#ffffff' or '#000000' — the recommended foreground text color for readability.
isDark
boolean
true if the color is perceptually dark (relative luminance ≤ 0.179).
isLight
boolean
true if the color is perceptually light.
contrast
ContrastInfo
WCAG contrast ratios. Contains white (number), black (number), and foreground (Color) — a Color object for readable text.
population
number
Relative pixel count from the quantizer. Higher values mean more dominant.
getSwatches() and getSwatchesSync() return a SwatchMap — an object with six keys, one for each semantic role. Each value is either a Swatch or null if no color matched that role.
options.colorSpace — 'oklch' or 'rgb'. Default: 'oklch'.
Also accepts all filter options: ignoreWhite, whiteThreshold, alphaThreshold, minSaturation.
ObserveController
Method
Description
stop()
Stops observing and cleans up all event listeners and animation frames. Always call this when done.
Behavior by source type
HTMLVideoElement — Extracts from the current frame on each requestAnimationFrame (throttled). Only runs while the video is playing. Also fires on seeked.
HTMLCanvasElement — Polls on each requestAnimationFrame (throttled).
HTMLImageElement — Extracts immediately if loaded, then watches for src/srcset attribute changes via MutationObserver. Also listens for the load event.