Decoding The SVG <code>path</code> Element: Curve And Arc Commands
On her quest to teach you how to code vectors by hand, Myriam Frisano’s second installment of a `path` deep dive explores the most complex aspects of SVG’s most powerful element. She’ll help you understand the underlying rules and function of how curves and arcs are constructed. By the end of it, your toolkit is ready to tackle all types of tasks required to draw with code — even if some of the lines twist and turn.

In the first part of decoding the SVG path
pair, we mostly dealt with converting things from semantic tags (line
, polyline
, polygon
) into the path
command syntax, but the path
element didn’t really offer us any new shape options. This will change in this article as we’re learning how to draw curves and arcs, which just refer to parts of an ellipse.
TL;DR On Previous Articles
If this is your first meeting with this series, I recommend you familiarize yourself with the basics of hand-coding SVG, as well as how the
works and have a basic understanding of animate, as this guide doesn’t explain them. I also recommend knowing about the M/m
command within the d
attribute (I wrote the aforementioned article on path line commands to help).
Note: This article will solely focus on the syntax of curve and arc commands and not offer an introduction to path
as an element.
Before we get started, I want to do a quick recap of how I code SVG, which is by using JavaScript. I don’t like dealing with numbers and math, and reading SVG code that has numbers filled into every attribute makes me lose all understanding of it. By giving coordinates names and having all my math easy to parse and all written out, I have a much better time with this type of code, and I think you will, too.
As the goal of this article is about understanding path
syntax and not about doing placement or how to leverage loops and other more basic things, I will not run you through the entire setup of each example. I’ll share some snippets of the code, but please note that it may be slightly adjusted from the CodePen or simplified to make the article easier to read. However, if there are specific questions about code not part of the text that’s in the CodePen demos — the comment section is open, as always.
To keep this all framework-agnostic, the code is written in vanilla JavaScript, though, in practice, TypeScript comes highly recommended when dealing with complex images. Drawing Bézier Curves
Being able to draw lines, polygons, polylines, and compounded versions of them is all fun and nice, but path
can also do more than just offer more cryptic implementations of basic semantic SVG tags.
One of those additional types is Bézier curves.
There are multiple different curve commands. And this is where the idea of points and control points comes in.
Bézier math plotting is out of scope for this article.
But, there is a visually gorgeous video by Freya Holmér called The Beauty of Bézier Curves which gets into the construction of cubic and quadratic bézier curves that features beautiful animation and the math becomes a lot easier to digest.
Luckily, SVG allows us to draw quadratic curves with one control point and cubic curves with two control points without having to do any additional math.
So, what is a control point? A control point is the position of the handle that controls the curve. It is not a point that is drawn.
I found the best way to understand these path commands is to render them like a GUI, like Affinity and Illustrator would. Then, draw the “handles” and draw a few random curves with different properties, and see how they affect the curve. Seeing that animation also really helps to see the mechanics of these commands.
This is what I’ll be using markers and animation for in the following visuals. You will notice that the markers I use are rectangles and circles, and since they are connected to lines, I can make use of marker
and then save myself a lot of animation time because these additional elements are rigged to the system. (And animating a single d
command instead of x
and y
attributes separately makes the SVG code also much shorter.)
Quadratic Bézier Curves: Q
& T
Commands
The Q
command is used to draw quadratic béziers. It takes two arguments: the control point and the end point.
So, for a simple curve, we would start with M
to move to the start point, then Q
to draw the curve.
const path = M${start.x} ${start.y} Q${control.x} ${control.y} ${end.x} ${end.y}
;
Since we have the Control Point, the Start Point, and the End Point, it’s actually quite simple to render the singular handle path like a graphics program would.
Funny enough, you probably have never interacted with a quadratic Bézier curve like with a cubic one in most common GUIs! Most of the common programs will convert this curve to a cubic curve with two handles and control points as soon as you want to play with it.
For the drawing, I created a couple of markers, and I’m drawing the handle in red to make it stand out a bit better.
I also stroked the main path
with a gradient and gave it a crosshatch pattern fill. (We looked at pattern
in my first article, linearGradient
is fairly similar. They’re both def
elements you can refer to via id
.) I like seeing the fill, but if you find it distracting, you can modify the variable for it.
I encourage you to look at the example with and without the rendering of the handle to see some of the nuance that happens around the points as the control points get closer to them.
See the Pen SVG Path Quadratic Bézier Curve Visual [forked] by Myriam.
Quadratic Béziers are the “less-bendy” ones.
These curves always remain somewhat related to “u” or “n” shapes and can’t be manipulated to be contorted. They can be squished, though.
Connected Bézier curves are called “Splines”. And there is an additional command when chaining multiple quadratic curves, which is the T
command.
The T
command is used to draw a curve that is connected to the previous curve, so it always has to follow a Q
command (or another T
command). It only takes one argument, which is the endpoint of the curve.
const path = M${p1.x} ${p1.y} Q${cP.x} ${cP.y} ${p2.x} ${p2.y} T${p3.x} ${p3.y}
The T
command will actually use information about our control Point cP
within the Q
command.
To see how I created the following example. Notice that the inferred handles are drawn in green, while our specified controls are still rendered in red.
See the Pen SVG Path Quadratic Curve T Command [forked] by Myriam.
OK, so the top curve takes two Q
commands, which means, in total, there are three control points. Using a separate control point to create the scallop makes sense, but the third control point is just a reflection of the second control point through the preceding point.
This is what the T
command does. It infers control points by reflecting them through the end point of the preceding Q
(or T
) command. You can see how the system all links up in the animation below, where all I’ve manipulated is the position of the main points and the first control points. The inferred control points follow along.
See the Pen SVG Path Quadratic Bézier Spline T Command Visual [forked] by Myriam.
The q
and t
commands also exist, so they will use relative coordinates.
Before I go on, if you do want to interact with a cubic curve, SVG Path Editor allows you to edit all path commands very nicely.
Cubic Bézier Curves: C
And S
Cubic Bézier curves work basically like quadratic ones, but instead of having one control point, they have two. This is probably the curve you are most familiar with.
The order is that you start with the first control point, then the second, and then the end point.
const path = M${p1.x} ${p1.y} C${cP1.x} ${cP1.y} ${cP2.x} ${cP2.y} ${p2.x} ${p2.y}
;
Let’s look at a visual to see it in action.
See the Pen SVG Path Cubic Bézier Curve Animation [forked] by Myriam.
Cubic Bézier curves are contortionists.
Unlike the quadratic curve, this one can curl up and form loops and take on completely different shapes than any other SVG element. It can split the filled area into two parts, while the quadratic curve can not.
Just like with the T
command, a reflecting command is available for cubic curves S
.
When using it, we get the first control point through the reflection, while we can define the new end control point and then the end point. Like before, this requires a spline, so at least one preceding C
(or S
) command.
const path = `
M ${p0.x} ${p0.y}
C ${c0.x} ${c0.y} ${c1.x} ${c1.y} ${p1.x} ${p1.y}
S ${c2.x} ${c2.y} ${p2.x} ${p2.y}
`;
I created a living visual for that as well.
See the Pen SVG Path Cubic Bézier Spline S Command Visual [forked] by Myriam.
When to useT
andS
:
The big advantage of using these chaining reflecting commands is if you want to draw waves or just absolutely ensure that your spline connection is smooth.
If you can’t use a reflection but want to have a nice, smooth connection, make sure your control points form a straight line. If you have a kink in the handles, your spline will get one, too.
Arcs: A
Command
Finally, the last type of path
command is to create arcs. Arcs are sections of circles or ellipses.
It’s my least favorite command because there are so many elements to it. But it is the secret to drawing a proper donut chart, so I have a bit of time spent with it under my belt.
Let’s look at it.
Like with any other path
command, lowercase implies relative coordinates. So, just as there is an A
command, there’s also an a
.
So, an arc path looks like this:
const path = M${start.x} ${start.y} A${radius.x} ${radius.y} ${xAxisRotation} ${largeArcFlag} ${sweepFlag} ${end.x} ${end.y}
;
And what the heck are xAxisRotation
, largeArcFlag
, and sweepFlag
supposed to be? In short:
xAxisRotation
is the rotation of the underlying ellipse’s axes in degrees.largeArcFlag
is a boolean value that determines if the arc is greater than 180°.sweepFlag
is also a boolean and determines the arc direction, so does it go clockwise or counter-clockwise?
To better understand these concepts, I created this visual.
See the Pen SVG Path Arc Command Visuals [forked] by Myriam.
Radius Size
You’ll notice in that CodePen that there are ellipses drawn for each command. In the top row, they are overlapping, while in the bottom row, they are stacked up. Both rows actually use the same radius.x
and radius.y
values in their arc definitions, while the distance between the start and end points increases for the second row.
The reason why the stacking happens is that the radius size is only taken into consideration if the start and end points fit within the specified ellipse. That behavior surprised me, and thus, I dug into the specs and found the following information on how the arc works:
“Arbitrary numerical values are permitted for all elliptical arc parameters (other than the boolean flags), but user agents must make the following adjustments for invalid values when rendering curves or calculating their geometry:
If the endpoint (x, y) of the segment is identical to the current point (e.g., the endpoint of the previous segment), then this is equivalent to omitting the elliptical arc segment entirely.
If either rx or ry is 0, then this arc is treated as a straight line segment (a “lineto”) joining the endpoints.
If either rx or ry have negative signs, these are dropped; the absolute value is used instead.
If rx, ry and x-axis-rotation are such that there is no solution (basically, the ellipse is not big enough to reach from the current point to the new endpoint) then the ellipse is scaled up uniformly until there is exactly one solution (until the ellipse is just big enough).
See the appendix section Correction of out-of-range radii for the mathematical formula for this scaling operation.”
— 9.5.1 Out-of-range elliptical arc parameters
So, really, that stacking is just nice and graceful error-handling and not how it was intended. Because the top row is how arcs should be used.
When plugging in logical values, the underlying ellipses and the two points give us four drawing options for how we could connect the two points along an elliptical path. That’s what the boolean values are for.
xAxisRotation
Before we get to the booleans, the crosshatch pattern shows the xAxisrotation
. The ellipse is rotated around its center, with the degree value being in relation to the x-direction of the SVG.
So, if you work with a circular ellipse, the rotation won’t have any effect on the arc (except if you use it in a pattern like I did there).
Sweep Flag
Notice the little arrow marker to show the arc drawing direction. If the value is 0, the arc is drawn clockwise. If the value is 1, the arc is drawn counterclockwise.
Large Arc Flag
The large Arc Flag tells the path if you want the smaller or the larger arc from the ellipse. If we have a scaled case, we get exactly 180° of our ellipse.
Arcs usually require a lot more annoying circular number-wrangling than I am happy doing (As soon as radians come to play, I tend to spiral into rabbit holes where I have to relearn too much math I happily forget.)Conclusion
They are more reliant on values being related to each other for the outcome to be as expected and there’s just so much information going in.
But — and that’s a bit but — arcs are wonderfully powerful!
Alright, that was a lot! However, I do hope that you are starting to see how path
commands can be helpful. I find them extremely useful to illustrate data.
Once you know how easy it is to set up stuff like grids, boxes, and curves, it doesn’t take many more steps to create visualizations that are a bit more unique than what the standard data visualization libraries offer.
With everything you’ve learned in this series of articles, you’re basically fully equipped to render all different types of charts — or other types of visualizations.
Like, how about visualizing the underlying cubic-bezier of something like transition-timing-function: ease;
in CSS? That’s the thing I made to figure out how I could turn those transition-timing-functions into something an
tag understands.
See the Pen CSS Cubic Beziers as SVG Animations & CSS Transition Comparisons [forked] by Myriam.
SVG is fun and quirky, and the path
element may be the holder of the most overwhelming string of symbols you’ve ever laid eyes on during code inspection. However, if you take the time to understand the underlying logic, it all transforms into one beautifully simple and extremely powerful syntax.
I hope with this pair of path
decoding articles, I managed to expose the underlying mechanics of how path plots work. If you want even more resources that don’t require you to dive through specs, try the MDN tutorial about paths. It’s short and compact, and was the main resource for me to learn all of this.
However, since I wrote my deep dive on the topic, I stumbled into the beautiful svg-tutorial.com, which does a wonderful job visualizing SVG coding as a whole but mostly features my favorite arc visual of them all in the Arc Editor. And if you have a path that you’d like properly decoded without having to store all of the information in these two articles, there’s SVG Path Visualizer, which breaks down path information super nicely.
And now: Go forth and have fun playing in the matrix.