I've started reading Josef Albers' Interaction of Color and am learning quite a lot from it. I particularly enjoy his details about what to expect in student reactions to particular exercises; you know he must have anticipated these reactions each time, with every class.
The basic principles of the first few chapters should be easy to demonstrate using d3.
First we have to be sure we can generate html and javascript markup in the notebook and invoke it in place. Let's try following this person's lead and call out to html first:
In [12]:
%%html
<h3>this is an h3 generated by html markup in the notebook</h3>
It works! Hmm, the output is shifted over within the results-containing block, but that's not really a problem.
Okay, next: let's set up a simple d3 viz container like in the example:
In [13]:
%%html
<h4>viz appears below here</h4>
<div id="viz"></div>
<h4>viz appears above here</h4>
And then load up d3 and get to rendering a simple something:
In [18]:
%%javascript
require.config({paths: {d3: "http://d3js.org/d3.v3.min"}});
require(["d3"], function(d3) {
var width = 400,
height = 200;
var svg = d3.select("#viz").append("svg")
.attr("width", width)
.attr("height", height);
var circle = svg.append("circle")
.attr("cx", width / 2)
.attr("cy", height / 2)
.attr("r", 100)
.attr("fill", "#5DAAD4");
});
That works, too! Now we're cooking with gas.
In this section we see the first of several color plates and I am already drawn in to what he has to teach us about the relativity of color, that "color is the most relative medium in art." Let's mimic the first experiment, making one color look different from itself, using different background colors. I'm guessing (poorly!) at colors somewhat close to those in the prepared studies in the text itself, using the color picker available here.
In [8]:
%%html
<div id="example-4a"></div>
In [9]:
%%javascript
require(["d3"], function(d3) {
var width = 700,
height = 800;
var svg = d3.select("#example-4a").append("svg")
.attr("width", width)
.attr("height", height);
var outer1 = svg.append("rect")
.attr("x", 50)
.attr("y", 50)
.attr("width", 600)
.attr("height", 300)
.attr("fill", "#4C0A73");
var inner1 = svg.append("rect")
.attr("x", 100)
.attr("y", 100)
.attr("width", 500)
.attr("height", 200)
.attr("fill", "#5A6E5E");
var outer2 = svg.append("rect")
.attr("x", 50)
.attr("y", 450)
.attr("width", 600)
.attr("height", 300)
.attr("fill", "#9DD1CE");
var inner2 = svg.append("rect")
.attr("x", 100)
.attr("y", 500)
.attr("width", 500)
.attr("height", 200)
.attr("fill", "#5A6E5E");
})
This use of d3 demonstrates several features which make it an appealing toolkit, even for a beginner:
I learned SVG many years ago, back in 2004, when it was still a fairly new web standard and had very few useable implementations. The good news is that it is much more widely implemented now, and that it hasn't changed much since back then (there's only been one new revision, a "second edition" of the first version), so if you know a few SVG basics then it's easy to see that d3 just uses an API defined in javascript to generate SVG. This is a lot easier than generating SVG by hand yourself; I know from first-hand experience a decade ago.
Another way to think of d3 is as a "domain specific language" for dynamic documents on the web. It's just javascript, but it's a flavor of types and techniques specific to generating SVG using javascript that lends itself well to visualizing data.
In any case, this copied "plate" demonstrates the basic principle well: the inner color is exactly the same in both rectangles, and it is the interaction between this color and the differing surrounding / background colors that makes it look different from itself from one to the next.
To make this a little more dynamic (it is the web after all) let's add the ability to change colors by clicking on the inner boxes. The code will be the same, but with the "click" method defined on each.
Click on the top inner box to make both inner boxes lighter. Click on the bottom inner box to make both inner boxes darker.
In [10]:
%%html
<div id="changing-colors"></div>
In [11]:
%%javascript
require(["d3"], function(d3) {
var width = 700,
height = 800;
var innercolor = "#5A6E5E";
var svg = d3.select("#changing-colors").append("svg")
.attr("width", width)
.attr("height", height);
var outer1 = svg.append("rect")
.attr("x", 50)
.attr("y", 50)
.attr("width", 600)
.attr("height", 300)
.attr("fill", "#4C0A73");
var inner1 = svg.append("rect")
.attr("x", 100)
.attr("y", 100)
.attr("width", 500)
.attr("height", 200)
.attr("fill", innercolor)
.on("click", function(){
brighten();
});
var outer2 = svg.append("rect")
.attr("x", 50)
.attr("y", 450)
.attr("width", 600)
.attr("height", 300)
.attr("fill", "#9DD1CE");
var inner2 = svg.append("rect")
.attr("x", 100)
.attr("y", 500)
.attr("width", 500)
.attr("height", 200)
.attr("fill", innercolor)
.on("click", function(){
darken();
});
function brighten () {
[inner1, inner2].forEach(function(item) {
item.style("fill", d3.hsl(item.style("fill")).brighter(.1));
});
}
function darken () {
[inner1, inner2].forEach(function(item) {
item.style("fill", d3.hsl(item.style("fill")).darker(.1));
});
}
});
This further reinforces the effect; at some points as you click to ratchet the intensity up or down the two inner boxes look like wholly different colors, and at other points (especially the extremes) it is clear that they are the same.