Hermite Basis Interpolation

Hermite interpolation is a technique for creating smooth curves that pass directly through a set of control points. Unlike Bezier curves where control points influence the curve but don't lie on it, Hermite splines guarantee the curve passes through each point while maintaining smooth tangents.

The Core Concept

A cubic Hermite spline interpolates between two points using four pieces of information:

  • P0: The starting point
  • P1: The ending point
  • M0: The tangent (velocity) at the starting point
  • M1: The tangent (velocity) at the ending point

The key insight is that knowing both the position and direction at each endpoint gives us enough information to construct a unique smooth curve between them.

The Basis Functions

The interpolation is controlled by four basis functions, each responsible for blending one of the inputs. As the parameter t goes from 0 to 1, these functions determine how much each input contributes to the final position:

tvalue00.5100.51h00 (point P0)h01 (point P1)h10 (tangent M0)h11 (tangent M1)

The four basis functions are:

  • h00(t) = 2t³ - 3t² + 1 (blue) — Starts at 1, smoothly decreases to 0. Controls how much P0 influences the curve.
  • h01(t) = -2t³ + 3t² (green) — Starts at 0, smoothly increases to 1. Controls how much P1 influences the curve.
  • h10(t) = t³ - 2t² + t (orange) — Zero at both ends with a positive bump. Lets M0 "push" the curve in its direction.
  • h11(t) = t³ - t² (purple) — Zero at both ends with a negative dip. Lets M1 "pull" the curve as it arrives.

Why These Functions Work

The basis functions are carefully designed with specific properties:

At t = 0 (start of curve):

  • h00(0) = 1, all others = 0 → curve starts exactly at P0
  • h10(0) = 0 but h10'(0) = 1 → derivative equals M0

At t = 1 (end of curve):

  • h01(1) = 1, all others = 0 → curve ends exactly at P1
  • h11(1) = 0 but h11'(1) = 1 → derivative equals M1

This guarantees the curve passes through both points with the specified tangents.

Implementation

typescript
function hermiteBasis(t: number): [number, number, number, number] {
  const t2 = t * t;
  const t3 = t2 * t;
  return [
    2 * t3 - 3 * t2 + 1, // h00: value at p0
    t3 - 2 * t2 + t, // h10: tangent at p0
    -2 * t3 + 3 * t2, // h01: value at p1
    t3 - t2, // h11: tangent at p1
  ];
}

To interpolate between two points, multiply each input by its corresponding basis function and sum:

typescript
interface Point {
  x: number;
  y: number;
}

function hermiteInterpolate(
  p0: Point,
  p1: Point,
  m0: Point,
  m1: Point,
  t: number,
): Point {
  const [h00, h10, h01, h11] = hermiteBasis(t);
  return {
    x: h00 * p0.x + h10 * m0.x + h01 * p1.x + h11 * m1.x,
    y: h00 * p0.y + h10 * m0.y + h01 * p1.y + h11 * m1.y,
  };
}

Visual Comparison

The difference between linear interpolation and Hermite interpolation is dramatic:

Linear (no interpolation)
Hermite interpolation

Both shapes pass through the exact same seven control points (shown in red). The linear version looks like a geometric polygon, while the Hermite version looks organic and natural.

The key insight: we're not adding more points to make it smooth. We're using the mathematical properties of the basis functions to calculate where the curve should go between the points we have.

Computing Tangents with Catmull-Rom

The challenge with Hermite splines is: where do the tangent vectors come from? One elegant solution is Catmull-Rom style tangents, where the tangent at each point is proportional to the vector between its neighbors:

typescript
function computeTangent(
  points: Point[],
  index: number,
  closed: boolean,
  tension: number = 0.5,
): Point {
  const n = points.length;
  let prev: Point, next: Point;

  if (closed) {
    // Wrap around for closed curves
    prev = points[(index - 1 + n) % n];
    next = points[(index + 1) % n];
  } else {
    // Clamp to endpoints for open curves
    prev = index > 0 ? points[index - 1] : points[index];
    next = index < n - 1 ? points[index + 1] : points[index];
  }

  return {
    x: (next.x - prev.x) * tension,
    y: (next.y - prev.y) * tension,
  };
}

The tension parameter controls curve tightness:

  • Lower values (0.2-0.3) create tighter curves that hug the control points
  • Higher values (0.5-0.7) create looser, more flowing curves

Generating a Complete Path

To create a smooth closed curve through multiple points:

typescript
function hermiteClosedPath(
  points: Point[],
  samplesPerSegment: number = 8,
  tension: number = 0.5,
): string {
  const n = points.length;

  // Compute tangents for all points
  const tangents = points.map((_, i) =>
    computeTangent(points, i, true, tension),
  );

  // Generate interpolated points
  const allPoints: Point[] = [];

  for (let i = 0; i < n; i++) {
    const p0 = points[i];
    const p1 = points[(i + 1) % n];
    const m0 = tangents[i];
    const m1 = tangents[(i + 1) % n];

    for (let j = 0; j < samplesPerSegment; j++) {
      const t = j / samplesPerSegment;
      allPoints.push(hermiteInterpolate(p0, p1, m0, m1, t));
    }
  }

  // Build SVG path
  let path = `M${allPoints[0].x.toFixed(1)},${allPoints[0].y.toFixed(1)} `;
  for (let i = 1; i < allPoints.length; i++) {
    path += `L${allPoints[i].x.toFixed(1)},${allPoints[i].y.toFixed(1)} `;
  }
  return path + "Z";
}

When to Use Hermite Interpolation

Hermite splines are ideal when:

  • You need curves that pass exactly through control points
  • You want C1 continuity (smooth first derivatives) at joints
  • You're chaining multiple curve segments together
  • You need control over the direction the curve enters/exits each point

They're commonly used for:

  • Animation paths and easing functions
  • Procedural shape generation
  • Font rendering
  • Camera paths in 3D graphics
  • Bezier curves: Control points influence the curve but don't lie on it
  • B-splines: Smoother (C2) but points don't lie exactly on the curve
  • Catmull-Rom splines: A specific type of Hermite spline where tangents are automatically computed from neighbors

See Also