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 🙏

© 2026 – Pkg Stats / Ryan Hefner

d3-3d

v2.0.2

Published

D3.js plugin for 3d visualization written in Typescript

Readme

d3-3d

d3-3d is meant for 3d visualizations. d3-3d allows the projection of 3d data onto the screen in the webbrowser. It is specially designed to work with d3.js.

Build Coverage npm npm npm npm bundle size npm TypeScript

See more examples

Features

  • First-class TypeScript support with full type definitions
  • Custom data accessor functions for any data format
  • Automatic centroid calculation for all shapes
  • Counter-clockwise orientation detection for polygons
  • Generic types for type-safe data transformations
  • Orthographic projection for SVG rendering
  • Sorting utilities for proper z-ordering (painter's algorithm)

Installing

If you use npm, npm install d3-3d. You can also download the latest release. Otherwise use unpkg to get the latest release. For example:

<script src="https://unpkg.com/d3-3d/build/d3-3d.js"></script>

For a specific version:

<script src="https://unpkg.com/d3-3d@version/build/d3-3d.js"></script>

TypeScript users: Type definitions are included automatically. No need to install @types/d3-3d.

Import

ES6 / TypeScript:

import {
  triangles3D,
  cubes3D,
  gridPlanes3D,
  points3D,
  lineStrips3D,
  polygons3D,
  planes3D,
  lines3D
} from 'd3-3d';

// Import utility function for sorting
import { sort } from 'd3-3d';

// Import TypeScript types
import type { Point3D, Point2D, TransformedPoint } from 'd3-3d';

API Reference

Note: All shapes (points3D, lines3D, lineStrips3D, triangles3D, planes3D, polygons3D, gridPlanes3D, cubes3D) share the same API. The methods below work for all shape types.

Core Methods:

  • .data() - transform and compute properties for your data.
  • .draw() - draw SVG path for a shape.

Configuration Methods:

Utility Functions:

  • sort() - utility function to sort shapes by depth.

Overview

d3-3d uses the browser's coordinate system and orthographic projection to display your data on the screen. It will calculate the centroid for all elements and the orientation for your polygons. Due to the fact that SVG isn't very 3d compatible d3-3d adds 3d transformations to SVG.

With d3-3d you can easily visualize your 3d data with full TypeScript support.

Basic Example:

import { triangles3D, sort } from 'd3-3d';

const data3D = [
  [
    { x: 0, y: -1, z: 0 },
    { x: -1, y: 1, z: 0 },
    { x: 1, y: 1, z: 0 }
  ]
];

// Create renderer with default Point3D type
const renderer = triangles3D()
  .scale(100)
  .origin({ x: 480, y: 250 })
  .rotateY(Math.PI / 4);

// Transform data - returns array with computed properties
const transformedData = renderer.data(data3D);

// Each transformed triangle includes:
// - rotated: { x, y, z } - rotated 3D coordinates
// - projected: { x, y } - 2D screen coordinates
// - centroid: { x, y, z } - geometric center
// - ccw: boolean - counter-clockwise orientation

// Render with D3
const svg = d3.select('svg');

svg
  .selectAll('path')
  .data(transformedData)
  .join('path')
  .attr('d', renderer.draw)
  .attr('fill', 'steelblue');

TypeScript with Custom Data Types:

import { cubes3D, sort, type Point3D } from 'd3-3d';

// Define your domain-specific data type
interface Building {
  lat: number;
  lng: number;
  height: number;
  name: string;
  color: string;
}

// Use generic type for full type safety
const renderer = cubes3D<Building>()
  .x((d) => d.lng)
  .y((d) => d.height)
  .z((d) => d.lat)
  .scale(50)
  .origin({ x: 400, y: 300 });

const buildings: Building[][] = [
  [
    { lat: 0, lng: 0, height: 10, name: 'Building A', color: '#ff6b6b' },
    { lat: 1, lng: 0, height: 15, name: 'Building B', color: '#4ecdc4' },
    { lat: 0.5, lng: 1, height: 20, name: 'Building C', color: '#45b7d1' }
  ]
];

const transformed = renderer.data(buildings);

// TypeScript knows about your custom properties!
transformed[0][0].name; // ✓ string
transformed[0][0].color; // ✓ string
transformed[0].centroid; // ✓ Point3D
transformed[0].ccw; // ✓ boolean

// Sort by depth for proper rendering (back-to-front)
const sorted = transformed.sort(sort);

svg
  .selectAll('path')
  .data(sorted)
  .join('path')
  .attr('d', renderer.draw)
  .attr('fill', (d) => d[0].color)
  .attr('stroke', 'black');

Shapes

All shapes share the same API for configuration, but differ in their input data format and output properties.

| Shape | SVG Element | Input Format | .draw() | ccw Property | | ---------------- | ----------- | -------------------------------------- | --------- | -------------- | | points3D | <circle> | Datum[] - Array of points | ❌ | ❌ | | lines3D | <line> | Datum[][] - Array of line pairs | ❌ | ❌ | | lineStrips3D | <path> | Datum[][] - Array of point arrays | ✅ | ❌ | | triangles3D | <path> | Datum[][] - Array of 3-point arrays | ✅ | ✅ | | planes3D | <path> | Datum[][] - Array of 4-point arrays | ✅ | ✅ | | polygons3D | <path> | Datum[][] - Array of N-point arrays | ✅ | ✅ | | gridPlanes3D | <path> | Datum[] - Grid of points* | ✅ | ✅ | | cubes3D | <path> | Datum[][] - Array of 8-vertex arrays | ✅ | ✅ (per face) |

Notes:

  • All shapes compute centroid, rotated, and projected properties
  • ccw (counter-clockwise) is computed for polygon-based shapes to detect front/back faces
  • Shapes without .draw() method (points3D, lines3D) can be rendered directly with SVG elements using the projected coordinates

Input Data Details:

  • points3D: Each point must have properties accessible via .x(), .y(), .z() accessors (default: {x, y, z})
  • lines3D: Each line is defined by exactly 2 points (start and end)
  • lineStrips3D: Each strip connects consecutive points in the array
  • triangles3D: Each triangle requires exactly 3 points in counter-clockwise order
  • planes3D: Each plane requires exactly 4 points in counter-clockwise order
  • polygons3D: Each polygon can have any number of points (≥3) in counter-clockwise order
  • gridPlanes3D: Input is a flat array of points that forms a grid. Important: You must specify the number of points per row using .rows() so the library can correctly reconstruct the faces. All rows must have the same length.
  • cubes3D: Each cube requires exactly 8 vertices ordered as shown below:

cube

API Methods

.data(data)

Transforms the input data by applying rotation, projection, and computing additional properties.

Available on: All shapes

Parameters:

  • data: Datum[][] - Array of shapes, where each shape is an array of data points

Returns: Triangle<Datum>[] (or Polygon<Datum>[], Plane<Datum>[], etc. depending on the shape)

Transformed shapes with the following properties:

  • Original data preserved - All properties from your input data are preserved
  • rotated: Point3D - Rotated 3D coordinates for each point
  • projected: Point2D - 2D screen coordinates for each point
  • centroid: Point3D - Computed geometric center of the shape
  • ccw: boolean - Whether the shape is counter-clockwise oriented (polygons only)

Example:

const renderer = triangles3D()
  .scale(100)
  .rotateY(Math.PI / 4);

const data = [
  [
    { x: 0, y: 0, z: 0 },
    { x: 1, y: 0, z: 0 },
    { x: 0, y: 1, z: 0 }
  ]
];

const result = renderer.data(data);

// Access computed properties
console.log(result[0].centroid); // { x: 0.33, y: 0.33, z: 0 }
console.log(result[0].ccw); // true
console.log(result[0][0].rotated); // { x: ..., y: ..., z: ... }
console.log(result[0][0].projected); // { x: ..., y: ... }

TypeScript with custom data:

interface CustomPoint {
  x: number;
  y: number;
  z: number;
  id: string;
  value: number;
}

const renderer = triangles3D<CustomPoint>()
  .x((d) => d.x)
  .y((d) => d.y)
  .z((d) => d.z);

const data: CustomPoint[][] = [...];
const result = renderer.data(data);

// Original properties are preserved
result[0][0].id;     // ✓ string
result[0][0].value;  // ✓ number

// Computed properties are added
result[0].centroid;  // ✓ Point3D
result[0].ccw;       // ✓ boolean

.draw()

Constructs an SVG <path> element string based on the transformed shape data.

Available on: lineStrips3D, triangles3D, planes3D, polygons3D, gridPlanes3D, cubes3D

Parameters:

  • shape: TransformedPoint<Datum>[] - A single transformed shape (from .data() result)

Returns: string - SVG path string (e.g., "M0,0L1,0L0.5,1Z")

Example:

const renderer = triangles3D();
const transformed = renderer.data(myData);

// Draw single shape
const pathString = renderer.draw(transformed[0]);

// Use with D3
svg.selectAll('path').data(transformed).join('path').attr('d', renderer.draw);

.x(x)

If x is specified, sets the x accessor to the specified function or number and returns the shape instance for chaining. If x is not specified, returns the current x accessor, which defaults to:

Available on: All shapes

function x(p) {
  return p.x;
}

This function will be invoked for each point in the input data array.

.y(y)

If y is specified, sets the y accessor to the specified function or number and returns the shape instance for chaining. If y is not specified, returns the current y accessor, which defaults to:

Available on: All shapes

function y(p) {
  return p.y;
}

This function will be invoked for each point in the input data array.

.z(z)

If z is specified, sets the z accessor to the specified function or number and returns the shape instance for chaining. If z is not specified, returns the current z accessor, which defaults to:

Available on: All shapes

function z(p) {
  return p.z;
}

This function will be invoked for each point in the input data array.

.scale(scale)

If scale is specified, sets the scale to the specified number and returns the shape instance for chaining. If scale is not specified, returns the current scale.

Available on: All shapes

Default: 1

.rotateX(angleX)

If angleX is specified, sets angleX to the specified number (in radians) and returns the shape instance for chaining. If angleX is not specified, returns the current angleX.

Available on: All shapes

Default: 0

angleX should be expressed in radians, for example: Math.PI / 4.

.rotateY(angleY)

If angleY is specified, sets angleY to the specified number (in radians) and returns the shape instance for chaining. If angleY is not specified, returns the current angleY.

Available on: All shapes

Default: 0

angleY should be expressed in radians, for example: Math.PI / 4.

.rotateZ(angleZ)

If angleZ is specified, sets angleZ to the specified number (in radians) and returns the shape instance for chaining. If angleZ is not specified, returns the current angleZ.

Available on: All shapes

Default: 0

angleZ should be expressed in radians, for example: Math.PI / 4.

.rotationCenter(point)

Sets the center point around which rotations are performed. This is different from .origin() which controls the 2D rendering position on the screen.

Available on: All shapes

Parameters:

  • point?: Point3D - The 3D point to rotate around

Returns:

  • If called without arguments: current Point3D value
  • If called with arguments: this (for chaining)

Default: { x: 0, y: 0, z: 0 }

Example:

const renderer = triangles3D()
  .rotationCenter({ x: 50, y: 50, z: 0 }) // Rotate around point (50,50,0)
  .rotateY(Math.PI / 2);

// The rotation will pivot around (50,50,0) instead of (0,0,0)

.origin(origin)

If origin is specified, sets the 2D rendering origin to the specified point and returns the shape instance for chaining. If origin is not specified, returns the current origin.

Available on: All shapes

Default: { x: 0, y: 0 }

.rows(rows)

Sets the number of points per row (columns) for gridPlanes3D. Since a grid is passed as a flat array, the library needs to know how many points constitute one horizontal line to correctly create the rectangular faces.

Available on: gridPlanes3D

Parameters:

  • rows?: number - Number of points per row

Returns:

  • If called without arguments: current number
  • If called with arguments: this (for chaining)

Default: 1

Example:

const points = [
  { x: 0, y: 0, z: 0 },
  { x: 1, y: 0, z: 0 },
  { x: 2, y: 0, z: 0 }, // Row 0
  { x: 0, y: 0, z: 1 },
  { x: 1, y: 0, z: 1 },
  { x: 2, y: 0, z: 1 } // Row 1
];

const grid = gridPlanes3D()
  .rows(3) // 3 points per row
  .data(points);

Utility Functions

sort()

A comparator function for sorting 3D shapes by their centroid's z-coordinate. Use this with JavaScript's .sort() to render shapes in correct depth order (painter's algorithm).

Usage:

import { triangles3D, sort } from 'd3-3d';

const renderer = triangles3D();
const transformed = renderer.data(data);

// Sort back-to-front for correct rendering
const sorted = transformed.sort(sort);

svg.selectAll('path').data(sorted).join('path').attr('d', renderer.draw);

Type Signature:

function sort<T extends HasCentroid>(a: T, b: T): number;

interface HasCentroid {
  centroid: { z: number };
}

Computed Properties

All shape transformations automatically compute additional properties on the returned data:

centroid

The geometric center of the shape in 3D space (after rotation). Available on all shapes.

const data = triangles3D().data(myTriangles);
console.log(data[0].centroid); // { x: 1.5, y: 2.0, z: 0.5 }

Use this for:

  • Sorting shapes by depth
  • Calculating bounding boxes
  • Finding center points for labels or interaction

ccw (Counter-Clockwise)

Boolean indicating whether the polygon is oriented counter-clockwise when viewed from the camera. Useful for backface culling. Available on: triangles3D, polygons3D, planes3D, cubes3D (per face).

const data = triangles3D().data(myTriangles);
if (data[0].ccw) {
  // Front-facing triangle - render normally
} else {
  // Back-facing triangle - optionally skip or render differently
}

Algorithm: Uses the shoelace formula on the rotated 2D projection to determine orientation.

rotated

3D coordinates after rotation has been applied. Available on each individual point.

const data = points3D().data(myPoints);
console.log(data[0].rotated); // { x: number, y: number, z: number }

projected

2D screen coordinates after orthographic projection. Available on each individual point.

const data = points3D().data(myPoints);
console.log(data[0].projected); // { x: number, y: number }

Migration Guide

Upgrading from v0.x to v1.0

The API has been modernized with a new .data() method pattern for better TypeScript support and clarity.

Before (v0.x):

const triangles = triangles3D();
const result = triangles(data); // Called as function

After (v1.0+):

const renderer = triangles3D();
const result = renderer.data(data); // Explicit .data() method

Breaking Changes:

  1. ❌ Direct function invocation removed: renderer(data) no longer works
  2. ✅ Use .data() method instead: renderer.data(data)
  3. ✅ Full TypeScript generics support added
  4. ✅ Computed properties (centroid, ccw) now included automatically

Benefits:

  • Better IDE autocomplete and type inference
  • Explicit API that's easier to understand
  • No confusion between configuration and data transformation
  • Full type safety with custom data structures

Migration Example:

- const triangles = triangles3D().scale(100);
- const result = triangles(data);
+ const renderer = triangles3D().scale(100);
+ const result = renderer.data(data);

  svg.selectAll('path')
    .data(result)
    .join('path')
    .attr('d', triangles.draw);

Star History

Star History Chart