Package Exports
- culori
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (culori) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Culori
A color library for JavaScript. Culori works across many color spaces to offer conversion, interpolation, color difference formulas, and blending functions.
It supports most color spaces and formats defined in the CSS Colors Level 4 spec:
- Named colors
- Hex colors with 3, 4, 6, or 8 digits
- RGB
- HSL
- HWB
- Lab and LCh
- Grays
Additional color spaces:
Culori can compute color differences based on a variety of formulas:
- simple Euclidean distance
- CIELAB Delta E* metric as formulated by CIE76, CMC l:c (1984), CIE94, and CIEDE2000
- Kotsarenko/Ramos distance
Use any of them to pick from a palette the perceptually-closest colors to a given color.
Culori is inspired by Mike Bostock's D3.js and Gregor Aisch's chroma.js, from which it takes ideas and academic references.
Foreword
If you're thinking Do we really need another JavaScript color library?, I hear you. Reader, for the most part, we don't. Mike Bostock's d3-color, and Gregor Aisch's chroma.js are two excellent, robust libraries that provide most of what you need for working with colors on the web. See the Other Projects section for even more libraries you can try.
Culori — from the Romanian word for ‘colors’ — started with a curiosity in getting color spaces at a deeper level, maths and all; by the time I came out of this rabbit-hole, I had what I thought is a fairly fast, fairly comprehensive, set of tools for working with color, with an implementation that has certain advantages to boot.
The API tries to balance brevity, convenience and flexibility. These goals led to a functional-style API, where you transform plain objects representing colors through a series of functions. It's arguably not as readable as a fluent API but the lack of classes makes it more flexible and extensible (more on that later).
The alpha channel, which holds a color's opacity, is treated differently than in other libaries. We don't equate an undefined alpha with an alpha of 1. The hex string #ff0000 should eventually be interpreted as a fully-opaque red color, but at the color-manipulation level we want to maintain the distinction between #ff0000 (no explicit alpha) and #ff0000ff (explicit alpha of 1).
Getting Started
Try it online
You can use npm.runkit.com/culori as a playground to test various API methods without installing culori in your project. Observable is another great place to tinker with the library and see the results visually.
Install it as an npm package
culori is bundled as UMD and ES on npm. Install it using npm
or yarn
:
# using npm
npm install culori
# using yarn
yarn add culori
You can then import culori in your project:
// CJS style: import the whole library
let culori = require('culori');
// ES style: import individual methods
import { rgb } from 'culori';
Add it via the <script>
tag
To import culori as a <script>
tag to use in a web page, you can load it from unpkg:
<script src='https://unpkg.com/culori'></script>
API Reference
- Color representation
- Basics methods
- Interpolation
- Difference
- Blending
- Random colors
- Extending culori
Color representation
Culori does not have a Color class. Instead, it uses plain objects to represent colors:
// A color in the RGB space
{
mode: 'rgb',
r: 0.1,
g: 0.2,
b: 1,
alpha: 1
}
The object needs to have a mode
property that identifies the color space, and values for each channel in that particular color space. See the Color Spaces section for the channels expected of each color space. Optionally, the alpha
property is used for the color's alpha channel.
Basic methods
# culori.parse(string) → color or undefined <>
Parses a string and returns the corresponding color. The color will be in the matching color space, e.g. RGB for hex strings, HSL for hsl(…, …, …)
strings, et cetera. If no built-in parsers can match the string, the function will return undefined.
// named colors:
culori.parse('red');
// => { r: 1, g: 0, b: 0, mode: 'rgb' }
// hex strings:
culori.parse('#ff0000');
// => { r: 1, g: 0, b: 0, mode: 'rgb' }
// HSL colors:
culori.parse('hsl(60 50% 10% / 100%)');
// => { h: 60, s: 0.5, b: 0.1, alpha: 1, mode: 'hsl' }
// Lab colors:
culori.parse('lab(100 -50 50)');
// => { l: 100, a: -50, b: 50, mode: 'lab' }
# culori.converter(mode = "rgb") → function (color or String) <>
Returns a converter: a function that can convert any color to the mode color space:
let rgb = culori.converter('rgb');
let lab = culori.converter('lab');
rgb('#f0f0f0');
// => { mode: "rgb", r: 0.4980392156862745, g: 0.4980392156862745, b: 0.4980392156862745 }
lab('#f0f0f0');
// => { mode: "lab", l: 94.79624582959184, a: 0 , b: 0 }"
Converters accept either strings (which will be parsed with culori.parse
under the hood) or color objects. If the mode
key is absent from the color, it's assumed to be in the converter's color space.
The available modes (color spaces) are listed below. Each color space included by default in culori has a shortcut for its converter, i.e. you can use culori.hsl
instead of culori.converter('hsl')
.
Mode | For | Shortcut |
---|---|---|
cubehelix |
Cubehelix color space | culori.cubehelix(color) |
dlab |
DIN99o Lab color space | culori.dlab(color) |
dlch |
DIN99o LCh color space | culori.dlch(color) |
hsi |
HSI color space | culori.hsi(color) |
hsl |
HSL color space | culori.hsl(color) |
hsv |
HSV color space | culori.hsv(color) |
hwb |
HWB color space | culori.hwb(color) |
lab |
Lab color space | culori.lab(color) |
lch |
LCh color space | culori.lch(color) |
lrgb |
Linearized RGB color space | culori.lrgb(color) |
rgb |
RGB color space | culori.rgb(color) |
yiq |
YIQ color space | culori.yiq(color) |
# culori.formatter(format = 'rgb') → function (color) <>
Returns a formatter: a function that can transform colors to a useful string representation.
let hex = culori.formatter('hex');
hex('red'); // ⇒ "#ff0000"
Available formats:
Format | Description |
---|---|
hex |
Returns the hex string for a color |
rgb |
Returns the rgb(…) / rgba(…) string for a color |
Reference: CSSOM standard serialization
# culori.displayable(color or String) <>
Some color spaces (Lab and LCh in particular) allow you to express colors that can't be displayed on-screen. This function checks whether a particular color fits inside the sRGB gamut — i.e. its r
, g
, and b
channels are all in the interval [0, 1]
.
culori.displayable('red'); // ⇒ true
culori.displayable('rgb(300 255 255)'); // ⇒ false
# culori.clamp(method = 'rgb') → function (color) <>
Returns a function which you use to retreive a representation that's displayable on the screen for any color. There are two available methods to squeeze the color into the displayable sRGB gamut:
method = 'rgb'
clamps the r
, g
, b
channel values of the color's RGB representation to the interval [0, 1]
.
method = 'lch'
converts the color to LCh and finds the largest Chroma value that's displayable for the given Lightness and Hue; if not even the achromatic version (Chroma = 0) of the LCh color is displayable, it falls back to the rgb
method.
// RGB clamping
culori.clamp('rgb')('lab(50 100 100)');
// => { mode: "lab", l: 54.29173376861782, a: 80.8124553179771, b: 69.88504032350531 }
// LCh clamping
culori.clamp('lch')('lab(50 100 100)');
// => { mode: "lab", l:50.0000028101302, a: 63.11644430269186, b: 63.11642289997279 }
The clamped color will always be returned in the original color space.
Returns a rounder: a function with which to round numbers to at most n digits of precision.
let approx = culori.round(4);
approx(0.38393993);
// => 0.3839
Interpolation
# culori.interpolate(colors, mode = "rgb", interpolations) <>
Returns an interpolator in the mode color space for an array of colors: a function that accepts a value t in the interval [0, 1]
and returns the interpolated color in the mode color space.
The colors in the array can be in any color space, or they can even be strings.
let grays = culori.interpolate(['#fff', '#000']);
grays(0.5); // => { mode: 'rgb', r: 0.5, g: 0.5, b: 0.5 }
By default, colors in all spaces are interpolated linearly. You can override the way specific channels are interpolated with the interpolations object, the third argument of culori.interpolate()
.
let custom_interpolator = culori.interpolate(['blue', 'red'], 'lch', {
h: culori.interpolateLinear() // long-path hue interpolation
});
There are a few interpolation methods available, listed below. Depending on the channel, the numeric values can be interpreted/interpolated in various modes. The hue channel, for example, is interpolated by taking into account the shortest path around the hue circle (interpolateHue
). And the interpolateAlpha
mode assumes an undefined alpha is 1
.
Color stop positions
You can specify positions of color stops to interpolate in the way they're defined in the CSS Images Module Level 4 specification:
culori.interpolate(['red', ['green', 0.25], 'blue']);
In the image below, you can see the effect of interpolating with evenly-spaced colors (1) vs. positioned colors stops (2):

