Color Thief

July 1, 2026 · View on GitHub

Extract dominant colors and palettes from images in the browser and Node.js.

npm version npm bundle size types

Install

npm install colorthief

Or load directly from a CDN:

<script src="https://unpkg.com/colorthief@3/dist/umd/color-thief.global.js"></script>

Quick Start

import { getColorSync, getPaletteSync, getSwatches } from 'colorthief';

// Dominant color
const color = getColorSync(img);
color.hex();      // '#e84393'
color.css();      // 'rgb(232, 67, 147)'
color.isDark;     // false
color.textColor;  // '#000000'

// Palette
const palette = getPaletteSync(img, { colorCount: 6 });
palette.forEach(c => console.log(c.hex()));

// Semantic swatches (Vibrant, Muted, DarkVibrant, etc.)
const swatches = await getSwatches(img);
swatches.Vibrant?.color.hex();

Features

  • TypeScript — full type definitions included
  • Browser + Node.js — same API, both platforms
  • Sync & async — synchronous browser API, async for Node.js and Web Workers
  • Live extractionobserve() watches video, canvas, or img elements and emits palette updates reactively
  • Web Workers — offload quantization off the main thread with worker: true
  • Progressive extraction — 3-pass refinement for instant rough results
  • OKLCH quantization — perceptually uniform palettes via colorSpace: 'oklch'
  • Semantic swatches — Vibrant, Muted, DarkVibrant, DarkMuted, LightVibrant, LightMuted
  • Rich Color objects.hex(), .rgb(), .hsl(), .oklch(), .css(), contrast ratios, text color recommendations
  • WCAG contrastcolor.contrast.white, color.contrast.black, color.contrast.foreground
  • AbortSignal — cancel in-flight extractions
  • CLIcolorthief photo.jpg with JSON, CSS, and ANSI output
  • Zero runtime dependencies

API at a Glance

FunctionDescription
getColorSync(source, options?)Dominant color (sync, browser only)
getPaletteSync(source, options?)Color palette (sync, browser only)
getSwatchesSync(source, options?)Semantic swatches (sync, browser only)
getColor(source, options?)Dominant color (async, browser + Node.js)
getPalette(source, options?)Color palette (async, browser + Node.js)
getSwatches(source, options?)Semantic swatches (async, browser + Node.js)
getPaletteProgressive(source, options?)3-pass progressive palette (async generator)
observe(source, options)Watch a source and emit palette updates (browser only)
createColor(r, g, b, population)Build a Color object from RGB values

Options

OptionDefaultDescription
colorCount10Number of palette colors (2–20)
quality10Sampling rate (1 = every pixel, 10 = every 10th)
colorSpace'oklch'Quantization space: 'rgb' or 'oklch'
gamut'srgb'Output gamut: 'srgb', 'display-p3', or 'auto' (browser only)
workerfalseOffload to Web Worker (browser only)
signalAbortSignal to cancel extraction
ignoreWhitetrueSkip white pixels

Color Object

Property / MethodReturns
.rgb(){ r, g, b }
.hex()'#ff8000'
.hsl(){ h, s, l }
.oklch(){ l, c, h }
.css(format?)'rgb(255, 128, 0)', 'hsl(…)', or 'oklch(…)' — a P3 color's default .css() is color(display-p3 …)
.array()[r, g, b]
.gamut'srgb' or 'display-p3'
.toString()Hex string (works in template literals)
.textColor'#ffffff' or '#000000'
.isDark / .isLightBoolean
.contrast{ white, black, foreground } — WCAG ratios
.populationRaw pixel count
.proportion0–1 share of total

Browser

import { getColorSync, getPaletteSync } from 'colorthief';

const img = document.querySelector('img');
const color = getColorSync(img);
console.log(color.hex());

const palette = getPaletteSync(img, { colorCount: 5 });

Accepts HTMLImageElement, HTMLCanvasElement, HTMLVideoElement, ImageData, ImageBitmap, and OffscreenCanvas.

Wide-gamut (Display P3)

By default colors are read and reported in sRGB. For P3-tagged / wide-gamut images, pass gamut to preserve the extra saturation:

// Force P3: read the image through a P3 canvas, report P3 colors
const palette = await getPalette(img, { gamut: 'display-p3' });
palette[0].css();            // 'color(display-p3 0.92 0.2 0.14)'
palette[0].gamut;            // 'display-p3'

// Auto: report P3 only when the image actually uses out-of-sRGB colors,
// otherwise behave exactly like sRGB
const auto = await getPalette(img, { gamut: 'auto' });

.rgb(), .array(), and .hex() always return sRGB (gamut-mapped when the color is P3), so existing rgb(...) strings keep working. The wide-gamut values live in .css() and .oklch(); use .rgb('display-p3') for the raw P3 components. Falls back to sRGB where P3 canvas support is unavailable. Node output is sRGB for now.

Live extraction with observe()

import { observe } from 'colorthief';

// Watch a video and update ambient lighting as it plays
const controller = observe(videoElement, {
    throttle: 200,    // ms between updates
    colorCount: 5,
    onChange(palette) {
        updateAmbientBackground(palette);
    },
});

// Stop when done
controller.stop();

Works with <video>, <canvas>, and <img> elements. For images, it uses a MutationObserver to detect src changes. For video and canvas, it polls using requestAnimationFrame with throttle.

Node.js

import { getColor, getPalette } from 'colorthief';

const color = await getColor('/path/to/image.jpg');
console.log(color.hex());

const palette = await getPalette(Buffer.from(data), { colorCount: 5 });

Accepts file paths and Buffers. Uses sharp for image decoding.

CLI

Quick start

npx colorthief-cli photo.jpg

The colorthief-cli package bundles everything needed (including sharp for image decoding), so it works immediately with no extra setup.

Commands

# Dominant color
colorthief-cli photo.jpg

# Color palette
colorthief-cli palette photo.jpg

# Semantic swatches
colorthief-cli swatches photo.jpg

Output formats

# Default: ANSI color swatches
colorthief-cli photo.jpg
# ▇▇ #e84393

# JSON with full color data
colorthief-cli photo.jpg --json

# CSS custom properties
colorthief-cli palette photo.jpg --css
# :root {
#     --color-1: #e84393;
#     --color-2: #6c5ce7;
# }

Options

colorthief-cli palette photo.jpg --count 5        # Number of colors (2-20)
colorthief-cli photo.jpg --quality 1              # Sampling quality (1=best)
colorthief-cli photo.jpg --color-space rgb        # Color space (rgb or oklch)

Stdin is supported — use - or pipe directly:

cat photo.jpg | colorthief-cli -

Multiple files are supported. Output is prefixed with filenames, and --json wraps results in an object keyed by filename.

Note: If you already have colorthief and sharp installed in a project, you can also use colorthief directly as the command name (without the -cli suffix).

Contributing

npm run build          # Build all dist formats
npm run test           # Run all tests (Mocha + Cypress)
npm run test:node      # Node tests only
npm run test:browser   # Browser tests (requires npm run dev)
npm run dev            # Start local server on port 8080

Releasing

# 1. Make sure you're on master with a clean working tree
git status

# 2. Run the full test suite
npm run build
npm run test:node
npm run test:browser   # requires npm run dev in another terminal

# 3. Preview what will be published
npm pack --dry-run

# 4. Tag and publish
npm version <major|minor|patch>   # bumps version, creates git tag
npm publish                       # builds via prepublishOnly, then publishes
git push && git push --tags

License

MIT - Lokesh Dhakar