d3.js is a great library for producing visualizations on the web. What if you want the power and syntax of d3, but don’t have or want a browser? The combination of node and the npm library jsdom can get you there.

What you’ll need

  • A shell
  • node, npm should come along

Minimal working example

This is going to assume that you only want d3 and jsdom installed locally. We will need two libraries from npm, d3 and jsdom. First cd into the directory you want create the visualization in. We can get the libraries by running

npm install d3
npm install jsdom

Make a new file called myfigure.js and start to edit it.

First, we need to get variables referencing the libraries. In node, we do that with requires.

const d3 = require("d3")
const jsdom = require("jsdom");
const { JSDOM } = jsdom;

d3 was built for the web and expects a top-level document variable. This is what jsdom is for. It emulates a DOM using only JavaScript. We need to make that top level document variable using jsdom.

const { document } = (new JSDOM()).window;

In case you aren’t aware of that ‘variable in curly brace’ syntax, that is called destructuring and is a useful language feature.

We can now make use of d3 to draw something! I’m going to keep things simple and just draw some red circles.

We use d3’s select function on our ‘fake’ document, and then we are off and running. Let’s make a svg element.

var width = 1000;
var height = 1000;

var svg = d3.select(document).select("body").append("svg")
    .attr('xmlns', 'http://www.w3.org/2000/svg')
    .attr("width", width)
    .attr("height", height);

Bind some data…

var data = [1, 250, 750]

svg.append("g").selectAll("circle")
    .data(data)
    .enter("circle")
        .append("circle").attr("cx", d => d)
        .attr("cy", "250")
        .style("fill", "red")
        .attr("r", "25")

And then print to standard output.

console.log(svg.node().outerHTML)

Here’s the entire file:

// myfigure.js
const d3 = require("d3")
const jsdom = require("jsdom");
const { JSDOM } = jsdom;

const { document } = (new JSDOM()).window;

var width = 1000;
var height = 1000;

var svg = d3.select(document).select("body").append("svg")
 .attr('xmlns', 'http://www.w3.org/2000/svg')
 .attr("width", width)
 .attr("height", height);

var data = [1, 250, 750]

svg.append("g").selectAll("circle")
    .data(data)
    .enter("circle")
        .append("circle").attr("cx", d => d)
        .attr("cy", "250")
        .style("fill", "red")
        .attr("r", "25")

console.log(svg.node().outerHTML)

Now in your shell you should be able to run the script with node.

node myfigure.js

If it goes well, you should see the svg XML printed.

Now you could pipe this into other programs or just redirect this to a file.

node myfigure.js > myfigure.svg

Open the svg file up in your favorite vector image program and check it out!