D3 provides several scales in d3-scale: quantitative, ordered, and categorical. EplusM is a quantitative scale, an effective alternative to the logarithmic scale for values that span over multiple orders of magnitude.
See these publications for details:
- Katerina Batziakoudi, Stéphanie Rey, Jean-Daniel Fekete. Beyond Log Scales: Toward Cognitively Informed Bar Charts for Orders of Magnitude Values. IEEE Transactions on Visualization and Computer Graphics (TVCG). 2026 ⟨hal-05171203⟩
- Katerina Batziakoudi, Florent Cabric, Stéphanie Rey, and Jean-Daniel Fekete. 2025. Lost in Magnitudes: Exploring Visualization Designs for Large Value Ranges. In Proceedings of the 2025 CHI Conference on Human Factors in Computing Systems (CHI '25). Association for Computing Machinery, New York, NY, USA, Article 1170, 1–18. https://doi.org/10.1145/3706598.3713487
In JavaScript development, you can add the library to your package.json file with the following command:
npm i d3-eplusm
You can also use it directly from unpkg.com by adding this line to your HTML file (see examples/ticks-cdn.html):
<script type="module">
import * as d3 from 'https://cdn.jsdelivr.net/npm/d3@7/+esm';
import { scaleEplusM, bricksEplusM } from 'https://unpkg.com/d3-eplusm?module';
...
</scriptYou can also use d3-eplusm in Observable, by using scaleEplusM = require("d3-eplusm@0.0.7-dev") as shown is this example. Katerina Batziakoudi also hosts a nice demo at Observable.
d3-eplusm provides two API: the EplusM scale using the d3 scale API, and a Bricks API to visualize bar charts using the Bricks encoding.
Like all the quantitative scales of d3, d3-eplusm can be used like this:
const x1 = scaleEplusM()
.domain([1, 1e10])
.range([marginLeft, width - marginRight]);
It provides the usual methods, such as ticks() and nice().
In addition, you can also specify the number of tick values you want using the goodTicks() method.
It takes either a number of tick labels to show, between 1 and 9, or an array of tick units to show, such as goodTicks([1, 5]) (equivalent to goodTicks(2) or simply goodTicks
The library provides helper functions to display Bricks. An EplusM bar chart with Bricks looks like this:
It requires the toplevel function bricksEplusM:
import { bricksEplusM } from 'd3-eplusm';The library defines the following functions:
const bricks = bricksEplusM(brickColor)returns an object to manage the bricks.bricks.declarePatterns(svg)takes a D3 svg object and adds ten brick pattern declarations, called#brick0to#brick9.bricks.barY(scale, v)returns the bar chart Y value for a bar with valuev.bricks.barHeight(scale, v)returns the bar chart height value for a bar with valuev.bricks.brickY(scale, v)returns the bar chart Y value for the brick part of a bar with valuev.bricks.brickHeight(scale, v)returns the bar chart height value for the brick part of a bar with valuev.bricks.brickPattern(v)returns the brick part pattern id of a bar with valuev.
It is typically used like this (see example/bricks.html):
...
// Add a rect for each bar without the bricks.
svg
.append('g')
.attr('fill', barColor)
.selectAll()
.data(data)
.join('rect')
.attr('x', (d) => x(d.Letter))
.attr('y', (d) => bricks.barY(y, d.Amount))
.attr('height', (d) => bricks.barHeight(y, d.Amount))
.attr('width', x.bandwidth());
// Add the bricks
svg
.append('g')
.selectAll()
.data(data)
.join('rect')
.attr('fill', (d) => bricks.pattern(d.Amount))
.attr('x', (d) => x(d.Letter))
.attr('y', (d) => bricks.brickY(y, d.Amount))
.attr('height', (d) => bricks.brickHeight(y, d.Amount))
.attr('width', x.bandwidth());