Pure JavaScript Smooth Curve Generation Using Bézier Curves

This guide shows how to generate smooth, high‑quality curves from a polyline in pure JavaScript by computing quadratic and cubic Bézier points, deriving control points via angle‑bisector geometry, assembling the segments, and rendering and animating the result on an HTML5 canvas without external libraries.

DeWu Technology
DeWu Technology
DeWu Technology
Pure JavaScript Smooth Curve Generation Using Bézier Curves

Smooth curve generation is a practical technique for turning a polyline into a visually pleasing continuous curve. It is often needed when drawing charts, animations, or custom graphics on the web.

The implementation relies on Bézier curves, which are parametric curves widely used in computer graphics. Both quadratic (second‑order) and cubic (third‑order) Bézier formulas are presented, allowing flexible control over the curve shape.

Quadratic Bézier formula:

B(t) = (1‑t)²·P0 + 2·t·(1‑t)·P1 + t²·P2
/**
 * @param {number} p0
 * @param {number} p1
 * @param {number} p2
 * @param {number} t
 * @return {number}
 */
bezier2P(p0, p1, p2, t) {
  const P0 = p0 * Math.pow(1 - t, 2);
  const P1 = p1 * 2 * t * (1 - t);
  const P2 = p2 * t * t;
  return P0 + P1 + P2;
}

/**
 * Get a point on a quadratic Bézier curve.
 */
getBezierNowPoint2P(p0, p1, p2, num, tick) {
  return {
    x: this.bezier2P(p0.x, p1.x, p2.x, num * tick),
    y: this.bezier2P(p0.y, p1.y, p2.y, num * tick)
  };
}

/**
 * Generate an array of points for a quadratic Bézier curve.
 */
create2PBezier(p0, p1, p2, num = 100, tick = 1) {
  const t = tick / (num - 1);
  const points = [];
  for (let i = 0; i < num; i++) {
    const pt = this.getBezierNowPoint2P(p0, p1, p2, i, t);
    points.push({ x: pt.x, y: pt.y });
  }
  return points;
}

Cubic Bézier formula:

B(t) = (1‑t)³·P0 + 3·t·(1‑t)²·P1 + 3·t²·(1‑t)·P2 + t³·P3
/**
 * @param {number} p0
 * @param {number} p1
 * @param {number} p2
 * @param {number} p3
 * @param {number} t
 * @return {number}
 */
bezier3P(p0, p1, p2, p3, t) {
  const P0 = p0 * Math.pow(1 - t, 3);
  const P1 = 3 * p1 * t * Math.pow(1 - t, 2);
  const P2 = 3 * p2 * Math.pow(t, 2) * (1 - t);
  const P3 = p3 * Math.pow(t, 3);
  return P0 + P1 + P2 + P3;
}

/**
 * Get a point on a cubic Bézier curve.
 */
getBezierNowPoint3P(p0, p1, p2, p3, num, tick) {
  return {
    x: this.bezier3P(p0.x, p1.x, p2.x, p3.x, num * tick),
    y: this.bezier3P(p0.y, p1.y, p2.y, p3.y, num * tick)
  };
}

/**
 * Generate an array of points for a cubic Bézier curve.
 */
create3PBezier(p0, p1, p2, p3, num = 100, tick = 1) {
  const t = tick / (num - 1);
  const points = [];
  for (let i = 0; i < num; i++) {
    const pt = this.getBezierNowPoint3P(p0, p1, p2, p3, i, t);
    points.push({ x: pt.x, y: pt.y });
  }
  return points;
}

To build a smooth polyline, control points for each segment are computed using a simple geometric method: the angle bisector of the two adjacent segments defines the direction, and a configurable ratio determines the curvature magnitude.

/**
 * Generate control points for a smooth line segment.
 */
createSmoothLineControlPoint(p1, pt, p2, ratio = 0.3) {
  const vec1T = vector2dMinus(p1, pt);
  const vecT2 = vector2dMinus(p2, pt);
  const len1 = vec1T.length;
  const len2 = vecT2.length;
  const v = len1 / len2;
  let delta;
  if (v > 1) {
    delta = vector2dMinus(p1, vector2dPlus(pt, vector2dMinus(p2, pt).scale(1 / v)));
  } else {
    delta = vector2dMinus(vector2dPlus(pt, vector2dMinus(p1, pt).scale(v)), p2);
  }
  delta = delta.scale(ratio);
  const control1 = { x: vector2dPlus(pt, delta).x, y: vector2dPlus(pt, delta).y };
  const control2 = { x: vector2dMinus(pt, delta).x, y: vector2dMinus(pt, delta).y };
  return { control1, control2 };
}

/**
 * Assemble a smooth line from an array of points.
 */
createSmoothLine(points, ratio = 0.3) {
  const len = points.length;
  if (len < 3) return [];
  const resultPoints = [];
  const controlPoints = [];
  for (let i = 0; i < len - 2; i++) {
    const { control1, control2 } = this.createSmoothLineControlPoint(
      new Vector2D(points[i].x, points[i].y),
      new Vector2D(points[i + 1].x, points[i + 1].y),
      new Vector2D(points[i + 2].x, points[i + 2].y),
      ratio
    );
    controlPoints.push(control1, control2);
    let segPoints;
    if (i === 0) {
      segPoints = this.create2PBezier(points[i], control1, points[i + 1], 50);
    } else {
      segPoints = this.create3PBezier(
        points[i],
        controlPoints[2 * i - 1],
        control1,
        points[i + 1],
        50
      );
    }
    resultPoints.push(...segPoints);
    if (i + 2 === len - 1) {
      const endSeg = this.create2PBezier(points[i + 1], control2, points[i + 2], 50);
      resultPoints.push(...endSeg);
    }
  }
  return resultPoints;
}

Example usage demonstrates how to define a set of input points, generate the smooth curve, draw both the original polyline (red) and the smooth curve (blue) on an HTML5 canvas, and animate a small element along the smooth path.

const input = [
  { x: 0,   y: 0 },
  { x: 150, y: 150 },
  { x: 300, y: 0 },
  { x: 400, y: 150 },
  { x: 500, y: 0 },
  { x: 650, y: 150 }
];
const s = path.createSmoothLine(input);
const ctx = document.getElementById('cv').getContext('2d');
// draw smooth curve (blue)
ctx.strokeStyle = 'blue';
ctx.beginPath();
ctx.moveTo(s[0].x, s[0].y);
for (let i = 1; i < s.length; i++) ctx.lineTo(s[i].x, s[i].y);
ctx.stroke();
// draw original polyline (red)
ctx.strokeStyle = 'red';
ctx.beginPath();
ctx.moveTo(input[0].x, input[0].y);
for (let i = 1; i < input.length; i++) ctx.lineTo(input[i].x, input[i].y);
ctx.stroke();
// animate a point along the smooth line
document.getElementById('btn').addEventListener('click', () => {
  let idx = 0;
  const app = document.getElementById('app');
  const step = () => {
    if (idx < s.length) {
      app.style.left = s[idx].x - 10 + 'px';
      app.style.top = s[idx].y - 10 + 'px';
      idx++;
      requestAnimationFrame(step);
    }
  };
  step();
});

This tutorial provides a complete, self‑contained solution for generating smooth curves in pure JavaScript, suitable for front‑end developers who need high‑quality vector graphics without external libraries.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Front-endalgorithmJavaScriptCanvasBézier CurveSmooth Curve
DeWu Technology
Written by

DeWu Technology

A platform for sharing and discussing tech knowledge, guiding you toward the cloud of technology.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.