Package Exports
- @instruments/colorscope
- @instruments/colorscope/accessibility
- @instruments/colorscope/analysis
- @instruments/colorscope/border
- @instruments/colorscope/convert
- @instruments/colorscope/embedding
- @instruments/colorscope/extraction
- @instruments/colorscope/format
- @instruments/colorscope/harmony
- @instruments/colorscope/match
- @instruments/colorscope/math
- @instruments/colorscope/mood
- @instruments/colorscope/naming
- @instruments/colorscope/naming/human
- @instruments/colorscope/naming/semantic
- @instruments/colorscope/naming/server
- @instruments/colorscope/palette
- @instruments/colorscope/profile
- @instruments/colorscope/sorting
- @instruments/colorscope/spectrum
- @instruments/colorscope/taxonomy
- @instruments/colorscope/temperature
- @instruments/colorscope/text-seed
- @instruments/colorscope/tolerance
- @instruments/colorscope/types
Readme
@instruments/colorscope
Color analysis toolkit for Node.js and browser-safe subpath imports. Colorscope extracts dominant colors, names and classifies colors with OKLab/OKLCH geometry, builds 128D palette embeddings, and exposes deterministic palette analysis utilities.
Install
pnpm add @instruments/colorscopesharp is an optional peer dependency: math, conversion, naming, analysis, and embedding subpaths work without it, so browser and math-only consumers skip the native install entirely. Image extraction (extraction/, border/, and the root barrel, which re-exports them) requires it — add it alongside colorscope in Node/server code:
pnpm add @instruments/colorscope sharpBreaking change (since sharp became optional): importing the root barrel without
sharpinstalled now fails at import time. Either installsharp, or switch to subpath imports (@instruments/colorscope/math,/convert,/naming, …) if you don't use extraction.
Use the root import from Node/server code, and use subpath imports from browser or React client components.
Quick Start
import { extractColorsFromUrl, withColorNames } from "@instruments/colorscope";
import { analyzePalette } from "@instruments/colorscope/analysis";
import { paletteToEmbedding } from "@instruments/colorscope/embedding";
const { colors } = await extractColorsFromUrl("https://example.com/product.jpg");
const named = withColorNames(colors);
const analysis = analyzePalette(named);
const embedding = paletteToEmbedding(named);Client-Safe Imports
import type { QuantizedColor } from "@instruments/colorscope/types";
import { analyzePalette } from "@instruments/colorscope/analysis";
import { hexToOklab } from "@instruments/colorscope/math";
import { getColorName, getColorAttributes } from "@instruments/colorscope/naming";
import { paletteToEmbedding } from "@instruments/colorscope/embedding";
import { generateHarmony } from "@instruments/colorscope/harmony";Server-only catalogue helpers live under @instruments/colorscope/naming/server because they load vendored library data from the package.
import { createColorResolver } from "@instruments/colorscope/naming";
import { getLibrary } from "@instruments/colorscope/naming/server";
const resolver = createColorResolver({
libraries: [getLibrary("core"), getLibrary("farrow-ball")].filter(Boolean),
});Published Subpaths
| Subpath | Purpose |
|---|---|
@instruments/colorscope |
Full Node/server barrel, including extraction |
@instruments/colorscope/extraction |
Sharp-backed image color and spectrum extraction |
@instruments/colorscope/border |
Uniform border detection for product imagery |
@instruments/colorscope/math |
HSL, RGB, OKLab math and perceptual distance |
@instruments/colorscope/convert |
OKLab, OKLCH, RGB, HSL, and hex conversion |
@instruments/colorscope/analysis |
Palette temperature, vibrancy, brightness, mood, clustering, and comparison |
@instruments/colorscope/embedding |
128D palette and region embeddings |
@instruments/colorscope/naming |
Canonical taxonomy, lexical attributes, and resolver core |
@instruments/colorscope/naming/semantic |
Hybrid lexical and semantic text resolver |
@instruments/colorscope/naming/server |
Built-in catalogue libraries for server runtimes |
@instruments/colorscope/palette |
Palette grids, anchor palettes, and distribution distance |
@instruments/colorscope/text-seed |
Deterministic colors and palettes from stable text identifiers |
@instruments/colorscope/mood |
Mood presets and OKLab region helpers |
@instruments/colorscope/sorting |
Palette sorting utilities |
@instruments/colorscope/spectrum |
Client-safe OKLCH spectrum model |
@instruments/colorscope/taxonomy |
Deterministic taxonomy descriptions and rule metadata |
@instruments/colorscope/temperature |
Warm/cool and light/dark appearance shifts |
@instruments/colorscope/tolerance |
UI tolerance slider to OKLab threshold mapping |
@instruments/colorscope/harmony |
Color harmonies and tonal palettes |
@instruments/colorscope/accessibility |
WCAG contrast and color-blindness simulation |
@instruments/colorscope/format |
CSS, Tailwind, and SCSS palette exports |
@instruments/colorscope/types |
Shared TypeScript data shapes |
Embeddings
paletteToEmbedding() returns a fixed 128D L2-normalized vector. Store EMBEDDING_VERSION next to persisted vectors and rebuild indexes when it changes.
import {
EMBEDDING_VERSION,
embeddingSimilarity,
paletteToEmbedding,
} from "@instruments/colorscope/embedding";
const a = paletteToEmbedding(["#c67a5c", "#8a9e82"]);
const b = paletteToEmbedding(["#bf7458", "#809775"]);
console.log(EMBEDDING_VERSION); // "oklch-anchor-quantiles-v11"
console.log(embeddingSimilarity(a, b));Semantic Naming
Semantic color resolution uses baked openai/text-embedding-3-small concept vectors and a caller-provided encoder.
import { createHybridColorResolver } from "@instruments/colorscope/naming/semantic";
const resolver = createHybridColorResolver({
encode: (text) => embedWithYourProvider(text),
});
await resolver.resolveColorName("warm limestone");Generate the package and docs with:
pnpm --filter @instruments/colorscope build
pnpm --filter @instruments/colorscope generate:api-docs