# chroma.js

chroma.js is a tiny JavaScript library (14kB) for dealing with colors!

## Quick-start

Here are a couple of things chroma.js can do for you:

- read colors from a wide range of formats
- analyze and manipulate colors
- convert colors into wide range of formats
- linear and bezier interpolation in different color spaces

Here's an example for a simple read / manipulate / output chain:

    chroma('pink').darken().saturate(2).hex()

Aside from that, chroma.js can also help you generate nice colors using various methods, for instance to be used in color palette for maps or data visualization.

    chroma.scale(['#fafa6e','#2A4858'])
        .mode('lch').colors(6)

chroma.js has a lot more to offer, but that's the gist of it.

## API

### chroma

#### (color)

The first step is to get your color into chroma.js. That's what the generic constructor `chroma()` does. The function is trying to guess the color format for you. For instances, it will recognized any named color from the W3CX11 specification:

    chroma('hotpink')

If there's no matching named color chroma.js checks for a hexadecimal string. It ignores case, the `#` sign is optional, and the shorter three letter format is recognized as well. So any of these are valid hexadecimal representations: `#ff3399`, `FF3399`, `#f39`, etc.

    chroma('#ff3399');
    chroma('F39');

In addition to hex strings, hexadecimal numbers (in fact, just any number between `0` and `16777215`), will be recognized, too.

    chroma(0xff3399)

If you pass the RGB channels individually, too. Each parameter must be within `0..255`. You can pass the numbers as individual arguments or as array.

    chroma(0xff, 0x33, 0x99);
    chroma(255, 51, 153);
    chroma([255, 51, 153]);

You can construct colors from different color spaces by passing the name of color space as the last argument. Here we define the same color in HSL by passing the hue angle (0-360) and percentages for saturation and l*ightness:

    chroma(330, 1, 0.6, 'hsl')

### chroma.hsl

#### (hue, saturation, lightness)

Alternatively, every color space has its own constructor function under the `chroma` namespace. For a list of all supported color spaces, check the appendix.

    chroma.hsl(330, 1, 0.6)

### chroma.hsv

#### (hue, saturation, value)

### chroma.lab

#### (Lightness, a, b)

### chroma.lch

#### (Lightness, chroma, hue)

The range for `lightness` and `chroma` depend on the hue, but go roughly from 0..100-150. The range for `hue` is 0..360.

    chroma.lch(80, 40, 130);
    chroma(80, 40, 130, 'lch');

### chroma.hcl

#### (hue, chroma, lightness)

You can use hcl instead of Lch. Lightness and hue channels are switched to be more consistent with HSL.

    chroma.hcl(130, 40, 80);
    chroma(130, 40, 80, 'hcl');

### chroma.cmyk

#### (cyan, magenta, yellow, black)

Each between 0 and 1.

    chroma.cmyk(0.2, 0.8, 0, 0);
    chroma(0.2, 0.8, 0, 0, 'cmyk');

### chroma.gl

#### (red, green, blue, [alpha])

GL is a variant of RGB(A), with the only difference that the components are normalized to the range of `0..1`.

    chroma.gl(0.6, 0, 0.8);
    chroma.gl(0.6, 0, 0.8, 0.5);
    chroma(0.6, 0, 0.8, 'gl');

### chroma.temperature

#### (K)

Returns a color from the color temperature scale. Based on Neil Bartlett's implementation.

    chroma.temperature(2000); // candle light
    chroma.temperature(3500); // sunset
    chroma.temperature(6500); // daylight

The effective temperature range goes from `0` to about `30000` Kelvin,

    f = function(i) {
        return chroma.temperature(i * 30000)
    }

### chroma.mix

#### (color1, color2, ratio=0.5, mode='rgb')

Mixes two colors. The mix ratio is a value between 0 and 1.

    chroma.mix('red', 'blue');
    chroma.mix('red', 'blue', 0.25);
    chroma.mix('red', 'blue', 0.75);

The color mixing produces different results based the color space used for interpolation.

    chroma.mix('red', 'blue', 0.5, 'rgb');
    chroma.mix('red', 'blue', 0.5, 'hsl');
    chroma.mix('red', 'blue', 0.5, 'lab');
    chroma.mix('red', 'blue', 0.5, 'lch');
    chroma.mix('red', 'blue', 0.5, 'lrgb');

