I’m in the process of creating the front-end of an app in React, which includes a product distribution pie chart. I wanted to create the whole thing in SVG – without jQuery or any other monstrous library. In this article, I’ll show you how to construct an SVG pie chart with Javascript that clocks in around 2KB. Beware: math is involved.

If you’re into Javascript- and you’d rather read the code than the whole explanation – feel free to take a look at the pastebin demo I’ve put together.

## Drawing An SVG Pie Chart – The Plan

A pie chart consists of one or more **circle sectors**. A circle sector is the portion of a disk enclosed by two radii and an arc. In other words: it’s a triangle with an arc instead of a straight line for a side.

The algorithm to draw a circle sector with SVG is the following. Start out with a square canvas. Imagine a circle that fills the whole canvas. The diameter is equal to the width of the canvas, its radius is half that value.

- Go to the center of the circle
- Draw a line straight up to the edge of the circle
- Draw an arc to some other point along the perimeter of the circle
- Draw a straight line back to the center of the circle

Most of this is trivial to calculate. The only tricky part is figuring out the end coordinates of the arc. Let’s say our SVG graphic is 200px wide and 200px high. Our circle fills the available space making the diameter 200px, and the radius is 100px (always half the diameter).

Whatever sector we decide to draw, the middle coordinates will always be `100,100`

– the center of the circle. Next, we draw a line straight up to the perimeter. This is always the same as well: `100,0`

. Our third coordinate is the one we need to calculate (see below) – the end point of the arc. The fourth point is middle of the circle again: `100,100`

.

The end point of the arc is a factor of two elements: the radius of our circle and the angle that the two radii create. The angle dictates the size of our sector. If we’d like to create a sector representing 25% of the whole the angle will be 20% of 360° which is 72°.

## The Math Behind Circle Sectors

I think it’s best to understand the underlying math because it will allow us to make better charts. Let’s draw up a sector with an added triangle and mark some points of interest.

The sector is a part of a circle with a radius of 115. Therefore, we know that the two straight sides of the sector are both 115 long. I’ve exaggerated the arc here to make sure we can see what’s what. We’re creating a sector that represents 12.5% of the whole which translates to a 45° angle (0.125 × 360°)

We also know some of the coordinates we’ll need. The center of the circle is at `115,115`

and the top of the segment is at `115,0`

. The only thing we’ll need is the unknown set of coordinates marked with a captial `X`

and `Y`

. To get them we’ll need to calculate the length of side `z`

, `x`

and `y`

(marked in blue). Let’s bust out all that trigonometry we learned in high school, three rules in particular:

- The cosine rule
`b`

^{2}= a^{2}+ b^{2}– 2ac cos(θ) - The sine rule simplified for right angle triangles
`sin(θ) = opposite/hypotenuse`

- Pythagoras’ theorem
`a`

^{2}+ b^{2}= c^{2}

Based on the cosine rule we can easily calculate the length of side z.

`// Calculate side z with the cosine rule`

z^{2} = 115^{2} + 115^{2} – 2×115×115 cos(45)

z = 88.0172

// Calculate side x using the sine rule

sin(45) = x/115

x = 81,3173

// Calculate side y using Mr. Pythagoras’ theorem

81,3173^{2} + y^{2} = 88.0172^{2}

y = 33.6827

We now have sides `x`

and `y`

which is great, because we can calculate the final coordinates with their help. The first coordinate of the arc’s end point (`X`

) will be `115 + x`

because we need to move on the horizontal axis away from the center of the circle by `x`

. The center of the circle is at 115, so we add `x`

to this value to arrive at `196.317`

.

We need to move down `y`

from the top of our circle which is at 0, so our `Y`

coordinate will be `y`

itself: `33.6827`

.

## Circle Sector Variations

There are four types of sectors that can occur, depending on their angles. Take a look at the diagram below for a visual representation of each.

It is important to distinguish between them because calculations differ slightly, and we need to move along the X axis differently for each. Note that there are numerous ways you could arrive at the end coordinates – you could draw different triangles than I have. I found this to be the easiest method since the value of the side `y`

is always the amount we need to move on the axis, no need to add a length to it.

In all these cases we use Pythagoras’ theorem to arrive at `y`

, but the we get to `x`

and `z`

differently, let’s see how:

- When the angle is
**acute**(less than 90°) we find`x`

