Appearance
D3 (Data-Driven Documents) is a JavaScript library that binds your data directly to SVG shapes in the browser, so a historian can build fully custom, interactive visualisations that tools like Datawrapper cannot. The trade-off is that you write code. If you only need a standard chart, start with Observable Plot (D3's friendlier sibling); reach for raw D3 when you need a bespoke layout or interaction. This guide walks through the core ideas with a small worked example.
What is D3, in plain language?
Think of D3 as glue between two worlds: your data (an array of records, say one per Tudor parish) and the document (SVG circles, lines, and text on a web page). You describe a rule — "one circle per parish, x-position from its founding year" — and D3 creates, updates, and removes elements to keep the page matching the data. That data-to-pixels mapping is the whole idea.
Do you need JavaScript first?
You need the basics: variables, arrays, functions, and arrow syntax (d => d.year), plus a little HTML and SVG. You do not need to be an expert. The classic beginner trap is trying to learn JavaScript and D3 simultaneously from zero — get a week of plain JavaScript under your belt first, and D3 will feel like a focused toolkit rather than a second language.
Should you use Observable Plot or raw D3?
For most historical charts, start higher up the ladder:
| Need | Use | Effort |
|---|---|---|
| Standard line/bar/scatter | Observable Plot | Very low |
| Custom layout, novel encoding | Raw D3 | High |
| Bespoke interaction, animation | Raw D3 | High |
| Quick exploratory chart | Observable Plot | Very low |
A timeline of monarchs in Plot is a handful of lines; the same in raw D3 is fifty. Save raw D3 for when its control is genuinely needed.
How do scales work?
A scale is a function that maps your data's range to screen space, so you never hand-compute pixels. For a timeline of founding dates:
js
import * as d3 from "d3";
const x = d3.scaleTime()
.domain([new Date(1500, 0, 1), new Date(1900, 0, 1)]) // your years
.range([40, 760]); // pixels
x(new Date(1600, 0, 1)); // → a pixel position inside the axisscaleTime understands dates and leap years; scaleLinear handles counts; scaleOrdinal maps categories to colours. Choosing the right scale is most of the work.
A small worked example: plotting parishes by date
Here is a minimal data join — D3's central mechanism — placing one circle per record:
js
const data = [
{ parish: "St Mary", founded: 1540, records: 120 },
{ parish: "All Saints", founded: 1612, records: 88 },
{ parish: "St Giles", founded: 1701, records: 54 },
];
const svg = d3.select("body").append("svg")
.attr("width", 800).attr("height", 200);
svg.selectAll("circle")
.data(data)
.join("circle") // enter + update + exit, handled
.attr("cx", d => x(new Date(d.founded, 0, 1)))
.attr("cy", 100)
.attr("r", d => Math.sqrt(d.records)); // area ~ record count.join("circle") is the modern, beginner-friendly form of the data join: it creates a circle for each record and keeps them in sync if the data changes. Radius scaled by the square root of the count keeps area proportional — a small but important honesty point.
What should you learn next?
Once the join clicks, the productive path is: add an axis with d3.axisBottom(x), add tooltips on mouseover, then explore a layout helper (d3.forceSimulation for networks, d3.geoPath for maps). Resist building everything from primitives — lean on Plot for ordinary charts and reserve your D3 effort for the genuinely custom historical visual that justified learning it.
Key Takeaways
- D3 binds data to SVG, enabling custom interactive visualisations at the cost of code.
- Learn basic JavaScript first; learning both from zero at once is the usual stall point.
- Prefer Observable Plot for standard charts; drop to raw D3 only when necessary.
- Scales map data domains to pixel ranges — pick
scaleTime,scaleLinear, orscaleOrdinaldeliberately. - The data join via
.join()is the core concept; start there. - Scale circle radius by the square root of a count so area stays proportional.
Frequently Asked Questions
What is D3 and why would a historian use it?
D3 (Data-Driven Documents) is a JavaScript library that binds data to SVG elements in the browser, letting historians build bespoke, fully interactive visualisations that off-the-shelf tools cannot produce, at the cost of writing code.
Do I need to know JavaScript before learning D3?
You need comfort with basic JavaScript (variables, arrays, functions, arrow syntax) and a little HTML and CSS; D3 itself is learnable alongside, but trying to learn both from absolute zero at once is the usual reason beginners stall.
Should I use raw D3 or a simpler wrapper like Observable Plot?
Start with Observable Plot for standard charts because it produces good results in a few lines; drop to raw D3 only when you need a custom layout, bespoke interaction, or an encoding that Plot cannot express.
How do scales work in D3?
A scale is a function mapping your data domain (for example years 1500 to 1900) to a visual range (for example 0 to 800 pixels); D3 provides linear, time, ordinal, and other scales so you rarely compute pixel positions by hand.
What is the single hardest concept in D3 for beginners?
The data join — how D3 matches array elements to DOM elements through enter, update, and exit selections; modern D3 simplifies this with the join() method, which is where beginners should start.