The anatomy of a cubic Bézier curve in SVG

To draw a cubic Bézier curve you need 4 points: The point where the curve begins (start), two control points cp1 and cp2 and an ending point (end). Usually, the curve do not pass through the points cp1 or cp2. These points are there only to provide directional information.

In my code, in JavaScript there is an array of points that defines a cubic Bézier curve. The first element of the array is the start point, the second element is the cp1, the third is the cp2 and the last is the end point:

  let points = [
  [25,220],  // start
  [75,70],   // cp1
  [170,25],  // cp2
  [225,210]  // end
];

In SVG you will code the curve like this:

  <path d="M25,220 C75,70 170,25 225,210" ></path>

You have to use first the "Move To" command to move the pointer to the point where the curve begins.

<path d="M25,220 C75,70 170,25 225,210" ></path>

Next you use the "Cubic Curve" command (C) to draw the curve and then you need to specify three sets of coordinates: for the control points and for the end point.

<path d="M25,220 C75,70 170,25 225,210" ></path>

How to find a point on a cubic Bézier

Let's say you want to find a point in the middle of the Bézier curve:

  let t = .5;

For this you will need some helper points:

  let helperPoints = [];

To get the helper points you will need a function that calculates the position of a point on a line. Imagine a virtual line that goes from point A to point B. ( In my code the points A and B are arrays where the first element is the x and the second element is the y coordinate of the point.)

  function lerp(A, B, t) {
  // A and B are arrays where the first element is the x 
  // and the second element is the y coordinate of the point
  // if(t == .5) the function returns a point in the center of the line AB
  // t is always a number between 0 and 1
  // 0 <= t <= 1
  return [
    (B[0] - A[0]) * t + A[0], // the x coordinate
    (B[1] - A[1]) * t + A[1]  // the y coordinate
  ];
}

This function returns an array of 2 elements with the coordinates x and y of a point.

The helper points

In the next pen, the helper points are the orange ones:

The first helper point is in the middle of the virtual line that goes from start to the first control point cp1.

  helperPoints[0] = lerp(points[0], points[1], t);

The second helper point is in the middle of the virtual line that goes from first control point cp1 to the second control point cp2:

  helperPoints[1] = lerp(points[1], points[2], t);

The third helper point is in the middle of the virtual line that goes from cp2 to end:

  helperPoints[2] = lerp(points[3], points[3], t);

The forth helper point is in the middle of the virtual line that goes from helperPoints[0] to helperPoints[1].

  helperPoints[3] = lerp(helperPoints[0], helperPoints[1], t);

The fifth helper point is in the middle of the virtual line that goes from helperPoints[1] to helperPoints[2].

  helperPoints[4] = lerp(helperPoints[1], helperPoints[2], t);

Finally (sorry if it was borring), the point in the middle of the Bézier curve, the fuchsia point, is the one in the middle of the virtual line that goes from helperPoints[3] to helperPoints[4]

  helperPoints[5] = lerp(helperPoints[3], helperPoints[4], t);

All this was calculated with t = .5 for the middle point. But the variable t can take any other value between 0 and 1, between the beginning and the end of the Bézier curve.

In the following pen please move the cursor of the slider to change the value of the variable t.

Add a point to an SVG path using javascript

Now suppose you have cubic Bézier curve, and you need to add a point to it without changing the shape. By "adding a point to a Bézier curve" I mean taking a Bezier curve and code it as 2 curves. For example this curve:

  <path d="M25,220 C75,70 170,25 225,210" ></path>

can be rewritten like this:

  <path d="M25,220 C32.5,197.5 41.0125,177.3625 50.25,160" ></path>
<path d="M50.25,160 C102.6,61.675 178.25,52.75 225,210" ></path>

or like this:

  <path d="M25,220
         C32.5,197.5 41.0125,177.3625 50.25,160 
         C102.6,61.675 178.25,52.75 225,210" ></path>

In order to do it you have to take advantage of the helper points.

As it comes out, the first Bézier curve begins at the start point ( points[0] ).
The control points for the first curve are helperPoints[0] and helperPoints[3].
The end point for the first curve is the fuchsia point: helperPoints[5].

The second Bézier curve begins at the fuchsia point: helperPoints[5].
The control points for the second curve are helperPoints[4] and helperPoints[2].
The end point for the second curve is the end point of the "parent" curve: points[3].

I've putted all this in a function:

  function getBezierPoints(t) {
  let helperPoints = [];

  // helper points 0,1,2
  for (let i = 1; i < 4; i++) {
    //points.length must be 4 !!!
    let p = lerp(points[i - 1], points[i], t);
    helperPoints.push(p);
  }

  // helper points 3,4
  helperPoints.push(lerp(helperPoints[0], helperPoints[1], t));
  helperPoints.push(lerp(helperPoints[1], helperPoints[2], t));

  // helper point 5 is where the first Bézier ends and where the second Bézier begins
  helperPoints.push(lerp(helperPoints[3], helperPoints[4], t));

  // points for the 2 "child" curves
  let firstBezier = [
    points[0],
    helperPoints[0],
    helperPoints[3],
    helperPoints[5]
  ];
  let secondBezier = [
    helperPoints[5],
    helperPoints[4],
    helperPoints[2],
    points[3]
  ];

  // returns 2 array of points for the new bezier curves
  return [firstBezier, secondBezier];
}

In the next pen I'm using this function to draw 2 Bézier curves instead of one. In the following pen please move the cursor of the slider to change the value of the variable t.

Where would you use this?

For example when you intent to morph one SVG shape into another, you need two shapes with the same number of points. If your shapes have a different number of points, being able to add points on a curve may come in handy.

I've written a post about Morphing in SVG - first steps and an other one about how to Prepare The Paths For Morphing