from the angle of the sector. - When the angle is
**obtuse**(more than 90°) we find`x`

from the complement angle of the sector (180° – angle) - When the angle is a
**reflex angle and smaller than 270° we use the**`angle - 180°`

to find`x`

. We use`360° - angle`

to calculate`z`

. - When the angle is a
**reflex angle and larger than 270° we use the**`360° - angle`

to find`x`

. We use`360° - angle`

to calculate`z`

.

Surely there must be a way to simplify! Yes, there is! Let’s take all sectors above 180° (bottom two on the image above) and mirror them along the center line. This way the bottom left sector will transform into the top left sector, and the bottom right sector will transform into the top right sector. The `Y`

value will always be the same; we just need to remember that if the original angle was above 180° we need to subtract `x`

from the radius if it was below we need to add it to the radius.

## Creating The Segment In SVG

Now that we know our arc coordinates we can finally start to draw our SVG pie chart. We’ll begin by focusing on a single sector. It’s extremely straightforward although SVG notation makes it a bit confusing. I’ll clarify right after the code.

```
<svg style="width: 230px; height: 230px;">
<path fill="#61C0BF" d="M115,115 L115,0 A115,115 1 0,1 196.317, 33.6827 z"></path>
</svg>
```

The steps taken are exactly as I’ve described them above. `M`

means move to, `L`

means create a line, `A`

means create an arc and `z`

closes the path. Here’s a detailed breakdown of how the sector is drawn:

- Move to
`115,115`

. This does not draw anything, it simply moves “the pencil” to those coordinates. - Draw a line from your current location up to
`115,0`

- From the current point, draw an arc to
`196.317,33.6827`

- Close the path by drawing a line from the current point back to the starting point

Here’s how this would look in general form:

```
<svg style="width: <diameter>px; height: <diameter>px;">
<path fill="#61C0BF" d="M<radius>,<radius> L<radius>,0 A<radius>,<radius> 1 0,1 <X>, <Y> z"></path>
</svg>
```

If we want to add more than one segment we have two options. Figure out each arc’s start and end point, or just draw all the arcs in the same position (starting from `radius,0`

) and then rotate them. I vouch for the second one – it’s a lot easier. You just need to rotate the arc by the correct amount.

The amount is the sum of arc angles before the current sector. Assume we have three sectors with 10°, 20° and 35° degree angles. The first one does not need to be rotated; it starts at 0°. The second should be rotated by 10° which would make it start from the end of the first sector. The third sector needs to start at the end of the second, so we rotate it by 30° – the sum of the two angles before it. You can execute the rotations using the transform property on the path.

The rotation needs three parameters. The first one is the degree of rotation. The second and third are the coordinates for the origin of the transformation. We want to rotate the sectors around the center point of the circle, which is `115,115`

```
<svg style="width: 230px; height: 230px;">
<path fill="#61C0BF" d="M115,115 L115,0 A115,115 1 0,1 196.317, 33.6827 z" transform="rotate(0, 115, 115)"></path>
<path fill="#DA507A" d="M115,115 L115,0 A115,115 1 0,1 196.317, 33.6827 z" transform="rotate(45, 115, 115)"></path>
<path fill="#BB3D49" d="M115,115 L115,0 A115,115 1 0,1 196.317, 33.6827 z" transform="rotate(90, 115, 115)"></path>
</svg>
```

## Generating An SVG Pie Chart With Javascript

We know everything to create a neat little framework that will draw an SVG pie chart for us. Let’s lay the groundwork by defining a Javascript data object:

```
var data = {
size: 230,
sectors: [
{
percentage: 0.45,
label: 'Thing 1'
},
{
percentage: 0.21,
label: "Thing Two"
},
{
percentage: 0.11,
label: "Another Thing"
},
{
percentage: 0.23,
label: "Pineapple"
}
]
}
```

Now, let’s create a function that we’ll run our data array through to calculate the points for the sectors. This function translates our math above into Javascript.

