
Level Up Your D3 Charts: Exploring d3-3d v2
Create lightweight 3D visuals in the browser without WebGL — learn how d3-3d brings 3D to D3 with TypeScript, projections, and fast SVG rendering.
Stefan Nieke
Why would you want 3D on the web without WebGL, cameras, or shaders? Why stick to SVG and math when engines like Three.js can render stunning 3D scenes?
That’s exactly why d3-3d exists.
I didn’t create this library to replace Three.js. I created it because I often needed lightweight 3D: avatars, isometric charts, or simple shapes with depth, rotation, and a sense of volume — without the heavy setup.
This article explains the thinking behind the library, focusing on simplicity, TypeScript-first design, and seamless integration with D3.
The problem I faced
True 3D requires a camera, lighting, shadows, and often textures. But many projects just need 3D-ish visuals:
- Simple 3D avatars
- Small charts with depth
- Animated shapes with volume
WebGL and Three.js felt heavy for these use cases. D3 is declarative and data-driven, but lives in 2D. So the question became:
Can we get the feeling of 3D while staying in D3’s mental model?
The core idea: projection instead of scene
d3-3d projects 3D coordinates into 2D space using math. There’s no camera, no perspective matrix, no WebGL. Just points, rotations, and projections. Simple, predictable, and fast.
This is what makes it different: it’s not trying to simulate reality; it’s giving you just enough 3D to feel spatial.
TypeScript-first
3D math can be tricky. Strong typing avoids subtle bugs and gives IDE autocomplete for points, shapes, and transformations. All core shapes are fully typed.
Example from the docs:
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 },
],
];
const renderer = triangles3D()
.scale(100)
.origin({ x: 480, y: 250 })
.rotateY(Math.PI / 4);
const transformedData = renderer.data(data3D);
Each transformed triangle includes rotated 3D coordinates, projected 2D coordinates, centroids, and counter-clockwise orientation detection. Perfect for SVG rendering.
Using custom data with TypeScript
import { cubes3D, sort, type Point3D } from "d3-3d";
interface Building {
lat: number;
lng: number;
height: number;
name: string;
color: string;
}
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[][] = [
/* ... */
];
const transformed = renderer.data(buildings);
const sorted = transformed.sort(sort);
// Render with D3
svg
.selectAll("path")
.data(sorted)
.join("path")
.attr("d", renderer.draw)
.attr("fill", (d) => d[0].color);
Full type safety, computed properties, and seamless D3 integration.
Conclusion
If you need lighting, textures, or complex cameras, Three.js or WebGL is better. But for fast, lightweight, D3-friendly 3D with TypeScript support, d3-3d hits the sweet spot.
It’s not a competitor to heavy 3D engines — it’s a pragmatic choice for everyday 3D in the browser.
Links
Beautiful Backgrounds: Animated Web Components for Stunning Pages
How do you make a website truly come alive without heavy frameworks? Beautiful Backgrounds is a TypeScript library that does exactly that: elegant, animated backgrounds as Web Components that are interactive, high-performance, and easy to integrate.
Software Craftsmanship is the real Agile
A senior perspective on how Software Craftsmanship makes Agile real — beyond frameworks and rituals.