### chroma.average

#### (colors, mode='rgb')

Similar to `chroma.mix`, but accepts more than two colors. Simple averaging of R,G,B components and the alpha channel.

    colors = ['#ddd', 'yellow', 'red', 'teal'];
    chroma.average(colors); // rgb
    chroma.average(colors, 'lab');
    chroma.average(colors, 'lch');
    chroma.average(colors, 'lrgb');

Also works with alpha channels.

    chroma.average(['red', 'rgba(0,0,0,0.5)']).css();

### chroma.blend

#### (color1, color2, mode)

Blends two colors using RGB channel-wise blend functions. Valid blend modes are `multiply`, `darken`, `lighten`, `screen`, `overlay`, `burn`, and `dodge`.

    chroma.blend('4CBBFC', 'EEEE22', 'multiply');
    chroma.blend('4CBBFC', 'EEEE22', 'darken');
    chroma.blend('4CBBFC', 'EEEE22', 'lighten');

### chroma.random

#### ()

Creates a random color by generating a random hexadecimal string.

    chroma.random();
    chroma.random();
    chroma.random();

### chroma.contrast

#### (color1, color2)

Computes the WCAG contrast ratio between two colors. A minimum contrast of 4.5:1 is recommended to ensure that text is still readable against a background color.

    // contrast smaller than 4.5 = too low
    chroma.contrast('pink', 'hotpink');
    // contrast greater than 4.5 = high enough
    chroma.contrast('pink', 'purple');

### chroma.distance

#### (color1, color2, mode='lab')

Computes the Euclidean distance between two colors in a given color space (default is `Lab`).

    chroma.distance('#fff', '#ff0', 'rgb');
    chroma.distance('#fff', '#f0f', 'rgb');
    chroma.distance('#fff', '#ff0');
    chroma.distance('#fff', '#f0f');

### chroma.deltaE

#### (reference, sample, L=1, C=1)

Computes color difference as developed by the Colour Measurement Committee of the Society of Dyers and Colourists (CMC) in 1984. The implementation is adapted from Bruce Lindbloom. The parameters L and C are weighting factors for lightness and chromaticity.

    chroma.deltaE('#ededee', '#edeeed');
    chroma.deltaE('#ececee', '#eceeec');
    chroma.deltaE('#e9e9ee', '#e9eee9');
    chroma.deltaE('#e4e4ee', '#e4eee4');
    chroma.deltaE('#e0e0ee', '#e0eee0');

### chroma.brewer

chroma.brewer is an map of ColorBrewer scales that are included in chroma.js for convenience. chroma.scale uses the colors to construct.

    chroma.brewer.OrRd

### chroma.limits

#### (data, mode, n)

A helper function that computes class breaks for you, based on data. It supports the modes equidistant (e), quantile (q), logarithmic (l), and k-means (k). Let's take a few numbers as sample data.

    var data = [2.0,3.5,3.6,3.8,3.8,4.1,4.3,4.4,
                4.6,4.9,5.2,5.3,5.4,5.7,5.8,5.9,
                6.2,6.5,6.8,7.2,8];

equidistant breaks are computed by dividing the total range of the data into n groups of equal size.

    chroma.limits(data, 'e', 4);

In the quantile mode, the input domain is divided by quantile ranges.

    chroma.limits(data, 'q', 4);

logarithmic breaks are equidistant breaks but on a logarithmic scale.

    chroma.limits(data, 'l', 4);

k-means break is using the 1-dimensional k-means clustering algorithm to find (roughly) n groups of "similar" values. Note that this k-means implementation does not guarantee to find exactly n groups.

    chroma.limits(data, 'k', 4);

## color

### color.alpha

#### (a)

Get and set the color opacity using `color.alpha`.

    chroma('red').alpha(0.5);
    chroma('rgba(255,0,0,0.35)').alpha();

### color.darken

#### (value=1)

Once loaded, chroma.js can change colors. One way we already saw above, you can change the lightness.

    chroma('hotpink').darken();
    chroma('hotpink').darken(2);
    chroma('hotpink').darken(2.6);

### color.brighten

#### (value=1)

Similar to `darken`, but the opposite direction

    chroma('hotpink').brighten();
    chroma('hotpink').brighten(2);
    chroma('hotpink').brighten(3);

### color.saturate

#### (value=1)

Changes the saturation of a color by manipulating the Lch chromaticity.

    chroma('slategray').saturate();
    chroma('slategray').saturate(2);
    chroma('slategray').saturate(3);

### color.desaturate

#### (value=1)

Similar to `saturate`, but the opposite direction.

    chroma('hotpink').desaturate();
    chroma('hotpink').desaturate(2);
    chroma('hotpink').desaturate(3);

### color.set

#### (channel, value)

Changes a single channel and returns the result a new `chroma` object.

    // change hue to 0 deg (=red)
    chroma('skyblue').set('hsl.h', 0);
    // set chromaticity to 30
    chroma('hotpink').set('lch.c', 30);

Relative changes work, too:

    // half Lab lightness
    chroma('orangered').set('lab.l', '*0.5');
    // double Lch saturation
    chroma('darkseagreen').set('lch.c', '*2');

### color.get

#### (channel)

Returns a single channel value.

    chroma('orangered').get('lab.l');
    chroma('orangered').get('hsl.l');
    chroma('orangered').get('rgb.g');

### color.luminance

#### ([lum, mode='rgb'])

If called without arguments color.luminance returns the relative brightness, according to the WCAG definition. Normalized to `0` for darkest black and `1` for lightest white.

    chroma('white').luminance();
    chroma('aquamarine').luminance();
    chroma('hotpink').luminance();
    chroma('darkslateblue').luminance();
    chroma('black').luminance();

chroma.js also allows you to adjust the luminance of a color. The source color will be interpolated with black or white until the correct luminance is found.

    // set lumincance to 50% for all colors
    chroma('white').luminance(0.5);
    chroma('aquamarine').luminance(0.5);
    chroma('hotpink').luminance(0.5);
    chroma('darkslateblue').luminance(0.5);

By default, this interpolation is done in RGB, but you can interpolate in different color spaces by passing them as second argument:

    chroma('aquamarine').luminance(0.5); // rgb
    chroma('aquamarine').luminance(0.5, 'lab');
    chroma('aquamarine').luminance(0.5, 'hsl');

### color.hex

Finally, chroma.js allows you to output colors in various color spaces and formats.

Most often you will want to output the color as hexadecimal string.

    chroma('orange').hex()

### color.name

Returns the named color. Falls back to hexadecimal RGB string, if the color isn't present.

    chroma('#ffa500').name();
    chroma('#ffa505').name();

### color.css

Returns a `RGB()` or `HSL()` string representation that can be used as CSS-color definition.

    chroma('teal').css();
    chroma('teal').alpha(0.5).css();
    chroma('teal').css('hsl');

### color.rgb

#### (round=true)

Returns an array with the `red`, `green`, and `blue` component, each as number within the range `0..255`. Chroma internally stores RGB channels as floats but rounds the numbers before returning them. You can pass `false` to prevent the rounding.

    chroma('orange').rgb();
    chroma('orange').darken().rgb();
    chroma('orange').darken().rgb(false);

### color.rgba

#### (round=true)

Just like `color.rgb` but adds the alpha channel to the returned array.

    chroma('orange').rgba();
    chroma('hsla(20, 100%, 40%, 0.5)').rgba();

### color.hsl

Returns an array with the `hue`, `saturation`, and `lightness` component. Hue is the color angle in degree (`0..360`), saturation and lightness are within `0..1`. Note that for hue-less colors (black, white, and grays), the hue component will be NaN.

    chroma('orange').hsl();
    chroma('white').hsl();

### color.hsv

Returns an array with the `hue`, `saturation`, and `value` components. Hue is the color angle in degree (`0..360`), saturation and value are within `0..1`. Note that for hue-less colors (black, white, and grays), the hue component will be NaN.

    chroma('orange').hsv();
    chroma('white').hsv();

### color.hsi

Returns an array with the `hue`, `saturation`, and `intensity` components, each as number between 0 and 255. Note that for hue-less colors (black, white, and grays), the hue component will be NaN.

    chroma('orange').hsi();
    chroma('white').hsi();

### color.lab

Returns an array with the L, a, and b components.

    chroma('orange').lab()

### color.lch

Returns an array with the Lightness, chroma, and hue components.

    chroma('skyblue').lch()