```
function calculateSectors( data ) {
var sectors = [];
var colors = [
"#61C0BF", "#DA507A", "#BB3D49", "#DB4547"
];
var l = data.size / 2
var a = 0 // Angle
var aRad = 0 // Angle in Rad
var z = 0 // Size z
var x = 0 // Side x
var y = 0 // Side y
var X = 0 // SVG X coordinate
var Y = 0 // SVG Y coordinate
var R = 0 // Rotation
data.sectors.map( function(item, key ) {
a = 360 * item.percentage;
aCalc = ( a > 180 ) ? 360 - a : a;
aRad = aCalc * Math.PI / 180;
z = Math.sqrt( 2*l*l - ( 2*l*l*Math.cos(aRad) ) );
if( aCalc <= 90 ) {
x = l*Math.sin(aRad);
}
else {
x = l*Math.sin((180 - aCalc) * Math.PI/180 );
}
y = Math.sqrt( z*z - x*x );
Y = y;
if( a <= 180 ) {
X = l + x;
arcSweep = 0;
}
else {
X = l - x;
arcSweep = 1;
}
sectors.push({
percentage: item.percentage,
label: item.label,
color: colors[key],
arcSweep: arcSweep,
L: l,
X: X,
Y: Y,
R: R
});
R = R + a;
})
return sectors
}
```

The result of this function is an array of objects that represent each sector. The object contains the percentage, label, color, radius, X and Y coordinates and the rotation.

Note that I needed to create the `aRad`

variable which is the angle in radians. Javascript calculates trigonometry functions in radians, so that’s what we need to feed it. You can convert a degree value to radians by multiplying by pi and dividing by 180.

Let’s see how this all works. I created `aCalc`

which is the name I gave to the transformed angle. To get the transformed angle, we deduct it from 360° if it is above 180°.

Depending on the calculated angle we arrive at `x`

slightly differently. Since we have `x`

and `z`

now, we can use Pythagoras’ theorem to calculate `y`

. The length of side `y`

will always be equal to amount we need to travel on the y axis so we have our `Y`

coordinate.

The `X`

coordinate depends on the original angle. If it was below 180° we need to add the length of side `x`

to the radius. If it was above 180° we need to subtract the length of `x`

from the radius, this arriving at our final `X`

coordinate.

We also need to set an `arcSweep`

value which is used in SVG (as the fourth number of the arc command). For angles below 180° we use 0, for angles above we use 1 to sure the arc is drawn in the correct place.

Lastly, we add all the data to an array and calculate the rotation for the next sector. We can now map over the sectors and add them to an SVG element to create our svg pie chart.

```
sectors = calculateSectors(data);
var newSVG = document.createElementNS( "http://www.w3.org/2000/svg","svg" );
newSVG.setAttributeNS(null, 'style', "width: "+data.size+"px; height: " + data.size+ "px");
document.getElementsByTagName("body")[0].appendChild(newSVG)
sectors.map( function(sector) {
var newSector = document.createElementNS( "http://www.w3.org/2000/svg","path" );
newSector.setAttributeNS(null, 'fill', sector.color);
newSector.setAttributeNS(null, 'd', 'M' + sector.L + ',' + sector.L + ' L' + sector.L + ',0 A' + sector.L + ',' + sector.L + ' 1 0,1 ' + sector.X + ', ' + sector.Y + ' z');
newSector.setAttributeNS(null, 'transform', 'rotate(' + sector.R + ', '+ sector.L+', '+ sector.L+')');
newSVG.appendChild(newSector);
})
var midCircle = document.createElementNS( "http://www.w3.org/2000/svg","circle" );
midCircle.setAttributeNS(null, 'cx', data.size * 0.5 );
midCircle.setAttributeNS(null, 'cy', data.size * 0.5);
midCircle.setAttributeNS(null, 'r', data.size * 0.28 );
midCircle.setAttributeNS(null, 'fill', '#42495B' );
newSVG.appendChild(midCircle);
```

First, I created an SVG element with a style attribute and appended it to the body tag of the document. Then I map over the sectors array and create a new path for each member of the array. The path has a fill attribute and a d attribute which contains the points to draw the sector as discussed. I substitute the appropriate data from the sector’s information. Then I set the rotation and append the path to our SVG element.

Finally, I add a new circle which has its center point at the same location as our large circle but is only 28% the size with a blue fill – thus creating the blue circle that blocks out the middle of our SVG pie chart.

## Final Thoughts

Recently I’ve learned to love SVG because it can be added inline which reduces requests, allows for smooth animations and can be manipulated a lot more easily than anything else. As you can see in this post, all images – except for the very first one – are SVG elements. It scales to any platform including retina screens, and it can be handled responsively just like any other image.

