Skip to content

Latest commit

 

History

History

color

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

color

npm version npm downloads Twitter Follow

This project is part of the @thi.ng/umbrella monorepo.

For the Clojure version, please visit: thi.ng/color-clj

About

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


Supported color spaces / modes

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

Color creation / conversion

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)'

Storage & memory mapping

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.

Color theme generation

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 color swatch
cool color swatch
dark color swatch
fresh color swatch
hard color swatch
intense color swatch
light color swatch
neutral color swatch
soft color swatch
warm color swatch
weak color swatch
ID 100 colors, single base color w/ color range preset, sorted by hue
bright color swatch
cool color swatch
dark color swatch
fresh color swatch
hard color swatch
intense color swatch
light color swatch
neutral color swatch
soft color swatch
warm color swatch
weak color swatch
ID 100 colors, 2 base colors w/ color range preset, sorted by brightness
bright color swatch
cool color swatch
dark color swatch
fresh color swatch
hard color swatch
intense color swatch
light color swatch
neutral color swatch
soft color swatch
warm color swatch
weak color swatch

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));

example result color swatches

Color sorting

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 channel
  • proximityHSV(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]));

sorted color swatches

RGB color transformations

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.

RGBA Porter-Duff compositing

This feature has been moved to the separate @thi.ng/porter-duff package.

Cosine gradients

The following presets are bundled (in cosine-gradients.ts):

Preview Gradient ID
gradient: blue-cyan blue-cyan
gradient: blue-magenta-orange blue-magenta-orange
gradient: blue-white-red blue-white-red
gradient: cyan-magenta cyan-magenta
gradient: green-blue-orange green-blue-orange
gradient: green-cyan green-cyan
gradient: green-magenta green-magenta
gradient: green-red green-red
gradient: heat1 heat1
gradient: magenta-green magenta-green
gradient: orange-blue orange-blue
gradient: orange-magenta-blue orange-magenta-blue
gradient: purple-orange-cyan purple-orange-cyan
gradient: rainbow1 rainbow1
gradient: rainbow2 rainbow2
gradient: rainbow3 rainbow3
gradient: rainbow4 rainbow4
gradient: red-blue red-blue
gradient: yellow-green-blue yellow-green-blue
gradient: yellow-magenta-cyan yellow-magenta-cyan
gradient: yellow-purple-magenta yellow-purple-magenta
gradient: yellow-red yellow-red

Two-color gradients

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

Multi-stop gradients

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",
// ]

Status

STABLE - used in production

Search or submit any issues for this package

Related packages

  • @thi.ng/pixel - Typed array backed, integer and floating point pixel buffers w/ customizable formats, blitting, dithering, conversions

Installation

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

Dependencies

Usage examples

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

API

Generated API docs

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 ]

Authors

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
}

License

© 2016 - 2021 Karsten Schmidt // Apache Software License 2.0