### color.hcl

Alias of lch, but with the components in reverse order.

    chroma('skyblue').hcl()

### color.temperature

Estimate the temperature in Kelvin of any given color, though this makes the only sense for colors from the temperature gradient above.

    chroma('#ff3300').temperature();
    chroma('#ff8a13').temperature();
    chroma('#ffe3cd').temperature();
    chroma('#cbdbff').temperature();
    chroma('#b3ccff').temperature();

### color.gl

Like RGB, but in the channel range of `[0..1]` instead of `[0..255]`

    chroma('33cc00').gl();

### color.clipped

When converting colors from CIELab color spaces to RGB the color channels get clipped to the range of `[0..255]`. Colors outside that range may exist in nature but are not displayable on RGB monitors (such as ultraviolet). you can use color.clipped to test if a color has been clipped or not.

    [c = chroma.hcl(50, 40, 20), c.clipped()];
    [c = chroma.hcl(50, 40, 40), c.clipped()];
    [c = chroma.hcl(50, 40, 60), c.clipped()];
    [c = chroma.hcl(50, 40, 80), c.clipped()];
    [c = chroma.hcl(50, 40, 100), c.clipped()];

As a bonus feature you can access the unclipped RGB components using `color._rgb._unclipped`.

    chroma.hcl(50, 40, 100).rgb();
    chroma.hcl(50, 40, 100)._rgb._unclipped;

## color scales

### chroma.scale

#### (colors=['white','black'])

A color scale, created with `chroma.scale`, is a function that maps numeric values to a color palette. The default scale has the domain `0..1` and goes from white to black.

    f = chroma.scale();
    f(0.25);
    f(0.5);
    f(0.75);

You can pass an array of colors to `chroma.scale`. Any color that can be read by `chroma()` will work here, too. If you pass more than two colors, they will be evenly distributed along the gradient.

    chroma.scale(['yellow', '008ae5']);
    chroma.scale(['yellow', 'red', 'black']);

### scale.domain

#### (domain)

You can change the input domain to match your specific use case.

    // default domain is [0,1]
    chroma.scale(['yellow', '008ae5']);
    // set domain to [0,100]
    chroma.scale(['yellow', '008ae5']).domain([0,100]);

You can use the domain to set the exact positions of each color.

    // default domain is [0,1]
    chroma.scale(['yellow', 'lightgreen', '008ae5'])
        .domain([0,0.25,1]);

### scale.mode

#### (mode)

As with `chroma.mix`, the result of the color interpolation will depend on the color mode in which the channels are interpolated. The default mode is `RGB`:

    chroma.scale(['yellow', '008ae5']);

This is often fine, but sometimes, two-color `RGB` gradients goes through kind of grayish colors, and `Lab` interpolation produces better results:

    chroma.scale(['yellow', 'navy']);
    chroma.scale(['yellow', 'navy']).mode('lab');

Also note how the RGB interpolation can get very dark around the center. You can achieve better results using linear RGB interpolation:

    chroma.scale(['#f00', '#0f0']);
    chroma.scale(['#f00', '#0f0']).mode('lrgb');

Other useful interpolation modes could be `HSL` or `Lch`, though both tend to produce too saturated / glowing gradients.

    chroma.scale(['yellow', 'navy']).mode('lab');
    chroma.scale(['yellow', 'navy']).mode('hsl');
    chroma.scale(['yellow', 'navy']).mode('lch');

### scale.gamma

Gamma-correction can be used to "shift" a scale's center more the the beginning (gamma < 1) or end (gamma > 1), typically used to "even" the lightness gradient. Default is 1.

    chroma.scale('YlGn').gamma(0.5);
    chroma.scale('YlGn').gamma(1);
    chroma.scale('YlGn').gamma(2);

### scale.correctLightness

This makes sure the lightness range is spread evenly across a color scale. Especially useful when working with multi-hue color scales, where simple gamma correction can't help you very much.

    chroma.scale(['black','red','yellow','white']);

    chroma.scale(['black','red','yellow','white'])
        .correctLightness();

### scale.cache

#### (true|false)

By default `chroma.scale` instances will cache each computed value => color pair. You can turn off the cache by setting

    chroma.scale(['yellow', '008ae5']).cache(false);