I’ve been creating some other UI elements using SVG as well, and it’s been a blast so far. I’m really enjoying how easy they are to create programmatically once you wrap your head around SVG. If you’d like to learn more can heartily recommend theSVG coordinate systems article by Sara Soueidan.

I’ve added the full code to a jsbin bin, feel free to play around with it!

This was such a helpful article! There was one thing I noticed, and that was the code that created the SVG d attribute wouldn’t calculate it right for angles over 180 degrees. The pie pieces would become skewed to mis-shape the circle. After playing with it a bit, I found out by changing the hard coded point 0,1 to 1,1 for angles over 180 degrees it would keep the circles integrity. Other than that it was a huge help. Thank you for sharing your brilliance!

Great read, thank you for sharing.

I implemented this in a web-application where i needed it. After poking at it i discovered that it might be easier to use relative X and Y for the arc. When we are using relative positioning we don’t need to do all that trigonometry to figure out the position, since it is just a circle and we can use the rules for the unit circle (https://en.wikipedia.org/wiki/Unit_circle). Then the method to get the path will become (where radians are given):

“`

var x = Math.sin(radians) * radius;

// subtract because we are starting at the top of the circle

var y = radius – (Math.cos(radians) * radius);

var sweep = radians > Math.PI ? ‘1’: ‘0’;

// I am using template strings here for brevity

var path = `M ${radius} ${radius} l 0 ${-radius} a ${radius} ${radius} 0 ${sweep} 1 ${x} ${y} Z

“`

It does not show any arch if there is only 1 sector with percentage 1:

var data = {

size: 230,

sectors: [

{

percentage: 0,

label: ‘Thing 1’

},

{

percentage: 0,

label: “Thing Two”

},

{

percentage: 0,

label: “Another Thing”

},

{

percentage: 1,

label: “Pineapple”

}

]

}

I’m just skimming while brainstorming how to implement SVG pie charts but the math-iness of your solution makes me excited to try it out. ðŸ™‚ Totally different from the usual “install this plugin and be done with it” tutorials. Thank you!

Great post! I’m working on a react component using your code. I noticed that when the percentage is 1 the sector wont render a full “pie”.

Hi! Your code has a bug. Set

var data = {

size: 100,

sectors: [

{

percentage: 0.55,

label: ‘Thing 1’

},

{

percentage: 0.45,

label: “Thing Two”

},

{

percentage: 0,

label: “Another Thing”

}

]

}

Hi Daniel,

this line of code (from your article) does not work for sectors bigger than 50%:

newSector.setAttributeNS(null, ‘d’, ‘M’ + sector.L + ‘,’ + sector.L + ‘ L’ + sector.L + ‘,0 A’ + sector.L + ‘,’ + sector.L + ‘ 1 0,1 ‘ + sector.X + ‘, ‘ + sector.Y + ‘ z’);

you should use the “sector.arcSweep” property:

newSector.setAttributeNS(null, ‘d’, ‘M’ + sector.L + ‘,’ + sector.L + ‘ L’ + sector.L + ‘,0 A’ + sector.L + ‘,’ + sector.L + ‘ 0 ‘ + sector.arcSweep + ‘,1 ‘ + sector.X + ‘, ‘ + sector.Y + ‘ z’);

and your code works like a charm ðŸ™‚

THANKS!!!

Thanks Daniel, good stuff! Quick question, looks like this breaks if any percentages are >60%, any suggestions for an easy fix?

Hello, thanks for great post, I learned a lot. You not only show how, but also explain why (in a very good way), which is not very common this days.

For myself I found a bit simplified math, which thanks to only using trigonometry is independent of angle type (acute, obtuse, etc.).

“M0,0 L[radius],0 A[radius],[radius] 1 0,1 [radius*cos(angle)], [radius*sin(angle )] z”

And then just translate the path by vector [radius,radius], so it will be in top left corner of svg). To follow the rules of drawing charts first sector should also be rotated -90 degrees, and all next [-90 + sum of previous sector angles].

I am just starting playing with SVG so I don’t know if this translation can easily be done in path parameters, coz I don’t understand yet how arc works ðŸ™‚

I wrote a Vue implementation of this to try it out live. Seems to work perfectly with more than one segment. I might try to think of a fix for one segment, but having only one segment seems to defeat the purpose of a pie chart anyway. https://cdpn.io/e/aEabGV