D3.js SVG Explained — Shapes, Paths & Transformations Step-by-Step
D3.js visualizations are built on SVG (Scalable Vector Graphics). Understanding SVG elements and how D3 generates them is essential for creating custom charts.
What You’ll Learn
By the end of this tutorial, you will be able to:
- Understand the SVG coordinate system and how it differs from math graphs
- Draw basic SVG shapes: circles, rectangles, lines, and ellipses
- Use SVG path commands (M, L, C, Q, Z) to draw custom shapes
- Leverage D3 path generators for lines, areas, arcs, and symbols
- Group elements with
<g>and apply transformations - Build data-driven SVG visualizations
Why SVG Matters for D3.js
SVG is the canvas D3.js paints on. When Doda Browser renders its memory usage timeline or Durga Antivirus Pro displays real-time threat detection charts, they’re using SVG elements created by D3.js. Unlike raster graphics (PNG, JPEG), SVG is resolution-independent — charts look crisp on any screen — and each element is a DOM node that can be styled, animated, and made interactive. Understanding SVG is understanding the foundation of every D3.js visualization.
Learning Path
flowchart LR
A["D3.js Basics"] --> B["D3.js SVG"]
B --> C["D3.js Scales & Axes"]
C --> D["D3.js Charts"]
D --> E["D3.js Interactions"]
D --> F["D3.js Data Handling"]
E --> G["D3.js Advanced"]
F --> G
G --> H["D3.js Reference"]
B:::current
classDef current fill:#4CAF50,color:#fff,stroke:#333,stroke-width:2px
The SVG Coordinate System — The Most Important Thing to Understand
Before drawing anything, you need to understand how SVG measures positions. This trips up almost every beginner.
(0,0) ──────────→ x (increases right)
│
│ ┌──────────────┐
│ │ (x, y) │
│ │ width │
│ │ height │
│ └──────────────┘
▼
y (increases DOWN)In math class: y increases upward. A value of 100 is “high up” on a graph.
In SVG: y increases downward. A value of 0 is at the top, 100 is further down.
This inversion is the #1 cause of confusion for beginners. When your bar chart appears upside down, this is why. You’ll need to invert the y-axis when drawing charts: y = chartHeight - value.
Creating the SVG Canvas
// Create an SVG canvas on the page
var svg = d3.select("body")
.append("svg")
.attr("width", 400)
.attr("height", 300)
.style("border", "1px solid #ccc");What this does: It selects <body>, appends an <svg> element 400×300 pixels, and gives it a light gray border so you can see the canvas boundaries. The width and height attributes define the viewable area.
Basic SVG Shapes
Think of SVG shapes as stamps — each shape is a DOM element with position and size attributes.
Circle
svg.append("circle")
.attr("cx", 100) // center x position
.attr("cy", 100) // center y position
.attr("r", 50) // radius in pixels
.attr("fill", "steelblue")
.attr("stroke", "black")
.attr("stroke-width", 2);Why each attribute?:
cx,cy— where the circle’s center sits on the canvasr— how big the circle is (distance from center to edge)fill— inside color; without this, the circle is invisiblestroke— outline color; optional but useful for definition
Rectangle
svg.append("rect")
.attr("x", 50) // top-left corner x
.attr("y", 50) // top-left corner y
.attr("width", 100)
.attr("height", 80)
.attr("rx", 5) // rounded corner radius x
.attr("ry", 5) // rounded corner radius y
.attr("fill", "tomato");Why position matters: x and y define the top-left corner, not the center. If you want a rectangle centered at (100, 100), subtract half the width from x and half the height from y.
Line
svg.append("line")
.attr("x1", 0) // start point x
.attr("y1", 0) // start point y
.attr("x2", 200) // end point x
.attr("y2", 100) // end point y
.attr("stroke", "green")
.attr("stroke-width", 3);Key insight: Lines don’t have a fill attribute — they only use stroke. Without setting stroke, the line won’t be visible.
Ellipse
svg.append("ellipse")
.attr("cx", 200) // center x
.attr("cy", 100) // center y
.attr("rx", 80) // x-radius (horizontal stretch)
.attr("ry", 50) // y-radius (vertical stretch)
.attr("fill", "purple");Think of an ellipse as a stretched circle. A circle with different rx and ry becomes an ellipse. When rx === ry, it’s a circle.
SVG Paths — The Power Tool
Paths are the most flexible SVG element. A single path can draw lines, curves, arcs, and complex shapes. The d attribute contains the drawing instructions.
Path Commands
| Command | Name | What It Does | Example |
|---|---|---|---|
| M | Move to | Lifts the pen, moves to (x, y) | M 100 50 |
| L | Line to | Draws a line to (x, y) | L 50 150 |
| H | Horizontal line | Draws horizontal line to x | H 200 |
| V | Vertical line | Draws vertical line to y | V 100 |
| C | Cubic Bezier | Curved line with 2 control points | C cx1 cy1, cx2 cy2, x y |
| Q | Quadratic Bezier | Curved line with 1 control point | Q cx cy, x y |
| A | Arc | Circular arc segment | A rx ry rot large sweep x y |
| Z | Close path | Draws straight line back to start | Z |
Drawing a Triangle with Paths
// A triangle: start at top, go to bottom-left, then bottom-right, close
svg.append("path")
.attr("d", "M 100 50 L 50 150 L 150 150 Z")
.attr("fill", "gold")
.attr("stroke", "orange");Reading the path command:
M 100 50— Move the pen to (100, 50) without drawing (this is the top point)L 50 150— Draw a line from (100, 50) to (50, 150) (bottom-left)L 150 150— Draw a line from (50, 150) to (150, 150) (bottom-right)Z— Close the shape: draw a line from (150, 150) back to (100, 50)
Quadratic Curve
svg.append("path")
.attr("d", "M 50 200 Q 100 50 200 150")
.attr("fill", "none")
.attr("stroke", "teal")
.attr("stroke-width", 2);Reading the curve: Start at (50, 200), draw a quadratic curve controlled by point (100, 50) ending at (200, 150). The control point acts like a magnet pulling the curve toward it.
D3 Path Generators — Writing Paths for You
Writing path strings by hand is error-prone. D3 provides generator functions that create path strings from data.
Line Generator
var data = [
{ x: 0, y: 50 },
{ x: 100, y: 80 },
{ x: 200, y: 30 },
{ x: 300, y: 120 },
{ x: 400, y: 60 }
];
// Create a line generator — it's a function that converts data to a path string
var line = d3.line()
.x(function(d) { return d.x; }) // how to get x from each data point
.y(function(d) { return d.y; }); // how to get y from each data point
// Bind the data and draw the path
svg.append("path")
.datum(data) // bind one array of data
.attr("d", line) // the generator creates the d string
.attr("fill", "none")
.attr("stroke", "steelblue")
.attr("stroke-width", 2);Why datum() instead of data()? A line chart uses one array of points to create one path element. .data() is for binding an array to multiple elements. .datum() binds a single value (the whole array) to one element.
Area Generator
var area = d3.area()
.x(function(d) { return d.x; })
.y0(200) // baseline — where the area starts
.y1(function(d) { return d.y; }); // top line
svg.append("path")
.datum(data)
.attr("d", area)
.attr("fill", "steelblue")
.attr("opacity", 0.3);An area chart is like a line chart with the space below filled in. y0 is the bottom boundary, y1 is the top boundary (the line itself).
Arc Generator (for Pie/Donut Charts)
var arc = d3.arc()
.innerRadius(50) // 0 = pie, >0 = donut
.outerRadius(100) // outer edge
.startAngle(0) // where to start (in radians)
.endAngle(Math.PI / 2); // where to end (90 degrees)
svg.append("path")
.attr("d", arc)
.attr("transform", "translate(200, 150)")
.attr("fill", "coral");Radians vs degrees: SVG uses radians, not degrees. Full circle = 2π radians. π/2 = 90°, π = 180°, 2π = 360°.
Symbol Generator (for Markers)
var symbols = [
d3.symbolCircle, d3.symbolCross, d3.symbolDiamond,
d3.symbolSquare, d3.symbolStar, d3.symbolTriangle, d3.symbolWye
];
svg.selectAll("path")
.data(symbols)
.enter()
.append("path")
.attr("d", d3.symbol().type(function(d) { return d; }).size(200))
.attr("transform", function(d, i) { return "translate(" + (50 + i * 50) + ", 50)"; })
.attr("fill", "steelblue");Useful for scatter plot markers — each symbol gives a different visual representation for data points.
SVG Groups (<g>)
Think of a <g> like a container or folder for SVG elements. Attributes applied to the group affect all children.
var g = svg.append("g")
.attr("transform", "translate(100, 50)");
g.append("circle").attr("r", 30).attr("fill", "red");
g.append("circle").attr("r", 20).attr("fill", "white");
g.append("circle").attr("r", 10).attr("fill", "red");Why groups matter: Without the group, you’d need to add cx and cy to every circle. With the group translated by (100, 50), all three circles are offset by that amount. This is how you build complex charts — each component (bars, axes, legend) lives in its own group.
Transformations
Transformations change how elements are positioned, rotated, or scaled. They act like instructions on how to interpret the coordinates, not changes to the coordinates themselves.
Translate (Move)
.attr("transform", "translate(x, y)")
// Dynamic with data
.attr("transform", function(d) {
return "translate(" + d.x + ", " + d.y + ")";
});Rotate
// Rotate around (0,0)
.attr("transform", "rotate(45)")
// Rotate around a specific center point
.attr("transform", "rotate(45, 100, 100)")Scale
// Uniform scale
.attr("transform", "scale(1.5)")
// Non-uniform (stretch)
.attr("transform", "scale(2, 0.5)")
// Combined — apply in order!
.attr("transform", "translate(50, 50) rotate(45) scale(1.5)")Order matters: Transformations are applied right-to-left. translate(50, 50) rotate(45) means: first rotate around (0,0), then move the result. This is different from rotate(45) translate(50, 50) which means: first move, then rotate.
Data-Driven SVG — Putting It Together
<!DOCTYPE html>
<html>
<head>
<title>Data-Driven SVG</title>
</head>
<body>
<svg width="400" height="200" id="chart"></svg>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
var data = [
{ shape: "circle", x: 60, y: 100, size: 40, color: "steelblue" },
{ shape: "rect", x: 160, y: 60, size: 40, color: "tomato" },
{ shape: "circle", x: 260, y: 100, size: 30, color: "green" },
{ shape: "rect", x: 340, y: 80, size: 25, color: "purple" }
];
var svg = d3.select("#chart");
// Draw circles from the data
svg.selectAll("circle")
.data(data.filter(function(d) { return d.shape === "circle"; }))
.enter()
.append("circle")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", function(d) { return d.size; })
.attr("fill", function(d) { return d.color; });
// Draw rectangles from the data
svg.selectAll("rect.custom")
.data(data.filter(function(d) { return d.shape === "rect"; }))
.enter()
.append("rect")
.attr("class", "custom")
.attr("x", function(d) { return d.x - d.size / 2; })
.attr("y", function(d) { return d.y - d.size / 2; })
.attr("width", function(d) { return d.size; })
.attr("height", function(d) { return d.size; })
.attr("fill", function(d) { return d.color; });
</script>
</body>
</html>What’s happening: We filter the data by shape type, draw circles for circle entries and rectangles for rect entries. This pattern — filtering data and drawing different shape types — is how you build scatter plots with different markers.
Common Mistakes
1. Thinking SVG y-Coordinates Increase Upward
SVG y increases downward. A bar chart drawn with data values directly as y positions will appear upside down. Always invert: y = chartHeight - value.
2. Forgetting to Set fill or stroke
// WRONG — invisible shape
svg.append("circle").attr("r", 20);
// RIGHT — visible
svg.append("circle").attr("r", 20).attr("fill", "steelblue");3. Using .data() Instead of .datum() for Path Generators
// WRONG — creates multiple paths, each with one point
svg.selectAll("path").data([points]).enter().append("path").attr("d", line);
// RIGHT — creates one path with all points
svg.datum(points).append("path").attr("d", line);4. Not Accounting for Group Transforms
When a <g> is translated by (100, 50), child coordinates are relative to the group. A circle at cx=0, cy=0 inside the group appears at (100, 50) on the SVG canvas. This is intentional but confusing if you’re calculating absolute positions.
5. Using CSS Pixels for SVG Attributes
SVG attributes like cx, cy, r, width, height are unitless (default to pixels). CSS properties like font-size need units:
// WRONG — SVG attribute with px suffix
.attr("font-size", "14px") // doesn't work
// RIGHT
.style("font-size", "14px")
.attr("font-size", 14) // also works (SVG treats as pixels)
6. Path Commands Without Space Separators
Some path commands work without spaces (M100 50L50 150), but always use spaces for readability and to avoid parsing bugs: M 100 50 L 50 150.
Practice Questions
Question 1
Why does y increase downward in SVG instead of upward like in math?
Answer: SVG was designed for document layout, not data visualization. The coordinate system mimics how you read a page — top to bottom. When building charts, you must invert the y-axis: y = chartHeight - value.
Question 2
What is the difference between a <g> element and a <div> in HTML?
Answer: <g> is a group container in SVG — it doesn’t have a visual appearance itself, but attributes and transforms applied to it affect all children. It’s the SVG equivalent of a <div>, but for vector graphics.
Question 3
When would you use d3.line() vs d3.area()?
Answer: Use d3.line() when you want to show a trend (like a stock price over time). Use d3.area() when you want to emphasize magnitude (like the volume of sales, where the filled area shows quantity).
Question 4
What does the path command Z do?
Answer: Z (close path) draws a straight line from the current position back to the starting point of the path. It’s how you close shapes like triangles and polygons.
Question 5
How do you create a donut chart instead of a pie chart?
Answer: Set innerRadius to a value greater than 0 in d3.arc(). For example, innerRadius(60) creates a donut with a 60px hole, while innerRadius(0) creates a solid pie.
Challenge
Create an SVG visualization that draws 10 circles in a row, each with a progressively larger radius and a different color. Use a data array and D3’s data join. The circles should be evenly spaced and the colors should transition from light to dark blue.
FAQ
Try It Yourself
Here’s a complete interactive SVG shape explorer. Add shapes to the canvas with random positions and colors:
<!DOCTYPE html>
<html>
<head>
<title>SVG Shape Explorer — Try It Yourself</title>
<style>
body { font-family: sans-serif; padding: 20px; }
#canvas { border: 1px solid #ccc; margin: 10px 0; }
.controls { margin: 10px 0; }
select, button, input { padding: 5px 10px; margin: 0 5px; cursor: pointer; }
</style>
</head>
<body>
<h2>SVG Shape Explorer</h2>
<div class="controls">
<select id="shapeSelect">
<option value="circle">Circle</option>
<option value="rect">Rectangle</option>
<option value="ellipse">Ellipse</option>
<option value="line">Line</option>
<option value="triangle">Triangle (Path)</option>
<option value="star">Star (Symbol)</option>
</select>
<input type="color" id="colorPicker" value="#4CAF50">
<button onclick="addShape()">Add Shape</button>
<button onclick="clearShapes()">Clear All</button>
</div>
<svg width="400" height="300" id="canvas"></svg>
<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
var svg = d3.select("#canvas");
function addShape() {
var type = document.getElementById("shapeSelect").value;
var color = document.getElementById("colorPicker").value;
var x = Math.random() * 350 + 25;
var y = Math.random() * 250 + 25;
switch (type) {
case "circle":
svg.append("circle")
.attr("cx", x).attr("cy", y)
.attr("r", 20).attr("fill", color);
break;
case "rect":
svg.append("rect")
.attr("x", x - 20).attr("y", y - 15)
.attr("width", 40).attr("height", 30)
.attr("rx", 3).attr("fill", color);
break;
case "ellipse":
svg.append("ellipse")
.attr("cx", x).attr("cy", y)
.attr("rx", 30).attr("ry", 20).attr("fill", color);
break;
case "line":
svg.append("line")
.attr("x1", x - 20).attr("y1", y)
.attr("x2", x + 20).attr("y2", y)
.attr("stroke", color).attr("stroke-width", 4);
break;
case "triangle":
svg.append("polygon")
.attr("points", (x)+","+(y-20)+" "+(x-20)+","+(y+15)+" "+(x+20)+","+(y+15))
.attr("fill", color);
break;
case "star":
var starPath = d3.symbol().type(d3.symbolStar).size(800)();
svg.append("path")
.attr("d", starPath)
.attr("transform", "translate("+x+","+y+")")
.attr("fill", color);
break;
}
}
function clearShapes() {
svg.selectAll("*").remove();
}
</script>
</body>
</html>Try this: Select different shape types, pick a color, and click “Add Shape” to place shapes randomly. Notice how each shape uses different SVG attributes.
What’s Next
Now that you understand SVG, you’re ready to map data to pixels:
| Tutorial | What You’ll Learn |
|---|---|
| D3.js Scales & Axes | Map data values to pixel coordinates |
| D3.js Charts | Build complete bar, pie, and line charts |
| D3.js Interactions | Add tooltips, drag, and zoom |
Related topics: SVG, CSS, JavaScript, HTML, Data Visualization
Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro — bringing secure, high-performance software to your digital life.
What’s Next
Congratulations on completing this D3Js Svg tutorial! Here’s where to go from here:
- Practice daily — Consistency is more important than long study sessions
- Build a project — Apply what you learned by building something real
- Explore related topics — Check out other tutorials in the same category
- Join the community — Discuss with other learners and share your progress
Remember: every expert was once a beginner. Keep coding!
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro