This project is part of the @thi.ng/umbrella monorepo.
For the Clojure version, please visit: thi.ng/color-clj
Array-based color types, CSS parsing, conversions, transformations, declarative theme generation, gradients, presets.
Note: This readme is a work-in-progress, partially outdated and will apply to the still unreleased version 3.0.0, a major overhaul of the entire package. Please see readme on main branch for the current version...
Fast color model/space conversions (any direction) between (in alphabetical order). All types support an alpha channel, which defaults to 100% opaque (apart from the integer types).
- ABGR (uint32,
0xaabbggrr
, aka sRGB(A) as packed int) - ARGB (uint32,
0xaarrggbb
, aka sRGB(A) as packed int) - CSS (string, hex3/hex4/hex6/hex8, named colors, rgba(), hsla(), etc.)
- HCY (float4, similar to LCH)
- HSI (float4)
- HSL (float4)
- HSV (float4)
- Lab (float4, D50/D65 versions)
- LCH (float4)
- Oklab (float4)
- RGB (float4, linear)
- sRGB (float4, gamma corrected)
- XYY (float4)
- XYZ (float4, aka CIE 1931, D50/D65 versions)
- YCC (float4, aka YCbCr)
From/To | CSS | HCY | HSI | HSL | HSV | Int | Lab | LCH | Oklab | RGB | sRGB | XYY | XYZ | YCC |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
CSS | ✅ | 🆎 | 🆎 | ✅ | 🆎 | ✅(1) | ✅(4) | ✅ | 🆎 | ✅ | ✅ | 🆎 | 🆎 | 🆎 |
HCY | 🆎 | ✅ | 🆎 | 🆎 | 🆎 | ❌ | 🆎 | 🆎 | 🆎 | ✅(2) | ✅(2) | 🆎 | 🆎 | 🆎 |
HSI | 🆎 | 🆎 | ✅ | 🆎 | 🆎 | ❌ | 🆎 | 🆎 | 🆎 | ✅(2) | ✅(2) | 🆎 | 🆎 | 🆎 |
HSL | ✅ | 🆎 | 🆎 | ✅ | 🆎 | ❌ | 🆎 | 🆎 | 🆎 | ✅(2) | ✅(2) | 🆎 | 🆎 | 🆎 |
HSV | 🆎 | 🆎 | 🆎 | ✅ | ✅ | ❌ | 🆎 | 🆎 | 🆎 | ✅(2) | ✅(2) | 🆎 | 🆎 | 🆎 |
Int | ✅ | 🆎 | 🆎 | 🆎 | 🆎 | ❌ | 🆎 | 🆎 | 🆎 | 🆎 | ✅ | ✅ | 🆎 | 🆎 |
Lab | ✅ | 🆎 | 🆎 | 🆎 | 🆎 | ❌ | ✅(3) | ✅ | 🆎 | ✅(3) | 🆎 | 🆎 | ✅(3) | 🆎 |
LCH | ✅ | 🆎 | 🆎 | 🆎 | 🆎 | ❌ | ✅ | ✅ | 🆎 | 🆎 | 🆎 | 🆎 | 🆎 | 🆎 |
Oklab | 🆎 | 🆎 | 🆎 | 🆎 | 🆎 | ❌ | 🆎 | 🆎 | ✅ | ✅ | 🆎 | 🆎 | ✅ | 🆎 |
RGB | 🆎 | ✅(2) | ✅(2) | ✅(2) | ✅(2) | ✅ | ✅(3) | ✅ | ✅ | ✅ | ✅ | 🆎 | ✅(3) | ✅(2) |
sRGB | ✅ | ✅(2) | ✅(2) | ✅(2) | ✅(2) | ✅ | 🆎 | 🆎 | 🆎 | ✅ | ✅ | 🆎 | 🆎 | 🆎 |
XYY | 🆎 | 🆎 | 🆎 | 🆎 | 🆎 | ❌ | 🆎 | 🆎 | 🆎 | 🆎 | 🆎 | ✅ | ✅ | 🆎 |
XYZ | 🆎 | 🆎 | 🆎 | 🆎 | 🆎 | ❌ | ✅ | 🆎 | 🆎 | ✅ | 🆎 | ✅ | ✅(3) | 🆎 |
YCC | 🆎 | 🆎 | 🆎 | 🆎 | 🆎 | ❌ | 🆎 | 🆎 | 🆎 | ✅(2) | 🆎 | 🆎 | 🆎 | ✅ |
- ✅ - direct conversion
- 🆎 - indirect conversion (mostly via RGB/sRGB)
- (1) - only via
parseHex()
- (2) - no consideration for linear/gamma encoded RGB/sRGB (see Wikipedia)
- (3) - including D50/D65 illuminant options
- (4) - parsed as Lab w/ D50 illuminant as per CSS Color Module Level 4
Each color space provides a factory function to create & convert color instances. These functions can take the following arguments:
- CSS string
- number (interpreted as packed ARGB int32)
- array (interpreted as linear RGB)
- scalars (one per channel)
- color instance (triggers conversion)
Additionally, an optional target backing buffer, start index and stride can be given. See next section.
// convert RGB CSS into HSL CSS
// (internally: string -> int32 -> srgb -> hsl -> string)
css(hsl("#4ff0"))
// 'hsla(60.000,100.000%,50.000%,0.267)'
All color types store their channel values in plain arrays, typed arrays of
(mostly) normalized values ([0,1]
interval). Where applicable, the hue too is
stored in that range (similar to CSS
turn
units), NOT in degrees.
Likewise, luminance is always stored in the [0,1]
too, even for Lab, LCH where
often the [0,100]
range is used instead.
As a fairly unique feature, all color types can be used to provided views of a backing memory buffer (e.g. for WASM/WebGL/WebGPU interop, pixel buffers etc.), incl. support for arbitrary component strides.
The lightweight class wrappers act similarly to the Vec2/3/4
wrappers in
@thi.ng/vectors,
support striding (for mapped memory views), named channel accessor
aliases (in addition to array indexing) and are fully compatible with
all vector functions.
The package provides several methods for procedural & declarative color theme generations. The latter relies on the concept of HSV color ranges, which can be sampled directly and/or mixed with a base color to produce randomized variations. Furthermore, multiple such ranges can be combined into a weighted set to define probabilistic color themes.
// single random color drawn from the "bright" color range preset
colorFromRange(RANGES.bright);
// [ 0.7302125322518669, 0.8519945301256682, 0.8134374983367859, 1 ]
// single random color based on given HSV base color and preset
colorFromRange(RANGES.warm, [0.33, 1, 1])
// [ 0.3065587375218628, 0.8651353734302525, 0.748781892650323, 1 ]
// infinite iterator of colors sampled from the preset
// (see table below)
const colors = colorsFromRange(RANGES.bright);
colors.next();
// {
// value: [ 0.006959075656347791, 0.8760165887192115, 0.912149937028727, 1 ],
// done: false
// }
// 10 cool reds, w/ 10% hue variance
[...colorsFromRange(RANGES.cool, [0, 0.8, 1], { num: 10, variance: 0.1 })]
// generate colors based on given (weighted) textual description(s)
// here using named CSS colors, but could also be HSV tuples
[...colorsFromTheme(
[["warm", "goldenrod"], ["cool", "springgreen", 0.1]],
{ num: 100, variance: 0.05 }
)]
// theme parts can also be given in the format used internally
// note: base colors are always in HSV
// all keys are optional (range, base, weight),
// but at least `range` or `base` must be given...
[...colorsFromTheme(
[
{ range: "warm", base: "goldenrod" },
{ range: RANGES.cool, base: [0, 1, 0.5], weight: 0.1 }
],
{ num: 100, variance: 0.05 }
)]
ID | 100 colors drawn from color range preset only, sorted by hue |
---|---|
bright |
|
cool |
|
dark |
|
fresh |
|
hard |
|
intense |
|
light |
|
neutral |
|
soft |
|
warm |
|
weak |
ID | 100 colors, single base color w/ color range preset, sorted by hue |
---|---|
bright |
|
cool |
|
dark |
|
fresh |
|
hard |
|
intense |
|
light |
|
neutral |
|
soft |
|
warm |
|
weak |
ID | 100 colors, 2 base colors w/ color range preset, sorted by brightness |
---|---|
bright |
|
cool |
|
dark |
|
fresh |
|
hard |
|
intense |
|
light |
|
neutral |
|
soft |
|
warm |
|
weak |
Full example:
import { colorsFromTheme, hsva, swatchesH } from "@thi.ng/color";
import { serialize } from "@thi.ng/hiccup";
import { svg } from "@thi.ng/hiccup-svg";
import { writeFileSync } from "fs";
// color theme definition using:
// color range preset names, CSS colors and weights
const theme: ColorThemePartTuple[] = [
["cool", "goldenrod"],
["fresh", "hotpink", 0.1],
["light", "springgreen", 0.1],
];
// generate 200 HSV colors based on above description
const colors = [...colorsFromTheme(theme, { num: 200, variance: 0.05 })];
// create SVG doc of color swatches (hiccup format)
// (convert colors to RGB for smaller file size)
const doc = svg(
{ width: 1000, height: 50, convert: true },
swatchesH(colors.map((x) => hsvaRgba([], x)), 5, 50)
);
// serialize to SVG file
writeFileSync("export/swatches-ex01.svg", serialize(doc));
The sortColors()
function can be used to sort an array of colors using
arbitrary sort criteria. The following comparators are bundled:
selectChannel(i)
- sort by channelproximityHSV(target)
- sort by distance to target color (HSV colors)proximityRGB(target)
- sort by distance to target color (RGB colors)
// (see above example)
const colors = [...colorsFromTheme(theme, { num: 200, variance: 0.05 })];
sortColors(colors, proximityHSV([0,1,0.5]));
RGB color matrix transformations, including parametric preset transforms:
- brightness
- contrast
- exposure
- saturation (luminance aware)
- hue rotation
- color temperature (warm / cold)
- sepia (w/ fade amount)
- tint (green / magenta)
- grayscale (luminance aware)
- subtraction/inversion (also available as non-matrix op)
- luminance to alpha
Transformation matrices can be combined using matrix multiplication /
concatenation (see concat()
) for more efficient application.
This feature has been moved to the separate @thi.ng/porter-duff package.
The following presets are bundled (in cosine-gradients.ts
):
The cosineCoeffs()
function can be used to compute the cosine gradient
coefficients between 2 start/end colors:
// compute gradient coeffs between red / green
cosineGradient(10, cosineCoeffs([1,0,0,1], [0,1,0,1])).map(rgbaCss)
// #ff0000
// #f70800
// #e11e00
// #bf4000
// #966900
// #699600
// #40bf00
// #1ee100
// #08f700
// #00ff00
The multiCosineGradient()
function returns an iterator of raw RGB
colors based on given gradient stops. This iterator computes a cosine
gradient between each color stop and yields a sequence of RGB values.
col.multiCosineGradient(
// num colors to produce
10,
// gradient stops (normalized positions, only RGB colors supported)
[0.1, col.RED], [0.5, col.GREEN], [0.9, col.BLUE]
)
// convert to CSS
.map(col.rgbaCss)
// [
// "#ff0000",
// "#ff0000",
// "#da2500",
// "#807f00",
// "#25da00",
// "#00ff00",
// "#00da25",
// "#00807f",
// "#0025da",
// "#0000ff",
// "#0000ff",
// ]
STABLE - used in production
Search or submit any issues for this package
- @thi.ng/pixel - Typed array backed, integer and floating point pixel buffers w/ customizable formats, blitting, dithering, conversions
yarn add @thi.ng/color
// ES module
<script type="module" src="https://unpkg.com/@thi.ng/color?module" crossorigin></script>
// UMD
<script src="https://unpkg.com/@thi.ng/color/lib/index.umd.js" crossorigin></script>
Package sizes (gzipped, pre-treeshake): ESM: 12.09 KB / CJS: 12.73 KB / UMD: 11.89 KB
- @thi.ng/api
- @thi.ng/arrays
- @thi.ng/binary
- @thi.ng/checks
- @thi.ng/compare
- @thi.ng/compose
- @thi.ng/defmulti
- @thi.ng/errors
- @thi.ng/math
- @thi.ng/random
- @thi.ng/strings
- @thi.ng/transducers
- @thi.ng/vectors
Several demos in this repo's /examples directory are using this package.
A selection:
Screenshot | Description | Live demo | Source |
---|---|---|---|
Heatmap visualization of this mono-repo's commits | Source | ||
Visualization of different grid iterator strategies | Demo | Source | |
Interactive pixel sorting tool using thi.ng/color & thi.ng/pixel | Demo | Source | |
Fork-join worker-based raymarch renderer | Demo | Source |
import * as col from "@thi.ng/color";
// route #1: asXXX() converters: string -> CSS -> ARGB (int) -> RGBA
const a = col.asRGBA(col.css("#3cf"));
// [0.2, 0.8, 1, 1]
// route #2: parseCSS(): string -> RGBA
const b = col.parseCss("hsla(30,100%,50%,0.75)");
// [ 1, 0.5, 0, 0.75 ]
// route #3: convert() multi-method: CSS -> RGBA -> HSVA
// (see convert.ts)
const c = col.convert("rgb(0,255,255)", "hsv", "css");
// [ 0.4999999722222268, 0.9999990000010001, 1, 1 ]
// route #4: direct conversion RGBA -> HSLA -> CSS
// first arg is output color (same calling convention as @thi.ng/vectors)
// (use `null` to mutate the input color)
col.hslaCss(col.rgbaHsla([], [1, 0.5, 0.5, 1]))
// "hsl(0.00,100.00%,75.00%)"
col.luminance(col.css("white"))
col.luminance(0xffffff, "int")
// 1
// apply color matrix (RGBA only)
col.transform([], col.saturation(1.25), a)
// [ 0.07835000000000002, 0.82835, 1, 1 ]
// combine matrix transformations
filter = col.concat(
col.saturation(0.5), // 50% saturation
col.brightness(0.1), // +10% brightness
);
col.transform([], filter, col.RED);
// [ 0.7065, 0.2065, 0.2065, 1 ]
Karsten Schmidt
If this project contributes to an academic publication, please cite it as:
@misc{thing-color,
title = "@thi.ng/color",
author = "Karsten Schmidt",
note = "https://thi.ng/color",
year = 2016
}
© 2016 - 2021 Karsten Schmidt // Apache Software License 2.0