npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

d3-ternary

v3.0.7

Published

Generate ternary plots

Downloads

396

Readme

D3 Ternary Plot

npm version

d3-ternary is a JavaScript library and D3.js module that makes it easy to create ternary plots, its API exposes configurable functions in the manner of other D3 modules.

Ternary plots are a type of triangular diagram that depict components proportions in three-component systems. Each point in the triangle corresponds to a unique composition of those three components.

Try d3-ternary your browser, view the introductory notebook on Observable and see the 'Ternary Plots' notebook collection for examples. Or make ternary plots in the browser on TernaryPlot.com which is built using d3-ternary.

Installing

If you use npm

npm install d3-ternary

You can also download the latest release on GitHub. For vanilla JS in modern browsers, import d3-ternary from Skypack:

<script type="module">
  import {
    barycentric,
    ternaryPlot,
  } from "https://cdn.skypack.dev/d3-ternary@2";

  const b = barycentric();
  const t = ternaryPlot(b);
</script>

API Reference

barycentric()

barycentric() <>

Constructs a new default ternary converter that converts ternary data to Cartesian coordinates. By default, it makes an equilateral triangle on the unit circle centered at the origin.

# barycentric(data) <>

Computes [x,y] coordinates from a ternary values (a single three-element array). Note that the [x, y] coordinates here are unscaled (radius of 1). All values are normalized by default.

# barycentric.invert(coordinates) <>

Computes ternary values from coordinates (a two-element array [x, y]). Note that the [x, y] coordinates here are unscaled i.e. a radius of 1.

# barycentric.a([a]) <>

If a is specified, sets the a-accessor to the specified function and returns this barycentric converter. If a is not specified, returns the current a-value accessor, which defaults to:

const a = (d) => d[0];

# barycentric.b([b]) <>

If b is specified, sets the b-accessor to the specified function and returns this barycentric converter. If b is not specified, returns the current b-value accessor, which defaults to:

const b = (d) => d[1];

# barycentric.c([c]) <>

If c is specified, sets the c-accessor to the specified function and returns this barycentric converter. If c is not specified, returns the current c-value accessor, which defaults to:

const c = (d) => d[2];

# barycentric.domains([domains]) <>

If domains is specified, sets the domains for each axis to the specified domains in order of [A, B, C] and returns this barycentric converter. Each domain should be a two-element array [min, max]. All domains must have equal lengths. This method allows you to create "partial" ternary plots that zoom in on a specific region of the full triangle. For example, setting domains to [[0.2, 0.4], [0.3, 0.5], [0.2, 0.4]] would show only the portion of the triangle where A is between 20-40%, B between 30-50%, and C between 20-40%.

If domains is not specified, returns the current domains for each axis.

// Create a zoomed ternary plot showing only values where
// each component is between 20% and 40%
barycentric.domains([
  [0.2, 0.4], // A axis
  [0.2, 0.4], // B axis
  [0.2, 0.4], // C axis
]);

# barycentric.scales() <>

Returns an array of the three d3.scaleLinear() scale functions used internally by the barycentric converter, in order of [A, B, C]. These scale functions map the input domains to normalized values between 0 and 1.

# barycentric.unscaled(data) <>

Similar to the standard conversion function, but bypasses the domain scaling. Takes a three-element array of ternary values and returns [x,y] coordinates on the unit circle. This is primarily used internally for plotting the triangle bounds and grid lines.

# barycentric.rotation([angle]) <>

If angle is specified, sets the rotation angle in degrees and returns this barycentric converter. If angle is not specified, returns the current rotation angle, which defaults to 0. Positive angles rotate clockwise.

ternaryPlot()

ternaryPlot(barycentric) <>

Constructs a new default ternary plot generator with the default options.

# ternaryPlot(data) <>

Computes [x, y] coordinates that are scaled by the plot radius from ternary data. Unlike the barycentric method, this method takes the plot radius into account.

# ternaryPlot.invert(coordinates) <>

Computes ternary values from [x, y] coordinates that are scaled by the radius. Unlike the barycentric.invert() method this method takes the plot radius into account. Note that for inverting mouse positions, the ternary plot should centered at the origin of the containing SVG element.

Configuration methods

# ternaryPlot.radius([radius]) <>

If radius is specified, sets the radius of the ternary plot to the specified number. If radius is not specified, returns the current radius, which defaults to 300 (px).

To set domains without these extra checks, use ternaryPlot.setDomains(domains).

Layout methods

# ternaryPlot.labels([labels]) <>

If labels is specified, sets the axis labels to the labels in order of [A, B, C] and returns the ternary plot. If labels is not specified, returns the current labels, which defaults to [[A, B, C]].

# ternaryPlot.labelAngles([angles]) <>

If angles is specified, sets the angles of the axis labels to the specified angles in order of [A, B, C] and returns the ternary plot. If angles is not specified, returns the current label angles, which defaults to [0, 60, -60]

# ternaryPlot.labelOffsets([offsets]) <>

If offsets is specified and is an array, sets the axis label offsets to the specified offsets in order of [A, B, C] and returns the ternary plot. If offsets is a number, sets all label offsets to that value. If offsets is not specified, returns the current label offsets, which defaults to [45, 45, 45] px.

# ternaryPlot.tickAngles([angles]) <>

If angles is specified, sets the angle of the ticks of each axis to the specified angles in order [A, B, C] and returns the ternary plot. If angles is not specified, returns the current tick angles, which defaults to [0, 60, -60].

# ternaryPlot.tickTextAnchors([textAnchors]) <>

If textAnchors is specified, sets the axis tick text-anchor to the specified text-anchors in order of [A, B, C] and returns the ternary plot. If textAnchors is not specified, returns the current tick text-anchors, which defaults to ["start", "start", "end"].

# ternaryPlot.tickSizes([sizes]) <>

If sizes is specified and is an array, sets the axis tick sizes to the specified tick sizes in order of [A, B, C] and returns the ternary plot. If sizes is a number, sets the tick sizes of all axes to sizes. If sizes is not specified, returns the current tick sizes, which defaults to [6, 6, 6] (px).

# ternaryPlot.tickFormat([format]) <>

If format is specified, sets the tick format. format can either be a format specifier string that is passed to d3.tickFormat(). To implement your own tick format function, pass a custom formatter function, for example const formatTick = (x) => String(x.toFixed(1)). If format is not specified, returns the current tick sizes, which defaults to "%", meaning ticks are formatted as percentages.

Plot Methods

# ternaryPlot.gridLines([count]) <>

Generates and returns an array of arrays containing grid line coordinates for each axis. If count is not specified, it defaults to 10. count can be a number or an array of numbers, one for each axis in order of [A, B, C]. Each array contains count elements of two-element arrays with the start- and end coordinates of the grid line.

Grid lines are generated using d3.scaleLinear.ticks(). The specified count is only a hint; the scale may return more or fewer values depending on the domain.

# ternaryPlot.ticks([count]) <>

Generates and returns an array of tick objects for each axis. If count is not specified, it defaults to 10. count can be a number or an array of numbers, one for each axis in order of [A, B, C].

Each tick object contains:

  • tick: The formatted tick text
  • position: An array of [x,y] coordinates
  • angle: The tick rotation angle
  • textAnchor: The SVG text-anchor value
  • size: The length of the tick line

Ticks are generated using d3.scaleLinear.ticks(). The specified count is only a hint; the scale may return more or fewer values depending on the domain.

# ternaryPlot.axisLabels([options]) <>

Generates and returns an array containing axis label objects. Each axis label object contains:

  • position: An array of [x,y] coordinates
  • angle: The rotation angle of the label
  • label: The axis label text

Takes an optional configuration object:

{
  center: false; // If true, places labels at center of axes instead of vertices
}

# ternaryPlot.triangle() <>

Returns an SVG path command for a the outer triangle. This is used for the bounds of the ternary plot and its clipPath.

Transform Functions

# domainsFromTransform(transform) <>

Converts a transform into domain ranges for the ternary plot axes. This can be used to handle zooming and panning using d3-zoom. The transform object contains

  • k: The zoom scale factor (1 = no zoom, >1 = zoomed in)
  • x: The x-translation
  • y: The y-translation

Returns an array of [start, end] domain ranges for axes A, B, and C. For example:

const transform = { k: 1.4285, x: -0.3711, y: -0.2142; }
const domains = domainsFromTransform(transform);
// Returns :
// [
//   [0, 0.7],
//   [0, 0.7],
//   [0.3, 1],
// ]

Throws an error if the transform would create invalid domains (outside the [0,1] range) or if trying to zoom out beyond the original triangle.

# transformFromDomains(domains) <>

The inverse of domainsFromTransform - converts domain ranges into a d3.zoom transform. This is useful when you want to programmatically set the zoom/pan state to focus on specific domain ranges.

Takes an array of [start, end] domain ranges for axes A, B, and C. Returns a transform object with:

  • k: The zoom scale factor
  • x: The x-translation (unscaled by radius)
  • y: The y-translation (unscaled by radius)

Example usage:

// Zoom in to show only values where each component is between 20-70%
const partialDomains = [
  [0, 0.7],
  [0, 0.7],
  [0.3, 1],
];

const b = barycentric().domains(partialDomains);

const { x, y, k } = transformFromDomains(b.domains());

// We need to sync d3-zoom with the tranform of the partial domains
const initialTransform = d3.zoomIdentity
  .translate(x * radius, y * radius)
  .scale(k);

chart.call(zoom).call(zoom.transform, initialTransform);

Note that the translations returned are unscaled by the plot radius - they should be scaled by the radius before being used with SVG transforms.

Rendering Examples

For detailed examples of how to render ternary plots:

Acknowledgments

Several projects have served as a starting point for this module.

All authors are thanked.