web-color-extractor
v1.0.0
Published
High-performance, zero-dependency client-side dynamic color palette extractor using HTML5 Canvas, MMCQ algorithm, and Web Workers.
Downloads
28
Maintainers
Readme
web-color-extractor 🎨
High-performance, zero-dependency client-side dynamic color palette extraction library and React Hook for Web & PWAs. Powered by HTML5 Canvas, Modified Median Cut Quantization (MMCQ), and Web Workers.
✨ Features
- ⚡ Non-Blocking Web Worker Execution: Offloads heavy pixel sampling and 3D RGB quantization to background threads to guarantee smooth 60 FPS UI performance.
- 🎨 7 Categorized Profile Tokens: Automatically categorizes colors into
dominant,vibrant,lightVibrant,darkVibrant,muted,lightMuted, anddarkMutedswatches. - ⚛️ First-Class React Integration: Includes the reactive
useColorExtractorhook with loading and error states out of the box. - 🛡️ WCAG 2.1 Contrast Validator: Calculates relative luminance and contrast ratios according to W3C standards to auto-select accessible white vs. black text.
- 📷 Universal Image Source Input: Accepts image URLs (
string),HTMLImageElement,HTMLCanvasElement, file uploads (File), andBlob. - 📦 Zero External Dependencies: Lightweight and self-contained footprint for fast web load times.
📦 Installation
# Using npm
npm install web-color-extractor
# Using yarn
yarn add web-color-extractor
# Using pnpm
pnpm add web-color-extractor🚀 Quickstart Guide
1. React Hook (useColorExtractor)
import React from 'react';
import { useColorExtractor } from 'web-color-extractor';
export function DynamicAlbumCard({ imageSrc }: { imageSrc: string }) {
const { palette, loading, error, processTimeMs } = useColorExtractor(imageSrc, {
maxColors: 10,
useWorker: true,
});
if (loading) return <div>Extracting palette...</div>;
if (error) return <div>Failed to extract palette</div>;
return (
<div
style={{
backgroundColor: palette?.darkVibrant?.hex || '#0f172a',
color: palette?.vibrant?.hex || '#38bdf8',
padding: '24px',
borderRadius: '20px',
transition: 'all 0.5s ease',
}}
>
<h2>{palette?.vibrant?.hex}</h2>
<p>Extraction finished in {processTimeMs} ms</p>
</div>
);
}2. Vanilla JavaScript / Async API (extractPalette)
import { extractPalette } from 'web-color-extractor';
async function applyTheme(imageSource: string | File | HTMLImageElement) {
const palette = await extractPalette(imageSource, {
quality: 5, // Downsample step (1 = highest quality, 5 = faster)
maxColors: 10, // Number of colors to quantize
useWorker: true, // Process MMCQ in Web Worker thread
maxDimension: 300, // Max canvas resolution before downsampling
});
console.log('Dominant Color:', palette.dominant?.hex);
console.log('Vibrant Color:', palette.vibrant?.hex);
console.log('Dark Vibrant:', palette.darkVibrant?.hex);
console.log('All Quantized Swatches:', palette.allSwatches);
// Apply to CSS custom properties
document.documentElement.style.setProperty('--bg-dynamic', palette.darkVibrant?.hex || '#121826');
document.documentElement.style.setProperty('--accent-dynamic', palette.vibrant?.hex || '#38bdf8');
}📖 API Reference
extractPalette(source, options?)
Extracts a categorized ColorPalette asynchronously.
source:string | HTMLImageElement | HTMLCanvasElement | File | Bloboptions:ExtractorOptions(Optional)
ExtractorOptions
| Option | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| maxColors | number | 10 | Maximum number of colors to quantize. |
| quality | number | 5 | Downsample step size: 1 samples every pixel, 5 samples every 5th pixel for speed. |
| maxDimension | number | 300 | Max width/height dimension to scale canvas image down before processing. |
| useWorker | boolean | true | Runs MMCQ algorithm inside a Web Worker thread if supported. |
| alphaThreshold | number | 125 | Skips transparent/semi-transparent pixels (0-255). |
| ignoreWhite | boolean | false | Ignores near-white background padding pixels. |
useColorExtractor(source, options?)
Custom React hook returning state object:
interface UseColorExtractorState {
palette: ColorPalette | null;
loading: boolean;
error: Error | null;
processTimeMs: number;
}📊 Data Structures
ColorPalette
interface ColorPalette {
dominant: ColorSwatch | null; // Most frequent color
vibrant: ColorSwatch | null; // High saturation, balanced lightness
lightVibrant: ColorSwatch | null; // High saturation, high lightness
darkVibrant: ColorSwatch | null; // High saturation, low lightness
muted: ColorSwatch | null; // Low saturation, balanced lightness
lightMuted: ColorSwatch | null; // Low saturation, high lightness
darkMuted: ColorSwatch | null; // Low saturation, low lightness
allSwatches: ColorSwatch[]; // All quantized swatches ordered by population
}ColorSwatch
interface ColorSwatch {
rgb: [number, number, number]; // [r, g, b] (0-255)
hex: string; // "#38BDF8"
hsl: { h: number; s: number; l: number }; // h: 0-360, s: 0-100, l: 0-100
population: number; // Pixel count in bucket
percentage: number; // % of total image coverage
isDark: boolean; // True if luminance < 0.35
}🛡️ Utilities
The library also exports standalone utility functions for WCAG accessibility and color conversions:
import { evaluateWCAG, getContrastRatio, rgbToHex, rgbToHsl } from 'web-color-extractor';
// Evaluate text contrast compliance for a background RGB
const wcag = evaluateWCAG([18, 24, 38]);
console.log(wcag.contrastRatio); // e.g. 14.2:1
console.log(wcag.preferredTextColor); // "#FFFFFF" or "#000000"
console.log(wcag.scoreAA); // true
console.log(wcag.scoreAAA); // true🛠️ Local Development & Running the Showcase App
Clone & Install Dependencies:
cd web-color-extractor npm installStart Dev Server:
npm run devOpen
http://localhost:3000to view the Material 3 showcase demo app.Build for Production:
npm run build
