12 KiB
D3.js Scale Reference
Comprehensive guide to all d3 scale types with examples and use cases.
Continuous scales
Linear scale
Maps continuous input domain to continuous output range with linear interpolation.
const scale = d3.scaleLinear()
.domain([0, 100])
.range([0, 500]);
scale(50); // Returns 250
scale(0); // Returns 0
scale(100); // Returns 500
// Invert scale (get input from output)
scale.invert(250); // Returns 50
Use cases:
- Most common scale for quantitative data
- Axes, bar lengths, position encoding
- Temperature, prices, counts, measurements
Methods:
.domain([min, max])- Set input domain.range([min, max])- Set output range.invert(value)- Get domain value from range value.clamp(true)- Restrict output to range bounds.nice()- Extend domain to nice round values
Power scale
Maps continuous input to continuous output with exponential transformation.
const sqrtScale = d3.scalePow()
.exponent(0.5) // Square root
.domain([0, 100])
.range([0, 500]);
const squareScale = d3.scalePow()
.exponent(2) // Square
.domain([0, 100])
.range([0, 500]);
// Shorthand for square root
const sqrtScale2 = d3.scaleSqrt()
.domain([0, 100])
.range([0, 500]);
Use cases:
- Perceptual scaling (human perception is non-linear)
- Area encoding (use square root to map values to circle radii)
- Emphasising differences in small or large values
Logarithmic scale
Maps continuous input to continuous output with logarithmic transformation.
const logScale = d3.scaleLog()
.domain([1, 1000]) // Must be positive
.range([0, 500]);
logScale(1); // Returns 0
logScale(10); // Returns ~167
logScale(100); // Returns ~333
logScale(1000); // Returns 500
Use cases:
- Data spanning multiple orders of magnitude
- Population, GDP, wealth distributions
- Logarithmic axes
- Exponential growth visualisations
Important: Domain values must be strictly positive (>0).
Time scale
Specialised linear scale for temporal data.
const timeScale = d3.scaleTime()
.domain([new Date(2020, 0, 1), new Date(2024, 0, 1)])
.range([0, 800]);
timeScale(new Date(2022, 0, 1)); // Returns 400
// Invert to get date
timeScale.invert(400); // Returns Date object for mid-2022
Use cases:
- Time series visualisations
- Timeline axes
- Temporal animations
- Date-based interactions
Methods:
.nice()- Extend domain to nice time intervals.ticks(count)- Generate nicely-spaced tick values- All linear scale methods apply
Quantize scale
Maps continuous input to discrete output buckets.
const quantizeScale = d3.scaleQuantize()
.domain([0, 100])
.range(['low', 'medium', 'high']);
quantizeScale(25); // Returns 'low'
quantizeScale(50); // Returns 'medium'
quantizeScale(75); // Returns 'high'
// Get the threshold values
quantizeScale.thresholds(); // Returns [33.33, 66.67]
Use cases:
- Binning continuous data
- Heat map colours
- Risk categories (low/medium/high)
- Age groups, income brackets
Quantile scale
Maps continuous input to discrete output based on quantiles.
const quantileScale = d3.scaleQuantile()
.domain([3, 6, 7, 8, 8, 10, 13, 15, 16, 20, 24]) // Sample data
.range(['low', 'medium', 'high']);
quantileScale(8); // Returns based on quantile position
quantileScale.quantiles(); // Returns quantile thresholds
Use cases:
- Equal-size groups regardless of distribution
- Percentile-based categorisation
- Handling skewed distributions
Threshold scale
Maps continuous input to discrete output with custom thresholds.
const thresholdScale = d3.scaleThreshold()
.domain([0, 10, 20])
.range(['freezing', 'cold', 'warm', 'hot']);
thresholdScale(-5); // Returns 'freezing'
thresholdScale(5); // Returns 'cold'
thresholdScale(15); // Returns 'warm'
thresholdScale(25); // Returns 'hot'
Use cases:
- Custom breakpoints
- Grade boundaries (A, B, C, D, F)
- Temperature categories
- Air quality indices
Sequential scales
Sequential colour scale
Maps continuous input to continuous colour gradient.
const colourScale = d3.scaleSequential(d3.interpolateBlues)
.domain([0, 100]);
colourScale(0); // Returns lightest blue
colourScale(50); // Returns mid blue
colourScale(100); // Returns darkest blue
Available interpolators:
Single hue:
d3.interpolateBlues,d3.interpolateGreens,d3.interpolateRedsd3.interpolateOranges,d3.interpolatePurples,d3.interpolateGreys
Multi-hue:
d3.interpolateViridis,d3.interpolateInferno,d3.interpolateMagmad3.interpolatePlasma,d3.interpolateWarm,d3.interpolateCoold3.interpolateCubehelixDefault,d3.interpolateTurbo
Use cases:
- Heat maps, choropleth maps
- Continuous data visualisation
- Temperature, elevation, density
Diverging colour scale
Maps continuous input to diverging colour gradient with a midpoint.
const divergingScale = d3.scaleDiverging(d3.interpolateRdBu)
.domain([-10, 0, 10]);
divergingScale(-10); // Returns red
divergingScale(0); // Returns white/neutral
divergingScale(10); // Returns blue
Available interpolators:
d3.interpolateRdBu- Red to blued3.interpolateRdYlBu- Red, yellow, blued3.interpolateRdYlGn- Red, yellow, greend3.interpolatePiYG- Pink, yellow, greend3.interpolateBrBG- Brown, blue-greend3.interpolatePRGn- Purple, greend3.interpolatePuOr- Purple, oranged3.interpolateRdGy- Red, greyd3.interpolateSpectral- Rainbow spectrum
Use cases:
- Data with meaningful midpoint (zero, average, neutral)
- Positive/negative values
- Above/below comparisons
- Correlation matrices
Sequential quantile scale
Combines sequential colour with quantile mapping.
const sequentialQuantileScale = d3.scaleSequentialQuantile(d3.interpolateBlues)
.domain([3, 6, 7, 8, 8, 10, 13, 15, 16, 20, 24]);
// Maps based on quantile position
Use cases:
- Perceptually uniform binning
- Handling outliers
- Skewed distributions
Ordinal scales
Band scale
Maps discrete input to continuous bands (rectangles) with optional padding.
const bandScale = d3.scaleBand()
.domain(['A', 'B', 'C', 'D'])
.range([0, 400])
.padding(0.1);
bandScale('A'); // Returns start position (e.g., 0)
bandScale('B'); // Returns start position (e.g., 110)
bandScale.bandwidth(); // Returns width of each band (e.g., 95)
bandScale.step(); // Returns total step including padding
bandScale.paddingInner(); // Returns inner padding (between bands)
bandScale.paddingOuter(); // Returns outer padding (at edges)
Use cases:
- Bar charts (most common use case)
- Grouped elements
- Categorical axes
- Heat map cells
Padding options:
.padding(value)- Sets both inner and outer padding (0-1).paddingInner(value)- Padding between bands (0-1).paddingOuter(value)- Padding at edges (0-1).align(value)- Alignment of bands (0-1, default 0.5)
Point scale
Maps discrete input to continuous points (no width).
const pointScale = d3.scalePoint()
.domain(['A', 'B', 'C', 'D'])
.range([0, 400])
.padding(0.5);
pointScale('A'); // Returns position (e.g., 50)
pointScale('B'); // Returns position (e.g., 150)
pointScale('C'); // Returns position (e.g., 250)
pointScale('D'); // Returns position (e.g., 350)
pointScale.step(); // Returns distance between points
Use cases:
- Line chart categorical x-axis
- Scatter plot with categorical axis
- Node positions in network graphs
- Any point positioning for categories
Ordinal colour scale
Maps discrete input to discrete output (colours, shapes, etc.).
const colourScale = d3.scaleOrdinal(d3.schemeCategory10);
colourScale('apples'); // Returns first colour
colourScale('oranges'); // Returns second colour
colourScale('apples'); // Returns same first colour (consistent)
// Custom range
const customScale = d3.scaleOrdinal()
.domain(['cat1', 'cat2', 'cat3'])
.range(['#FF6B6B', '#4ECDC4', '#45B7D1']);
Built-in colour schemes:
Categorical:
d3.schemeCategory10- 10 coloursd3.schemeAccent- 8 coloursd3.schemeDark2- 8 coloursd3.schemePaired- 12 coloursd3.schemePastel1- 9 coloursd3.schemePastel2- 8 coloursd3.schemeSet1- 9 coloursd3.schemeSet2- 8 coloursd3.schemeSet3- 12 coloursd3.schemeTableau10- 10 colours
Use cases:
- Category colours
- Legend items
- Multi-series charts
- Network node types
Scale utilities
Nice domain
Extend domain to nice round values.
const scale = d3.scaleLinear()
.domain([0.201, 0.996])
.nice();
scale.domain(); // Returns [0.2, 1.0]
// With count (approximate tick count)
const scale2 = d3.scaleLinear()
.domain([0.201, 0.996])
.nice(5);
Clamping
Restrict output to range bounds.
const scale = d3.scaleLinear()
.domain([0, 100])
.range([0, 500])
.clamp(true);
scale(-10); // Returns 0 (clamped)
scale(150); // Returns 500 (clamped)
Copy scales
Create independent copies.
const scale1 = d3.scaleLinear()
.domain([0, 100])
.range([0, 500]);
const scale2 = scale1.copy();
// scale2 is independent of scale1
Tick generation
Generate nice tick values for axes.
const scale = d3.scaleLinear()
.domain([0, 100])
.range([0, 500]);
scale.ticks(10); // Generate ~10 ticks
scale.tickFormat(10); // Get format function for ticks
scale.tickFormat(10, ".2f"); // Custom format (2 decimal places)
// Time scale ticks
const timeScale = d3.scaleTime()
.domain([new Date(2020, 0, 1), new Date(2024, 0, 1)]);
timeScale.ticks(d3.timeYear); // Yearly ticks
timeScale.ticks(d3.timeMonth, 3); // Every 3 months
timeScale.tickFormat(5, "%Y-%m"); // Format as year-month
Colour spaces and interpolation
RGB interpolation
const scale = d3.scaleLinear()
.domain([0, 100])
.range(["blue", "red"]);
// Default: RGB interpolation
HSL interpolation
const scale = d3.scaleLinear()
.domain([0, 100])
.range(["blue", "red"])
.interpolate(d3.interpolateHsl);
// Smoother colour transitions
Lab interpolation
const scale = d3.scaleLinear()
.domain([0, 100])
.range(["blue", "red"])
.interpolate(d3.interpolateLab);
// Perceptually uniform
HCL interpolation
const scale = d3.scaleLinear()
.domain([0, 100])
.range(["blue", "red"])
.interpolate(d3.interpolateHcl);
// Perceptually uniform with hue
Common patterns
Diverging scale with custom midpoint
const scale = d3.scaleLinear()
.domain([min, midpoint, max])
.range(["red", "white", "blue"])
.interpolate(d3.interpolateHcl);
Multi-stop gradient scale
const scale = d3.scaleLinear()
.domain([0, 25, 50, 75, 100])
.range(["#d53e4f", "#fc8d59", "#fee08b", "#e6f598", "#66c2a5"]);
Radius scale for circles (perceptual)
const radiusScale = d3.scaleSqrt()
.domain([0, d3.max(data, d => d.value)])
.range([0, 50]);
// Use with circles
circle.attr("r", d => radiusScale(d.value));
Adaptive scale based on data range
function createAdaptiveScale(data) {
const extent = d3.extent(data);
const range = extent[1] - extent[0];
// Use log scale if data spans >2 orders of magnitude
if (extent[1] / extent[0] > 100) {
return d3.scaleLog()
.domain(extent)
.range([0, width]);
}
// Otherwise use linear
return d3.scaleLinear()
.domain(extent)
.range([0, width]);
}
Colour scale with explicit categories
const colourScale = d3.scaleOrdinal()
.domain(['Low Risk', 'Medium Risk', 'High Risk'])
.range(['#2ecc71', '#f39c12', '#e74c3c'])
.unknown('#95a5a6'); // Fallback for unknown values