color-thief

GitHub

Grab the color palette from an image using just Javascript. Works in the browser and in Node.

13,616 stars TypeScript
RAW Doc

Repository: lokesh/color-thief


Stars: 13585

README.md

Color Thief

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

![npm version](https://www.npmjs.com/package/colorthief)
![npm bundle size](https://bundlephobia.com/package/colorthief)
![types](https://www.npmjs.com/package/colorthief)

Install

bash
npm install colorthief

Or load directly from a CDN:

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

Quick Start

js
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

| Function | Description |
|---|---|
| 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

| Option | Default | Description |
|---|---|---|
| colorCount | 10 | Number of palette colors (2–20) |
| quality | 10 | Sampling rate (1 = every pixel, 10 = every 10th) |
| colorSpace | 'oklch' | Quantization space: 'rgb' or 'oklch' |
| worker | false | Offload to Web Worker (browser only) |
| signal | — | AbortSignal to cancel extraction |
| ignoreWhite | true | Skip white pixels |

Color Object

| Property / Method | Returns |
|---|---|
| .rgb() | { r, g, b } |
| .hex() | '#ff8000' |
| .hsl() | { h, s, l } |
| .oklch() | { l, c, h } |
| .css(format?) | 'rgb(255, 128, 0)', 'hsl(…)', or 'oklch(…)' |
| .array() | [r, g, b] |
| .toString() | Hex string (works in template literals) |
| .textColor | '#ffffff' or '#000000' |
| .isDark / .isLight | Boolean |
| .contrast | { white, black, foreground } — WCAG ratios |
| .population | Raw pixel count |
| .proportion | 0–1 share of total |

Browser

js
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.

Live extraction with observe()

js
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

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

bash
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

bash

Dominant color


colorthief-cli photo.jpg

Color palette


colorthief-cli palette photo.jpg

Semantic swatches


colorthief-cli swatches photo.jpg

Output formats

bash

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

bash
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:

bash
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).

- Demo page & live examples
- GitHub
- npm

Contributing

bash
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

bash

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