Skip to content

Instantly share code, notes, and snippets.

@bewest
Forked from mbostock/.block
Created December 11, 2012 17:37
Show Gist options
  • Save bewest/4260525 to your computer and use it in GitHub Desktop.
Save bewest/4260525 to your computer and use it in GitHub Desktop.
L*a*b* and HCL color spaces

D3 2.10 adds support for CIE L*a*b* and HCL. These perceptually-motivated color spaces are designed with humans (rather than computers) in mind. RGB and HSL interpolation can cause unintentional grouping due to parts of the color space appearing more visually similar; L*a*b* and HCL, in contrast, are perceptually uniform. For more information, see Gregor Aisch’s post How To Avoid Equidistant HSV Colors and Drew Skau’s post Dear NASA: No More Rainbow Color Scales, Please.

You can create L*a*b* or HCL colors directly using d3.lab or d3.hcl. For example:

var steelblue = d3.lab(52, -4, -32);
var steelblue = d3.hcl(-97, 32, 52);

You can also convert from RGB or HSL. This is useful for creating brighter or darker colors with uniform changes in perception:

var lightsteelblue = d3.lab("#4682b4").brighter();
var darksteelblue = d3.hcl("hsl(207, 44%, 49%)").darker();

Best of all, you can use d3.interpolateLab or d3.interpolateHcl in conjunction with quantitative scales and transitions:

var color = d3.scale.linear()
    .range(["steelblue", "brown"])
    .interpolate(d3.interpolateHcl);

L*a*b* and HCL interpolation is a convenient way to implement color scales that are comparable in quality to Colorbrewer.

<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
</style>
<body>
<script src="http://d3js.org/d3.v2.min.js?2.10.0"></script>
<script>
var interpolators = {
"HSL": d3.interpolateHsl,
"HCL": d3.interpolateHcl,
"Lab": d3.interpolateLab,
"RGB": d3.interpolateRgb
};
var width = 960,
height = 500;
var y = d3.scale.ordinal()
.domain(d3.keys(interpolators))
.rangeRoundBands([0, height], .1);
var values = d3.range(960 - 28);
var x = d3.scale.ordinal()
.domain(values)
.rangeRoundBands([14, width - 14]);
var color = d3.scale.linear()
.domain([0, values.length - 1])
.range(["hsl(62,100%,90%)", "hsl(228,30%,20%)"]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g = svg.selectAll("g")
.data(d3.entries(interpolators))
.enter().append("g")
.attr("transform", function(d) { return "translate(0," + y(d.key) + ")"; });
g.each(function(d) {
color.interpolate(d.value);
d3.select(this).selectAll("rect")
.data(values)
.enter().append("rect")
.attr("x", x)
.attr("width", x.rangeBand())
.attr("height", y.rangeBand)
.style("fill", color);
});
g.append("text")
.attr("x", 28)
.attr("y", y.rangeBand() / 2)
.attr("dy", ".35em")
.text(function(d) { return d.key; });
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment