In the first part of decoding the SVG path, we mostly looked at how to convert things from semantic tags (line, polyline, polygon) into path command syntax, but the path element did not really offer us any new shape options. In this article, that changes, because we will learn how to draw curves and arcs, which are simply parts of an ellipse.
A quick recap of previous articles
If this is your first encounter with this series, we recommend getting familiar with the basics of hand-coding SVG, as well as how <marker> works, and having a basic understanding of animate, because this guide does not explain those topics. We also recommend knowing about the M/m command in the <path> d attribute — we wrote the above-mentioned article about path line commands to help with that.
Note: This article focuses only on the syntax of curve and arc commands and does not offer an introduction to path as an element.
Before we begin, we want to quickly review how we code SVG — using JavaScript. We do not like working with numbers and math, and when reading SVG code where every attribute is filled with numbers, we lose all sense of what is going on. Giving coordinates names and having easy-to-read, written-out math makes this type of code much easier for us, and we think it will help you too.
Because the goal of this article is to understand path syntax rather than layout or how to use loops and other simpler things, we will not go through the full setup for every example. We will share code snippets, but note that they may be slightly adjusted from the CodePen examples or simplified to make the article easier to read. However, if you have specific questions about code that is not in the text but appears in the CodePen demos, the comments section, as always, is open.
To keep everything framework-independent, the code is written in plain JavaScript, although in practice, when working with complex images, TypeScript is highly recommended.
Drawing Bézier curves
Being able to draw lines, polygons, polylines, and their compound versions is fun and useful, but path can do more than simply offer more cryptic implementations of basic semantic SVG tags.
One of those additional types is Bézier curves.
There are several different curve commands. And this is where the idea of points and control points comes in.
The mathematical plotting of Bézier curves is outside the scope of this article.
However, there is a visually wonderful video by Freya Holmér called “The Beauty of Bézier Curves”, which dives into the construction of cubic and quadratic Bézier curves with beautiful animation, making the math much easier to digest.
Fortunately, SVG lets us draw quadratic curves with one control point and cubic curves with two control points, without doing any additional math.
So what is a control point? A control point is the position of a handle that controls the curve. It is not a point that gets drawn.
We found that the best way to understand these path commands is to render them as a graphical user interface (GUI), the way Affinity and Illustrator would. Then draw the “handles” and a few random curves with different properties and see how they affect the curve. Seeing that animation also really helps understand the mechanics of these commands.
That is exactly why we will use markers and animation in the visualizations below. You will notice that the markers used are rectangles and circles, and because they are connected with lines, we can use marker and save a lot of animation time, since these additional elements are attached to the system. Animating one d command instead of separate x and y attributes also makes the SVG code much shorter.
Quadratic Bézier curves: the Q and T commands
The Q command is used to draw quadratic Bézier curves. It takes two arguments: a control point and an end point.
So, for a simple curve, we would start with M to move to the starting point, then Q to draw the curve.
const path = `M${start.x} ${start.y} Q${control.x} ${control.y} ${end.x} ${end.y}`;
Because we have a control point, a starting point, and an end point, it is actually fairly simple to render a single-handle path, the way a graphics program would.
The funny thing is that you have probably never interacted with a quadratic Bézier curve the same way you have with a cubic one in most common GUIs! Most popular programs will convert this curve into a cubic curve with two handles and control points as soon as you want to play with it.
For drawing, we created several markers and draw the handle in red so that it stands out more clearly.
We also defined the main path with a gradient and gave it a cross-hatch pattern fill. We talked about pattern in the first article; linearGradient is fairly similar. Both are def elements that you can reference through an id. We like seeing the fill, but if it distracts you, you can modify its variable.
We encourage you to look at the example with and without the handle rendering to see some of the nuances that appear around points when the control points get close to them.
See the Pen SVG Path Quadratic Bézier Curve Visual by Smashing Magazine (@smashingmag) on CodePen.
(See CodePen SVG Path Quadratic Bézier Curve Visual)
Quadratic Bézier curves are “less curvy.” These curves always stay somewhat similar to “u” or “n” shapes and cannot be twisted. They can, however, be compressed.
Connected Bézier curves are called “splines.” And there is an additional command for connecting multiple quadratic curves: the T command.
The T command is used to draw a curve that is connected to the previous curve, so it must always follow a Q command (or another T command). It accepts only one argument, which is the end point 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 in the Q command.
To see how we created this example, note that the implied handles are drawn in green, while the controls we specify are still rendered in red.
See the Pen SVG Path Quadratic Curve T Command by Smashing Magazine (@smashingmag) on CodePen.
(See CodePen SVG Path Quadratic Curve T Command)
Okay, so the upper curve uses two Q commands, which means there are three control points in total. Using a separate control point to create the curve makes sense, but the third control point is simply the reflection of the second control point across the previous point.
That is exactly what the T command does. It predicts control points by reflecting them across the end point of the previous Q (or T) command. You can see how the system connects in the animation below, where we manipulated only the positions of the main points and the first control points. The implied control points follow along.
See the Pen SVG Path Quadratic Bézier Spline T Command Visual by Smashing Magazine (@smashingmag) on CodePen.
(See CodePen SVG Path Quadratic Bézier Spline T Command Visual)
There are also q and t commands, which use relative coordinates.
Before continuing, if you want to interact with a cubic curve, SVG Path Editor lets you edit all path commands very nicely.
Cubic Bézier curves: C and S
Cubic Bézier curves work basically like quadratic curves, but instead of 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 finally 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 the visualization to see it in action.
See the Pen SVG Path Cubic Bézier Curve Animation by Smashing Magazine (@smashingmag) on CodePen.
(See CodePen SVG Path Cubic Bézier Curve Animation)
Cubic Bézier curves are masters of flexibility. Unlike a quadratic curve, this one can twist, loop, and take completely different forms from any other SVG element. It can split a filled area into two parts, while a quadratic curve cannot.
As with the T command, cubic curves have a reflection command available: S.
When using it, the first control point is obtained through reflection, and we can define the new final control point and end point ourselves. As before, this requires a spline, so at least one previous 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}`;
We created a live visualization for that too.
See the Pen SVG Path Cubic Bézier Spline S Command Visual by Smashing Magazine (@smashingmag) on CodePen.
(See CodePen SVG Path Cubic Bézier Spline S Command Visual)
When to useTandS: The big advantage of these chained reflection commands is when you want to draw waves, or simply make absolutely sure that the connection in your spline is smooth.
If you cannot use reflection but want a nice smooth connection, make sure your control points form a straight line. If there is a break in the handles, your spline will have one too.
Arcs: the A command
Finally, the last type of path command is for creating arcs. Arcs are parts of circles or ellipses.
This is our least favorite command because it has so many pieces. But it is the secret to drawing a proper pie chart, so we have spent some time with it.
Let’s look at it.
As with every other path command, the lowercase letter means relative coordinates. So just as there is an A command, there is 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 on earth are xAxisRotation, largeArcFlag, and sweepFlag? In brief:
xAxisRotationis the rotation of the underlying ellipse’s axes in degrees.largeArcFlagis a boolean value that determines whether the arc is greater than 180°.sweepFlagis also a boolean value and determines the direction of the arc — that is, whether it goes clockwise or counterclockwise.
To better understand these concepts, we created the following visualization.
See the Pen SVG Path Arc Command Visuals by Smashing Magazine (@smashingmag) on CodePen.
(See CodePen SVG Path Arc Command Visuals)
Radius size
In that CodePen, you will notice that ellipses are drawn for each command. In the top row, they overlap, and in the bottom row, they are stacked on top of each other. 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 in the second row.
The stacking happens because the radius size is only respected if the start and end points fit inside the specified ellipse. This behavior surprised us, so we dug into the specifications and found this information about how arcs work:
“Any numerical value is allowed for all elliptical arc parameters (except the boolean flags), but user agents must make the following corrections for invalid values when rendering curves or calculating their geometry:
- If the endpoint (x, y) of the segment is identical to the current point (for example, the endpoint of the previous segment), this is equivalent to omitting the elliptical arc segment entirely.
- If rx or ry is 0, this arc is treated as a straight line segment (lineto) connecting the endpoints.
- If rx or ry have negative signs, those signs are removed; the absolute value is used instead.
- If rx, ry, and x-axis-rotation are such that no solution exists (essentially, the ellipse is not large enough to reach from the current point to the new endpoint), the ellipse is scaled up uniformly until there is exactly one solution (until the ellipse is just large enough).
See the appendix section Correction of out-of-range radii, which provides the mathematical formula for this scaling operation.”
— 9.5.1 Out-of-range elliptical arc parameters
So in reality, that stacking is just graceful error handling, not how it was intended to be used. The top row is how arcs are supposed to be used.
With sensible values, the underlying ellipse and two points give us four drawing options for how we could connect two points along the ellipse’s path. That is exactly what the boolean values are for.
xAxisRotation
Before moving on to the boolean values, the cross-hatching shows the xAxisRotation. The ellipse is rotated around its center, and the degree value relates to the SVG x direction. So if you are working with a circular ellipse, rotation will have no effect on the arc (except if you use it in the pattern, as we did there).
Sweep Flag
Notice the small arrow marker showing the direction in which the arc is drawn. 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 whether you want the smaller or larger arc from the ellipse. If we have the scaled-up case, we get exactly 180° of our ellipse.
Arcs usually require a lot more annoying circular-number handling than we like doing. As soon as radians appear, we tend to fall into rabbit holes where we have to relearn far too much math that we happily forget. They depend more on whether values relate to one another in order for the result to be as expected, and there is simply a lot of information going in.
But — and this is a big but — arcs are wonderfully powerful!
Conclusion
Okay, that was a lot! But we hope you are beginning to see how path commands can be useful. We find them extremely useful for illustrating data.
Once you know how easy it is to create things like grids, boxes, and curves, it does not take many more steps to create visualizations that are a little more unique than what standard data visualization libraries provide.
With everything you have learned in this article series, you are basically fully equipped to render all types of charts — or other kinds of visualizations.
For example, what about visualizing a basic cubic Bézier curve, say CSS transition-timing-function: ease;? That is exactly what we created when trying to figure out how we could turn those transition-timing-function values into something the <animate> tag understands.
See the Pen CSS Cubic Beziers as SVG Animations & CSS Transition Comparisons by Smashing Magazine (@smashingmag) on CodePen.
(See CodePen CSS Cubic Beziers as SVG Animations & CSS Transition Comparisons)
SVG is fun and strange, and the path element may contain the most overwhelming string of characters you have ever seen during code inspection. But if you take the time to understand the underlying logic, it all becomes one beautifully simple and incredibly powerful syntax.
We hope that with these two path decoding articles, we have managed to reveal the core mechanics of how path drawing works. If you want more resources that do not require diving into specifications, try the MDN tutorial on paths. It is short and compact, and it was the main resource we used while learning all of this.
However, since writing a detailed overview on this topic, we discovered the wonderful svg-tutorial.com, which does a great job visualizing SVG coding in general, but especially stands out with our favorite arc visualization in the Arc Editor. And if you have a path you would like to properly decode without keeping all the information from these two articles in your head, there is SVG Path Visualizer, which breaks down path information very nicely.
And now: go forth and have fun in the matrix.



