← Back to Module 09

D3 Scales Compared

Scales are functions that map data values (the domain) to pixel positions (the range). Below, the hand-rolled linearScale() from Module 02 runs side-by-side with D3's scaleLinear(). They produce identical output — D3 just gives you more features.

1. Linear Scale: Manual vs. D3

0
1250
300 → 0 (inverted for y-axis: bottom to top)
625

Manual linearScale() (Module 02)

function linearScale(dMin, dMax, rMin, rMax) { return function(value) { const fraction = (value - dMin) / (dMax - dMin); return rMin + fraction * (rMax - rMin); }; } const yScale = linearScale(0, 1250, 300, 0); yScale(625); // = ?
Result: 150.00 px

d3.scaleLinear()

const yScale = d3.scaleLinear() .domain([0, 1250]) .range([300, 0]); yScale(625); // = ?
Result: 150.00 px
Both produce the same value
300 (bottom) 0 (top)
Manual
D3
They are the same math. D3's scaleLinear() is doing the exact same fraction-based interpolation as the manual function. But D3 adds .nice(), .ticks(), .invert(), and .clamp() on top.

2. d3.scaleBand() for Categorical Data

Unlike scaleLinear(), which maps numbers to numbers, scaleBand() maps categories (strings) to evenly-spaced bands. Each band has a starting position and a width. This is what you need for the x-axis of a bar chart.

0.20
500px
const xScale = d3.scaleBand() .domain(['/home', '/about', '/products', '/contact', '/blog']) .range([0, 500]) .padding(0.20); xScale('/home'); // → start position xScale.bandwidth(); // → band width
bandwidth() = 80px

3. Scales Are Just Functions

This is the key insight: a D3 scale is a plain JavaScript function. You call it with a value and get a pixel back. There is no magic.

// Create the scale const yScale = d3.scaleLinear() .domain([0, 1250]) .range([300, 0]); // It's just a function: typeof yScale; // "function" yScale(1250); // 0 (max value → top) yScale(0); // 300 (min value → bottom) yScale(625); // 150 (midpoint → middle) // Use it anywhere you'd use a function: data.map(d => yScale(d.views)); // → array of pixel positions svg.selectAll('rect') .attr('y', d => yScale(d.views)); // D3 calls the function per element
Bonus features that come free:
yScale.invert(150) → 625 (pixel back to data value — useful for mouse position decoding)
yScale.ticks(5) → [0, 250, 500, 750, 1000, 1250] (nice tick values for axes)
yScale.nice() → rounds domain to friendly boundaries