Skip to content

Instantly share code, notes, and snippets.

@fgassert
Forked from mbostock/.block
Last active August 29, 2015 14:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fgassert/11304263 to your computer and use it in GitHub Desktop.
Save fgassert/11304263 to your computer and use it in GitHub Desktop.

D3 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.v3.min.js"></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(5);
var x = d3.scale.ordinal()
.domain(values)
.rangeRoundBands([14, width - 14]);
var color = d3.scale.linear()
.domain([0, values.length - 1])
.range(["#ff1900", "#dddddd"]);
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