@kayxean/chromatrix
v1.0.6
Published
CIEXYZ-based color engine using Float32Array matrix pooling
Downloads
194
Maintainers
Readme
Chromatrix
A high-performance, heavy-duty color engine for the web. Designed for accuracy and efficiency, it uses Float32Array buffers and CIEXYZ hubs to ensure mathematically sound conversions without the memory overhead of typical object-heavy color libraries.
Usage
Install via your preferred package manager:
# vite+
vp add @kayxean/chromatrix
# pnpm
pnpm add @kayxean/chromatrix
# npm
npm install @kayxean/chromatrix
# yarn
yarn add @kayxean/chromatrix
# bun
bun add @kayxean/chromatrixCore Library
Foundational API for buffer pool management, color space conversion, CSS parsing and formatting, and Color object lifecycle. All functions operate on Float32Array buffers using a shared pool for performance.
[!CAUTION] Modifying the internal
valuebuffer directly can bypass the library's space tracking. Always usemutateColor()or library utilities to ensure conversion integrity.
Color
Creates, manages, and manipulates Color objects and their underlying Float32Array buffers.
createMatrix()
Retrieves a Float32Array(3) buffer from the object pool.
export function createMatrix(): Float32Array;- This function retrieves the next available
Float32Array(3)from the internal pre-allocated pool to avoid garbage collection.
const matrix = createMatrix();[!IMPORTANT] Pool Limit: The pool has a maximum of 2048 buffers. When exhausted,
createMatrixreturns a "dead cell" buffer.
dropMatrix()
Returns a Float32Array buffer to the object pool for reuse.
export function dropMatrix(arr: Float32Array): void;arr: The buffer to return to the pool.
dropMatrix(matrix);[!TIP] Always return buffers to the pool when finished to prevent "dead cells" caused by pool exhaustion.
mountMatrix()
Initializes the object pool with a specific number of pre-allocated buffers.
export function mountMatrix(size: number): void;size: Number of buffers to pre-allocate (max 2048).
mountMatrix(50);clearMatrix()
Empties the entire object pool.
export function clearMatrix(): void;- Resets the internal pool head to its initial state, effectively clearing all pre-allocated memory.
clearMatrix();countMatrix()
Returns the number of buffers currently available in the pool.
export function countMatrix(): number;- Returns the number of currently available (unclaimed) buffers in the internal object pool.
const count = countMatrix();createColor()
Creates a Color object with a space, value buffer, and optional alpha.
export function createColor<S extends Space>(space: S, values: Float32Array, alpha = 1): Color<S>;space: The target color space.values: Float32Array with 3 color components.alpha: Optional alpha value (0 to 1). Defaults to1.
const red = createColor('rgb', new Float32Array([1, 0, 0]));dropColor()
Releases a Color object's underlying buffer back to the pool.
export function dropColor<S extends Space>(color: Color<S>): void;color: The color object to release.
dropColor(red);[!TIP] Releasing colors back to the pool is critical in high-frequency operations like animations or real-time color picking.
cloneColor()
Creates a deep copy of a Color object using a new buffer from the pool.
export function cloneColor<S extends Space>(color: Color<S>): Color<S>;color: The color to clone.
const copy = cloneColor(original);mutateColor()
Converts a color to a different space in-place, modifying the original object.
export function mutateColor<S extends Space>(color: Color<S>, to: S): asserts color is Color<S>;color: The color to convert.to: The target color space.
mutateColor(color, 'oklch');[!WARNING] This function performs in-place mutation. The input
colorobject is modified directly and itsspaceproperty is updated.
deriveColor()
Creates a new Color in a target space without modifying the original.
export function deriveColor<S extends Space, T extends Space>(color: Color<S>, to: T): Color<T>;color: The source color.to: The target color space.
const lch = deriveColor(rgb, 'oklch');Convert
convertColor()
Converts raw buffer values between spaces using CIEXYZ hubs.
export function convertColor(
input: Float32Array,
output: Float32Array,
from: Space,
to: Space,
): void;input: Source color values.output: Destination buffer (can be same as input).from: Source color space.to: Target color space.
convertColor(v1, v2, 'rgb', 'oklch');Format
formatCss()
Converts Color objects to CSS functional notation or hex strings.
export function formatCss<S extends Space>(color: Color<S>, asHex = false, precision = 2): string;color: TheColorobject to format.asHex: Output as hex string if space is 'rgb'.precision: Number of decimal places (default: 2).
formatCss(color, true); // "#ff0000"Parse
parseColor()
Parses any valid CSS color string into a Color object.
export function parseColor(s: string): Color<Space>;s: The CSS color string (hex, rgb, hsl, lab, lch, oklab, oklch, color()).
const color = parseColor('oklch(60% 0.15 30)');[!NOTE]
parseColor()is the primary entry point for string parsing. It automatically detects and handles hex, functional notations (rgb,hsl,lab,oklch), and thecolor()function.
Types
Core type definitions used across the library.
Space
The supported color spaces.
export type Space =
| 'rgb'
| 'hsl'
| 'hsv'
| 'hwb'
| 'lab'
| 'lch'
| 'oklab'
| 'oklch'
| 'lrgb'
| 'xyz50'
| 'xyz65';Color
The core color object.
export type Color<S extends Space> = {
space: S;
value: Float32Array;
alpha: number;
};Mutable
Utility type for in-place modifications.
export type Mutable<S extends Color<Space>> = { -readonly [K in keyof S]: S[K] };Utilities
Higher-level tools for color manipulation and analysis. Remember to dropColor() when done with generated objects.
Adjust
In-place color adjustments using OKLCH or RGB space.
lighten()
Increases lightness in OKLCH space.
export function lighten(color: Color<Space>, ratio: number): void;color: Color to adjust (mutated in-place).ratio: 0 to 1 (maximize lightness).
lighten(color, 0.2);[!WARNING] All adjustment functions (lighten, darken, saturate, etc.) mutate the input color object in-place and automatically convert it to
oklchspace for perceptual accuracy.
darken()
Decreases lightness in OKLCH space.
export function darken(color: Color<Space>, ratio: number): void;color: Color to adjust (mutated in-place).ratio: 0 to 1 (minimize lightness).
darken(color, 0.2);saturate()
Increases chroma in OKLCH space.
export function saturate(color: Color<Space>, ratio: number): void;color: Color to adjust (mutated in-place).ratio: 0 to 1 (maximize chroma).
saturate(color, 0.2);desaturate()
Decreases chroma in OKLCH space.
export function desaturate(color: Color<Space>, ratio: number): void;color: Color to adjust (mutated in-place).ratio: 0 to 1 (minimize chroma).
desaturate(color, 0.2);whiten()
Increases lightness and decreases chroma in OKLCH space.
export function whiten(color: Color<Space>, ratio: number): void;color: Color to adjust (mutated in-place).ratio: 0 to 1 (maximize lightness, minimize chroma).
whiten(color, 0.2);blacken()
Decreases lightness and chroma in OKLCH space.
export function blacken(color: Color<Space>, ratio: number): void;color: Color to adjust (mutated in-place).ratio: 0 to 1 (minimize lightness and chroma).
blacken(color, 0.2);rotate()
Rotates hue by specified degrees in OKLCH space.
export function rotate(color: Color<Space>, angle: number): void;color: Color to adjust (mutated in-place).angle: Degrees to rotate (0-360).
rotate(color, 45);invert()
Inverts RGB components in linear RGB space.
export function invert(color: Color<Space>): void;color: Color to invert (mutated in-place).
invert(color);matchLuminance()
Copies the lightness component from source to target in OKLCH space.
export function matchLuminance(source: Color<Space>, target: Color<Space>): void;source: Lightness source color.target: Color to overwrite (mutated in-place).
matchLuminance(source, target);Analyze
Perceptual analysis and chromatic adaptation.
getDistance()
Calculates perceptual distance between two colors.
export function getDistance(
colorA: Color<Space>,
colorB: Color<Space>,
method: 'oklab' | 'deltaE2000' | 'itp' = 'oklab',
): number;colorA,colorB: Colors to compare.method: Distance metric (default:'oklab').
const dist = getDistance(c1, c2, 'deltaE2000');[!TIP]
oklabis the fastest method and recommended for most UI tasks.itp(ICtCp) is highly accurate for high-dynamic-range content.
sortColors()
Sorts an array of colors by perceptual attributes or distance.
export function sortColors(
colors: Readonly<Color<Space>[]>,
by: 'luminance' | 'hue' | 'chroma' | 'distance',
target?: Color<Space>,
): Color<Space>[];colors: Array of colors to sort.by: Attribute to sort by.target: Reference color for'distance'sorting.
const sorted = sortColors(palette, 'luminance');averageColor()
Calculates the perceptual average of an array of colors.
export function averageColor(colors: Readonly<Color<Space>[]>): Color<Space>;colors: Array of colors to average.
const avg = averageColor([c1, c2, c3]);[!NOTE]
averageColor()performs calculation in Oklab space to ensure the resulting average is perceptually balanced and avoids hue shifts.
adaptColor()
Applies Chromatic Adaptation Transform (Bradford CAT).
export function adaptColor(
color: Color<Space>,
from: 'd65' | 'd50',
to: 'd65' | 'd50',
): Color<Space>;color: Color to adapt.from,to: Source and target white points.
const adapted = adaptColor(color, 'd65', 'd50');[!IMPORTANT]
adaptColor()utilizes the Bradford Chromatic Adaptation Transform, which is essential when mixing colors calibrated for different white points (e.g., standard sRGB D65 and print-standard D50).
isEqual()
Checks if two colors are perceptually identical.
export function isEqual<S extends Space, T extends Space>(
a: Color<S>,
b: Color<T>,
tolerance = 0.001,
): boolean;a,b: Colors to compare.tolerance: Perceptual threshold (default:0.001).
const equal = isEqual(c1, c2, 0.001);Contrast
Accessibility and contrast scoring.
getContrast()
Calculates the APCA contrast score.
export function getContrast(text: Color<Space>, background: Color<Space>): number;text: Foreground color.background: Background color.
const score = getContrast(text, bg);[!NOTE]
getContrast()uses the APCA (Accessible Perceptual Contrast Algorithm), which accounts for font weight and spatial frequency for more accurate accessibility scoring.
getContrastRating()
Converts a raw APCA score to a human-readable rating.
export function getContrastRating(
score: number,
): 'platinum' | 'gold' | 'silver' | 'bronze' | 'ui' | 'fail';score: APCA contrast score.
const rating = getContrastRating(score);getContrastRatio()
Calculates the standard WCAG 2.x contrast ratio.
export function getContrastRatio(colorA: Color<Space>, colorB: Color<Space>): number;colorA,colorB: Colors to compare.
const ratio = getContrastRatio(c1, c2);isAccessible()
Checks if a color pair meets WCAG standards.
export function isAccessible(
text: Color<Space>,
background: Color<Space>,
level: 'AA' | 'AAA' = 'AA',
isLargeText = false,
): boolean;text,background: Color pair to check.level: Accessibility level (default:'AA').isLargeText: Whether text size is large (default:false).
const ok = isAccessible(text, bg, 'AAA', true);matchContrast()
Adjusts a color's lightness to meet a target APCA contrast.
export function matchContrast(
color: Color<Space>,
background: Color<Space>,
targetContrast: number,
): void;color: Color to adjust (mutated in-place).background: Reference background.targetContrast: Target APCA score.
matchContrast(color, bg, 75);[!WARNING]
matchContrast()uses an iterative approach. If the background and target contrast range are physically impossible to reach in sRGB, the function will clamp to the closest possible value.
pickContrast()
Selects the color from an array with the highest contrast.
export function pickContrast(
background: Color<Space>,
options: Readonly<Color<Space>[]>,
): Color<Space>;background: Reference background.options: Array of candidate colors.
const best = pickContrast(bg, [c1, c2, c3]);Gamut
Range checking and clamping.
inGamut()
Checks if a color is within the valid sRGB gamut.
export function inGamut(color: Color<Space>, epsilon = 0.0001): boolean;color: Color to check.epsilon: Tolerance (default:0.0001).
if (!inGamut(color)) toGamut(color);[!IMPORTANT] Wide-gamut spaces like Oklab can represent colors outside the sRGB range. Use
toGamut()before CSS formatting to ensure the color displays correctly on standard monitors.
toGamut()
Clamps a color's chroma in OKLCH space to fit within sRGB.
export function toGamut(color: Color<Space>): void;color: Color to clamp (mutated in-place).
toGamut(color);clampRgb()
Clamps RGB values to 0-1 range.
export function clampRgb(color: Color<Space>): void;color: Color to clamp (mutated in-place).
clampRgb(color);[!NOTE]
clampRgb()is intended forrgbandlrgbspaces. Whilexyz50andxyz65share a similar 0-1 range, clamping them directly with this function is not recommended as it may distort chromaticity.
clampHsv()
Clamps HSV values.
export function clampHsv(color: Color<Space>): void;color: Color to clamp (mutated in-place).
clampHsv(color);[!NOTE]
clampHsv()is intended forhsl,hsv, andhwbspaces.
clampCartesian()
Clamps lightness for Cartesian spaces (lab, oklab).
export function clampCartesian(color: Color<Space>): void;color: Color to clamp (mutated in-place).
clampCartesian(color);[!NOTE]
clampCartesian()is intended forlabandoklabspaces.
clampPolar()
Clamps polar space values (lightness, chroma, hue).
export function clampPolar(color: Color<Space>): void;color: Color to clamp (mutated in-place).
clampPolar(color);[!NOTE]
clampPolar()is intended forlchandoklchspaces.
Gradient
CSS gradient generation.
GradientType
Supported gradient interpolation types.
export type GradientType = 'linear' | 'radial' | 'conic';GradientStop
Used for defining CSS gradient stops.
export type GradientStop = Readonly<{
color: Color<Space>;
position?: number;
}>;createLinearGradient()
Creates a CSS linear gradient string.
export function createLinearGradient(stops: ReadonlyArray<GradientStop>, angle = 180): string;stops: Array of gradient stops.angle: Rotation angle (default:180).
const css = createLinearGradient([
{ color: c1, position: 0 },
{ color: c2, position: 100 },
]);createRadialGradient()
Creates a CSS radial gradient string.
export function createRadialGradient(
stops: ReadonlyArray<GradientStop>,
shape = 'ellipse',
position = 'center',
): string;stops: Array of gradient stops.shape: Gradient shape (default:'ellipse').position: Origin position (default:'center').
const css = createRadialGradient(stops, 'circle', 'center');createConicGradient()
Creates a CSS conic gradient string.
export function createConicGradient(
stops: ReadonlyArray<GradientStop>,
angle = 0,
position = 'center',
): string;stops: Array of gradient stops.angle: Starting angle (default:0).position: Center position (default:'center').
const css = createConicGradient(stops, 0, 'center');createSmoothGradient()
Generates a gradient with interpolated steps between two colors.
export function createSmoothGradient(
start: Color<Space>,
end: Color<Space>,
steps: number,
type: GradientType = 'linear',
options?: Readonly<{ angle?: number; shape?: 'circle' | 'ellipse'; position?: string }>,
): string;start,end: Boundary colors.steps: Number of interpolation steps.type: Gradient type (default:'linear').options: Display options (angle, shape, position).
const css = createSmoothGradient(red, blue, 10, 'linear');createMultiColorGradient()
Creates a gradient passing through multiple colors in sequence.
export function createMultiColorGradient(
colors: ReadonlyArray<Color<Space>>,
type: GradientType = 'linear',
options?: Readonly<{ angle?: number; shape?: 'circle' | 'ellipse'; position?: string }>,
): string;colors: Array of colors to include.type: Gradient type (default:'linear').options: Display options.
const css = createMultiColorGradient([c1, c2, c3], 'conic');Palette
Color harmonies and scales.
mixColor()
Interpolates between two colors in polar space.
export function mixColor(colorA: Color<Space>, colorB: Color<Space>, ratio: number): void;colorA: Base color (mutated in-place).colorB: Target color to mix toward.ratio: Mix percentage (0 to 1).
mixColor(color, target, 0.5);[!TIP]
mixColor()performs interpolation in Oklch space, which prevents the muddy gray transitions often seen in RGB-based color mixing.
mixSubtractive()
Subtractive color mixing in linear RGB space.
export function mixSubtractive(colorA: Color<Space>, colorB: Color<Space>, ratio = 0.5): void;colorA: Base color (mutated in-place).colorB: Target color.ratio: Mix percentage (default:0.5).
mixSubtractive(color, target, 0.5);createHarmony()
Generates colors by rotating hue.
export function createHarmony(input: Color<Space>, ratios: Readonly<number[]>): Color<Space>[];input: Base color.ratios: Array of hue rotation offsets.
const triad = createHarmony(color, [120, 240]);createScales()
Creates a multi-stop color scale.
export function createScales(stops: Readonly<Color<Space>[]>, steps: number): Color<Space>[];stops: Sequence of anchor colors.steps: Total colors to generate.
const scale = createScales([c1, c2, c3], 9);createTints()
Generates tints from color to white.
export function createTints(color: Color<Space>, steps: number): Color<Space>[];color: Base color.steps: Number of tints to generate.
const tints = createTints(color, 5);createShades()
Generates shades from color to black.
export function createShades(color: Color<Space>, steps: number): Color<Space>[];color: Base color.steps: Number of shades to generate.
const shades = createShades(color, 5);createTonal()
Generates a full tonal scale.
export function createTonal(color: Color<Space>, steps = 9): Color<Space>[];color: Base color.steps: Total colors in the scale (default:9).
const tones = createTonal(color, 9);[!NOTE] By default,
createTonal()generates a 9-step scale ranging from 0% to 100% lightness while preserving the input's hue and chroma in Oklch space.
Picker
PickerFn
Callback type for interactive picker updates.
export type PickerFn = <S extends Space>(hsv: Color<S>) => void;createPicker()
Creates an interactive picker instance with state management.
export function createPicker(color: Color<Space>): {
update: (x: number, y: number, type: 'sv' | 'h' | 'a') => void;
assign: (next: Color<Space>) => void;
setHue: (h: number) => void;
setSaturation: (s: number) => void;
setValue: (v: number) => void;
setAlpha: (a: number) => void;
subscribe: (fn: PickerFn) => () => void;
getState: () => Color<Space>;
getValue: () => { h: number; s: number; v: number; a: number };
getHue: () => number;
getSaturation: () => number;
getBrightness: () => number;
getAlpha: () => number;
getColor: () => Color<Space>;
getSolid: () => Color<Space>;
dispose: () => void;
};color: Initial color for the picker.
const picker = createPicker(color);
picker.subscribe((c) => console.log(formatCss(c)));
picker.update(0.5, 0.5, 'sv');[!IMPORTANT] You must call
.dispose()when the picker is no longer needed (e.g., component unmount) to release its internal color buffer back to the shared pool.
Simulate
Vision deficiency and environmental filters.
DeficiencyType
Supported color vision deficiency simulations.
export type DeficiencyType = 'protanopia' | 'deuteranopia' | 'tritanopia' | 'achromatopsia';simulateDeficiency()
Applies color vision deficiency simulation.
export function simulateDeficiency(
color: Color<Space>,
type: DeficiencyType | 'none',
severity: number = 1,
): void;color: Color to simulate (mutated in-place).type: Type of deficiency.severity: Intensity of the effect (default:1).
simulateDeficiency(color, 'protanopia', 1);simulateAmbient()
Blends color toward white to simulate glare.
export function simulateAmbient(color: Color<Space>, intensity = 0.4): void;color: Color to simulate (mutated in-place).intensity: Glare intensity (default:0.4).
simulateAmbient(color, 0.4);simulateNightMode()
Reduces saturation and value for night vision.
export function simulateNightMode(color: Color<Space>, intensity = 0.5): void;color: Color to simulate (mutated in-place).intensity: Filter intensity (default:0.5).
simulateNightMode(color, 0.5);simulateLowLight()
Mixes color with luminance for low light simulation.
export function simulateLowLight(color: Color<Space>, darkness = 0.5): void;color: Color to simulate (mutated in-place).darkness: Environment darkness (default:0.5).
simulateLowLight(color, 0.5);simulateCataract()
Reduces blue channel and adds fog for cataract simulation.
export function simulateCataract(color: Color<Space>, severity = 0.5): void;color: Color to simulate (mutated in-place).severity: Severity of cataract (default:0.5).
simulateCataract(color, 0.5);simulateSunlight()
Blends color toward white for sunlight simulation.
export function simulateSunlight(color: Color<Space>, intensity = 0.7): void;color: Color to simulate (mutated in-place).intensity: Light intensity (default:0.7).
simulateSunlight(color, 0.7);[!WARNING] Simulated values may exceed 0-1 range. Use
clampRgb()ortoGamut()before formatting.
The Math
Rather than writing thousands of individual conversion formulas, this library uses a Hub and Bridge architecture.
- The Hubs: Modern spaces (
rgb,oklab) target CIEXYZ D65. Reference spaces (lab,lch) target CIEXYZ D50. - The Bridge: When moving between hubs, we use a Bradford CAT (Chromatic Adaptation Transform). This prevents the "color shift" usually seen when switching between D50 and D65 standards.
By using a Float32Array pool, the library performs these complex matrix multiplications without triggering the garbage collector.
Development
This project uses Vite+ for building, testing, and development tooling.
| Command | Description |
| -------------------- | -------------------------------------------------------------------------- |
| vp pack | Transpiles and bundles the library into dist/ using the Rolldown engine. |
| vp pack --watch | Rebuilds your color utilities in real-time as you save changes. |
| vp test | Test the Vitest suite using the integrated test runner. |
| vp test --coverage | Runs tests and generates a 100% threshold V8 coverage report. |
| vp test bench | Runs performance benchmarks for matrix and color operations. |
| vp check | Analyzes the codebase for errors and style issues using Oxlint. |
| vp check --fix | Automatically fixes linting errors and formats code via Oxfmt. |