To specify a positioned color stop, use an array that contains the color followed by its position. The color stops should be specified in ascending order.
For omitted (implicit) positions, we apply the rules from the spec:
- if the first color doesn't have a position, it's assumed to be
0
; if the last color doesn't have a position, it's assumed to be1
; - any other color stops that don't have a position will be evenly distributed along the gradient line between the positioned color stops.
Easing functions
You can add easing functions between any two colors in the array:
const easeIn = t => t * t;
culori.interpolate(['red', easeIn, 'green']);
Any function in the colors array will be interpreted as an easing function, which is (for our purposes), a function that takes an argument t ∈ [0, 1]
and returns a value v ∈ [0, 1]
.
Culori comes with just a few easing functions, but you can find several online:
- some classic easing functions;
- eases by Matt DesLauriers;
- bezier-easing by Gaëtan Renaudeau builds
cubic-bezier
easings as defined in the CSS Easing Functions Level 1 spec; - d3-scale lets you set the scale's domain and range to
[0, 1]
.
Interpolation hints
Any number in he colors array will be interpreted as an interpolation hint:
// interpolation hint
culori.interpolate(['red', 0.25, 'green']);
👉 As opposed to the CSS spec, interpolation hints don't affect color stop positions in culori.
Built-in easing functions
A few easing functions that come with culori:
# culori.easingMidpoint(H = 0.5) <>
Proposed here, the midpoint
easing function lets you shift the midpoint of a gradient like in tools such as Adobe Photoshop. You can use it with culori.interpolate
as an alternative to interpolation hints:
culori.interpolate(['red', easingMidpoint(0.25), 'blue']);
// equivalent to
culori.interpolate(['red', 0.25, 'blue']);
The Smoothstep easing function.
Smootherstep is a variant of the Smoothstep easing function.
Interpolation methods
You'll use these methods when you want to override how colors get interpolated in a specific color space, or when defining the default interpolation for custom color spaces.
# culori.interpolateLinear(normalize = identity, γ = 1) <>
A linear interpolator for values in a channel. By default does not normalize the values.
# culori.interpolateSplineBasis(normalize = identity, type = "default", γ = 1) <>
A basis spline interpolator for values in a channel. The type can be one of the following:
default
creates a basis spline that passes through the first and last values in the array.closed
creates a closed basis splineopen
creates an open basis spline (not yet implemented)
# culori.interpolateSplineNatural(normalize = identity, type = "default", γ = 1) <>
A natural spline interpolator for values in a channel. The type can be one of the following:
default
creates a natural splineclosed
creates a closed natural spline
# culori.interpolateSplineMonotone(normalize = identity, type = "default", γ = 1) <>
A monotone spline interpolator for values in a channel. The type can be one of the following:
default
creates a monotone splineclosed
creates a closed monotone splineopen
creates an open monotone spline (not yet implemented)
# culori.interpolateCosine(normalize = identity, γ = 1) <>
Interpolates the value using the cosine function, which can offer a smoother alternative to linear interpolation.
Interpolation modes
By default, channel values that need to be interpolated are not normalized in any way. However, for some channels, we need to do some normalization before we interpolate the values:
Used for the hue channel in various color spaces, normalizes the array such that the interpolator takes into account the shortest path around the hue circle:
hsl/definition.js
export default {
// ...
interpolate: {
h: interpolateLinear(interpolateHue),
s: interpolateLinear(),
l: interpolateLinear(),
alpha: interpolateLinear(interpolateAlpha)
}
// ...
};
By default, hue channels in all color spaces will use this normalization. However, the example below interpolates colors in the HSL color space by treating hues as normal numeric values instead:
let hsl_long = culori.interpolate(['blue', 'red', 'green'], 'hsl', {
h: culori.interpolateLinear()
});
(Notice we're not sending culori.interpolateHue
to culori.interpolateLinear()
as the first argument.)
Used for the alpha channel in various color spaces such that undefined
values are treated as 1
(full opacity) in some situations. This is the default for the alpha channel in all color spaces.
Evenly-spaced samples
# culori.samples(n = 2, γ = 1) <>
Returns an array of n equally-spaced samples from the [0, 1]
range, with 0
and 1
at the ends. The function also accepts a γ (gamma) parameter which will map each value t to tγ.
culori.samples(3); // => [0, 0.5, 1]
culori.samples(5); // => [0, 0.25, 0.5, 0.75, 1]
The samples are useful for culori.interpolate() to generate color scales:
let grays = culori.interpolate(['#fff', '#000']);
culori.samples(5).map(grays); // => five evenly-spaced colors
As with the interpolate()
method, you can map the samples through an easing function or scale to obtain a different distribution of the samples.
let culori = require('culori');
let easing = require('bezier-easing');
// Bezier easing
let bezier = easing(0, 0, 1, 0.5);
culori.samples(10).map(bezier);
// easeInQuad
culori.samples(10).map(t => t * t);
Difference
These methods are concerned to finding the distance between two colors based on various formulas. Each of these formulas will return a function (colorA, colorB) that lets you measure the distance between two colors.
These are also available as a separate D3 plugin.
# culori.differenceEuclidean(mode = 'rgb', weights = [1, 1, 1, 0]) <>
Returns a Euclidean distance function in a certain color space.
You can optionally assign different weights to the channels in the color space. See, for example, the Kotsarenko/Ramos distance.
The default weights [1, 1, 1, 0]
mean that the alpha, which is the fourth channel in all the color spaces culori defines, is not taken into account. Send [1, 1, 1, 1]
as the weights to include it in the computation.
For the h
channel in the color (in any color space that has this channel), we're using a shortest hue distance to compute the hue's contribution to the distance. In spaces such as HSL or HSV, where the range of this difference is [0, 180]
— as opposed to [0, 1]
for the other channels — consider adjusting the weights so that the hue contributes "equally" to the distance:
let hsl_distance = culori.differenceEuclidean('hsl', [
1 / (180 * 180),
1,
1,
0
]);
Computes the CIE76 ΔE*ab color difference between the colors a and b. The computation is done in the Lab color space and it is analogous to culori.differenceEuclidean('lab').
# culori.differenceCie94(kL = 1, K1 = 0.045, K2 = 0.015) <>
Computes the CIE94 ΔE*94 color difference between the colors a and b. The computation is done in the Lab color space.
# culori.differenceCiede2000(Kl = 1, Kc = 1, Kh = 1) <>
Computes the CIEDE2000 ΔE*00 color difference between the colors a and b as implemented by G. Sharma. The computation is done in the Lab color space.
Returns a CIEDE2000 Delta E* function.
Computes the CMC l:c (1984) ΔE*CMC color difference between the colors a and b. The computation is done in the Lab color space.
Note: ΔE*CMC is not considered a metric since it's not symmetrical, i.e. the distance from a to b is not always equal to the distance from b to a. Therefore it cannot be reliably used with culori.nearest().
# culori.differenceDin99o() <>
Computes the DIN99o ΔE*99o color difference between the colors a and b. The computation is done in the DIN99o color space.
# culori.differenceKotsarenkoRamos() <>
Computes the Kotsarenko/Ramos color difference between the colors a and b. This is a weighted Euclidean distance in the YIQ color space.
Nearest color(s)
# culori.nearest(colors, metric = differenceEuclidean(), accessor = identity) → function(color, n = 1, τ = Infinity) <>
For a given metric color difference formula, and an array of colors, returns a function with which you can find n colors nearest to color, with a maximum distance of τ.
Pass n = Infinity to get all colors in the array with a maximum distance of τ.
Blending
Color blending works as defined in the W3C Compositing and Blending Level 2 specification.
# culori.blend(colors, type = 'normal', mode = 'rgb') → color <>
Available blending modes:
normal
multiply
screen
hard-light
overlay
darken
lighten
color-dodge
color-burn
soft-light
difference
exclusion
Note: culori currently implements the separable blend modes, that is the blend modes that work on each channel in the color space independently. color, hue, saturation, and lightness modes are not yet available.
An example of blending three colors:
culori.blend(
['rgba(255, 0, 0, 0.5)', 'rgba(0, 255, 0, 0.5)', 'rgba(0, 0, 255, 0.5)'],
'screen'
);
// => { mode: 'rgb', alpha: 0.875, r: 0.57..., g: 0.57..., b:0.57... }
In addition to strings, the type parameter supports a function (b, s) → v that takes the values of the backdrop and source color to return the blended value. This allows you to write your own (separable) blending functions. For example, an average blending mode:
culori.blend(['red', 'green'], function average(b, s) {
return (b + s) / 2;
});
Random colors
# culori.random(mode = 'rgb', constraints = {}) <>
Obtain a random color from a particular color space, with optional constraints. The resulting color will be in the color space from where it has been picked.
Basic usage:
culori.random();
// => { mode: 'rgb', r: 0.75, g: 0.12, b: 0.99 }
Specifying constraints
Random colors are, by definition, all over the color space and not all of them will look particularly nice. Some color spaces, such as HSL or HSV, are also biased towards colors close to black and/or white, because of the way these color spaces stretch the RGB cube into cyllinders.
For more control on how the colors are generated, you can specify constraints for each individual channel in the color space. Constraints can be either a constant number or an interval from where to pick the channel value:
culori.random('hsv', {
h: 120 // number,
s: [0.25, 0.75] // interval
});
The alpha channel is excluded by default. To obtain colors with random alpha values, include a constraint for alpha
:
culori.random('lrgb', { alpha: [0, 1] });
Displayable random colors
The value for any channel in the color space for which there are no constraints will be picked from the entire range of that channel. However, some color spaces, such as LAB or LCH, don't have explicit ranges for certain channels; for these, some approximate ranges have been pre-computed as the limits of the displayable sRGB gamut.
Even with these ranges in place, a combination of channel values may not be displayable. Check if that's the case with culori.displayable()
, and pass the color through culori.clamp()
to obtain a displayable version.
Extending culori
# culori.defineMode(definition) <>
Defines a new color space through a definition object. By way of example, here's the definition of the HSL color space:
{
mode: 'hsl',
output: {
rgb: convertHslToRgb
},
input: {
rgb: convertRgbToHsl
},
channels: ['h', 's', 'l', 'alpha'],
ranges: {
h: [0, 360]
},
parsers: [parseHsl],
interpolate: {
h: interpolateLinear(interpolateHue),
s: interpolateLinear(),
l: interpolateLinear(),
alpha: interpolateLinear(interpolateAlpha)
}
}
The properties a definition needs are the following:
mode
: the string identifier for the color spaceoutput
: a set of functions to convert from the color space we're defining to other color spaces. At leastrgb
needs to be included; in case a specific conversion pair between two color spaces is missing, RGB is used as the "buffer" for the conversion.input
: opposite ofoutput
; a set of function to convert from various color spaces to the color space we're defining. At leastrgb
needs to be included.channels
: a list of channels for the color space.ranges
: the ranges for values in specific channels; if left unspecified, defaults to[0, 1]
.parsers
: any parsers for the color space that can transform strings into colorsinterpolate
: the default interpolations for the color space.
Note: The order of the items in the channels
array matters. To keep things simple, we're making the following conventions:
- the fourth item in the array should be
alpha
- any cyclical values (e.g. hue) should be identified by
h
, in the range[0, 360)
These constrains make sure differenceEuclidean()
works as expected.
Color Spaces
RGB
🕳 expand this section
LRGB (Linear RGB)
🕳 expand this section
📖 See Jamie Wong's excellent deep dive into color.
HSL / HSV / HSI
HSL, HSV, and HSI are a family of representations of the RGB color space, created in 1970 to provide color spaces more closely aligned to how humans perceive colors.
💡 In this family of color spaces, the hue is undefined for achromatic colors (i.e. shades of gray).
hsl
Channel | Range | Description |
---|---|---|
h |
[0, 360) |
Hue |
s |
[0, 1] |
Saturation in HSL |
l |
[0, 1] |
Lightness |
The figure below shows a slice of the HSL color space for a particular hue:

hsv
Channel | Range | Description |
---|---|---|
h |
[0, 360) |
Hue |
s |
[0, 1] |
Saturation in HSV |
v |
[0, 1] |
Value |
The figure below shows a slice of the HSV color space for a particular hue:

hsi
Channel | Range | Description |
---|---|---|
h |
[0, 360) |
Hue |
s |
[0, 1] |
Saturation in HSI |
i |
[0, 1] |
Intensity |
The figure below shows a slice of the HSI color space for a particular hue:

💡 While the hue in this family of color spaces retains its value in all of them, the saturation in HSL is not interchangeable with the saturation from HSV, nor HSI — they're computed differently, depending on the color space.
HWB
The HWB color space was developed by Alvy Ray Smith, who also created the HSV color space. It's meant to be more intuitive for humans to use and faster to compute.
References:
- Smith, Alvy Ray (1996) — "HWB — A More Intuitive Hue-Based Color Model", Journal of Graphics, GPU and Game tools.
Lab / LCh (CIE)
As defined in the CSS Color Module Level 4 spec, we use the D50 illuminant as a reference white for these color spaces.
lab
Channel | Range | Description |
---|---|---|
l |
[0, 100] |
Lightness |
a |
[-79.167, 93.408] |
Green–red component |
b |
[-111.859, 93.246] |
Blue–yellow component |
lch
Channel | Range | Description |
---|---|---|
l |
[0, 100] |
Lightness |
c |
[0, 131.008] |
Chroma |
h |
[0, 360) |
Hue |
💡 The range for the
a
andb
channels in Lab, and thec
channel in LCh, depend on the specific implementation. I've obtained the ranges from the tables above by converting all sRGB colors defined byr, g, b ∈ ℕ ⋂ [0, 255]
into Lab and LCh respectively.
DIN99 Lab / LCh
The DIN99 color space "squishes" the CIE Lab color space to obtain an effective color difference metric that can be expressed as a simple Euclidean distance. We implement the latest iteration of the the standard, DIN99o.
dlab
Channel | Range | Description |
---|---|---|
l |
[0, 100] |
Lightness |
a |
[-39.229, 45.166] |
|
b |
[-43.002, 44.424] |
dlch
Channel | Range | Description |
---|---|---|
l |
[0, 100] |
Lightness |
c |
[0, 50.944] |
Chroma |
h |
[0, 360) |
Hue |
References:
- "Industrial Color Physics", Georg A. Klein, Springer (2010)
YIQ
YIQ is the color space used by the NTSC color TV system. It contains the following channels:
Channel | Range | Description |
---|---|---|
Y | [0, 1] |
Luma |
I | [-0.593, 0.593] |
In-phase (orange-blue axis) |
Q | [-0.520, 0.520] |
Quadrature (green-purple axis) |
Cubehelix
The Cubehelix color scheme was described by Dave Green in this paper:
- Green, D. A., 2011, "A colour scheme for the display of astronomical intensity images", Bulletin of the Astronomical Society of India, 39, 289. (2011BASI...39..289G at ADS)
It was expanded into a color space by Mike Bostock and Jason Davies in D3.
cubehelix
The channels in the cubehelix
color space maintain the conventions from D3, namely:
Channel | Range | Description |
---|---|---|
h |
[0, 360) |
Hue (Based on start color and rotations as defined in Green's paper) |
s |
[0, 4.6143] |
Saturation (Called hue in op. cit.) |
l |
[0, 1] |
Lightness |
Culori Recipes
A collection of useful functions that are not currently part of culori.
Relative luminance of a color
The relative luminance of a color is defined as:
L = 0.2126 * R + 0.7152 * G + 0.0722 * B;
Where R, G, and B are the components from the LRGB color space.
To compute it in culori:
function luminance(color) {
let c = culori.lrgb(color);
return 0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b;
}
Note: The WCAG defines the luminance using a deprecated value for converting sRGB to LRGB. If you'd like a strict implementation, you'll need to write a separate sRGB → LRGB conversion from scratch.
Contrast ratio
Using the luminance()
function above, the contrast()
ratio is simply the ratio between the luminances of two colors, with the values shifted by 0.05 to avoid division by zero when comparing against black.
function contrast(colorA, colorB) {
let L1 = luminance(colorA);
let L2 = luminance(colorB);
return (L1 + 0.05) / (L2 + 0.05);
}
Related projects
These projects add more functionality to culori, but they're separate as to keep the core bundle small:
culori-scales
Color scales (ColorBrewer, matplotlib, etc).
culori-names
More named colors, from a variety of sources.
Products using culori
Moqups
All our color-manipulation functions are handled by culori.
Dainty
Dainty is a configurable refined and balanced color theme for Visual Studio using Culori’s CIELAB capabilities for generating and processing colors.
Does your product/project use culori? Create a PR and add yourself to this list!
Other projects
Some popular libraries you may want to look at are:
- TinyColor by Brian Grinstead
- color by Heather Arthur
- color.js by Andrew Brehaut et al
- chromatist by Jacob Rus
- gradstop.js by Siddharth Parmar
Please suggest more interesting projects.