### scale.padding

#### (pad)

Reduces the color range by cutting of a fraction of the gradient on both sides. If you pass a single number, the same padding will be applied to both ends.

    chroma.scale('RdYlBu');
    chroma.scale('RdYlBu').padding(0.15);
    chroma.scale('RdYlBu').padding(0.3);
    chroma.scale('RdYlBu').padding(-0.15);

Alternatively you can specify the padding for each sides individually by passing an array of two numbers.

    chroma.scale('OrRd');
    chroma.scale('OrRd').padding([0.2, 0]);

### scale.colors

#### (num, format='hex')

You can call `scale.colors(n)` to quickly grab `n` equi-distant colors from a color scale. If called with no arguments, `scale.colors` returns the original array of colors used to create the scale.

    chroma.scale('OrRd').colors(5);
    chroma.scale(['white', 'black']).colors(12);

If you want to return `chroma` instances just pass null as `format`.

### scale.classes

#### (numOrArray)

If you want the scale function to return a distinct set of colors instead of a continuous gradient, you can use `scale.classes`. If you pass a number the scale will broken into equi-distant classes:

    // continuous
    chroma.scale('OrRd');
    // class breaks
    chroma.scale('OrRd').classes(5);
    chroma.scale('OrRd').classes(8);

You can also define custom class breaks by passing them as array:

    chroma.scale('OrRd').classes([0,0.3,0.55,0.85,1]);

### chroma.brewer

chroma.js includes the definitions from ColorBrewer2.org. Read more about these colors in the corresponding paper by Mark Harrower and Cynthia A. Brewer.

    chroma.scale('YlGnBu');
    chroma.scale('Spectral');

To reverse the colors you could simply reverse the domain:

    chroma.scale('Spectral').domain([1,0]);

You can access the colors directly using `chroma.brewer`.

    chroma.brewer.OrRd

### chroma.bezier

#### (colors)

`chroma.bezier` returns a function that bezier-interpolates between colors in `Lab` space. The input range of the function is `[0..1]`.

    // linear interpolation
    chroma.scale(['yellow', 'red', 'black']);
    // bezier interpolation
    chroma.bezier(['yellow', 'red', 'black']);

You can convert an bezier interpolator into a chroma.scale instance

    chroma.bezier(['yellow', 'red', 'black'])
        .scale()
        .colors(5);

## cubehelix

### chroma.cubehelix

#### (start=300, rotations=-1.5, hue=1, gamma=1, lightness=[0,1])

Dave Green's cubehelix color scheme!!

    // use the default helix...
    chroma.cubehelix();
    // or customize it
    chroma.cubehelix()
        .start(200)
        .rotations(-0.5)
        .gamma(0.8)
        .lightness([0.3, 0.8]);

### cubehelix.start

#### (hue)

start color for hue rotation, default=`300`

    chroma.cubehelix().start(300);
    chroma.cubehelix().start(200);

### cubehelix.rotations

#### (num)

number (and direction) of hue rotations (e.g. 1=`360°`, 1.5=`540°``), default=-1.5

    chroma.cubehelix().rotations(-1.5);
    chroma.cubehelix().rotations(0.5);
    chroma.cubehelix().rotations(3);

### cubehelix.hue

#### (numOrRange)

hue controls how saturated the colour of all hues are. either single value or range, default=1

    chroma.cubehelix();
    chroma.cubehelix().hue(0.5);
    chroma.cubehelix().hue([1,0]);

### cubehelix.gamma

#### (factor)

gamma factor can be used to emphasise low or high intensity values, default=1

    chroma.cubehelix().gamma(1);
    chroma.cubehelix().gamma(0.5);

### cubehelix.lightness

#### (range)

lightness range: default: [0,1] (black -> white)

    chroma.cubehelix().lightness([0,1]);
    chroma.cubehelix().lightness([1,0]);
    chroma.cubehelix().lightness([0.3,0.7]);

### cubehelix.scale

You can call `cubehelix.scale()` to use the cube-helix through the `chroma.scale` interface.

    chroma.cubehelix()
        .start(200)
        .rotations(-0.35)
        .gamma(0.7)
        .lightness([0.3, 0.8])
      .scale() // convert to chroma.scale
        .correctLightness()
        .colors(5);

﻿