How to create a radial progress bar with SVG

Simple radial progress bar using SVG, CSS and JS.
unending spiral

Radial progress

JS Bin

What is it for?

A while back we implemented a little countdown timer, which involved creating a radial progress bar. We ended up building a SVG radial timer, however we found that a lot of online resources were a bit complicated. In the end we built a straight forward simple radial progress bar using SVG, CSS and JS.

Why SVG?

We chose SVG over images because it can be styled with CSS, allowing us to quickly change look and feel of the progress bar without asking for another image from designer. It also has a lower memory footprint than an image since we don’t need to load any additional assets. The most important reason is of course “it is vector based” so it works well on different resolutions.

Creating a circle with SVG

Our basic SVG structure is the circle:

<circle cx="60" cy="60" r="50"/>

cxcy and r are the minimum attributes which are needed to describe a circle.

cx and cy

cx and cy are attributes describing the position of the circle center, relative to SVG starting position in the top left corner


The r attribute specifies the radius of the <circle>

Creating a ring using a circle!

Ring is nothing but a <circle> which has only a visible border. To achieve this we need to make the inner part of the circle transparent and add some thick border. In SVG we can’t directly use border-width or border-color to achieve what we are aiming for. So let’s look how we can achieve this using the SVG attribute stroke.

The easiest way to illustrate the stroke attribute, is to consider the <line> element as an example.


For a SVG element stroke is similar to what borders are in CSS. Size, color and style can be modified in order to create a dashed effect. In addition to defining color and width with the stroke attribute, we also use stroke-dasharray and stroke-dashoffset.


The stroke-dasharray describes the length of a dash.

<line stroke-dasharray="2em" x1="0" y1="0" x2="8em" y2="0" />

In the example each dash is 2em long. JS Bin


stroke-dashoffset describes the offset from the default position of our stroke.

<line stroke-dashoffset="1em" stroke-dasharray="2em" x1="0" y1="1em" x2="8em" y2="1em" />

In the example the green line has a stroke-dashoffset of 1em, which means that the starting position of the stroke moved 1em to the left.


fill is used to set the fill color of the SVG element. In our case we set it to transparent so that we get a ring at the end.

JS Bin

Creating layers in SVG

In order to create a progress bar we need to create some layers. One background layer, and one layer actually moving as the timer counts down. So in our example there is one ring at the bottom creating the background for the progress bar, and then another ring on top where we use stroke-dasharray and stroke-dashoffset. This allows us to modify the state of the progress bar by adjust the stroke-dashoffset parameter.

JS Bin

There is however a problem with our current example it start at 90° and moves counter clockwise. In order to have the initial position to 0°, we rotate the circles 270° degrees. To make it move clockwise we’ll have to adjust the stroke-dashoffset. We’ll address that in the animation step.

Since we wanted to fill the center with text, we added a circle at the bottom with fill so the whole radial progress bar has a background.


The last step is of course to put it all together and make it come alive. So in our last example we have added some javascript that sets and updates the stroke-offset of the progress-cover ring every second.

As mentioned in the previous section if we increase the stroke-dashoffset it will move counter clockwise. And since this a time we want the animation to move clockwise, we set the stroke-dashoffset to an negative value. And by decreasing the stroke-dashoffset by 1/60 part of the circumference of the ring, we get a nice one minute countdown timer.

JS Bin


Photo by Patrick McManaman on Unsplash

Want to join our Engineering team?
Apply today!