← Back

Bézier Curve Fitting

Least-squares fitting of cubic Bézier segments to a point sequence. Used by Potrace and Inkscape to convert traced outlines into smooth curves.

How it works

After simplification leaves a sparse polyline, curve fitting replaces straight segments with cubic Bézier curves — each defined by four control points: two endpoints and two interior handles. The goal is to find handles that minimise the sum of squared distances between the curve and the original points.

The standard approach is the Schneider algorithm (as described in Graphics Gems): parameterise the input points along the chord, solve for the two handle positions via a 2×2 linear system, then check the maximum error. If error exceeds a threshold, split at the worst point and recurse.

function fitCurve(points, error): // Chord-length parameterise the input u = chordLengthParam(points) // Solve least-squares for the two handles [P1, P2] = generateBezier(points, u) // Find the point with maximum deviation [maxErr, splitIdx] = maxError(points, bezier(P0,P1,P2,P3), u) if maxErr < error: return [bezier(P0, P1, P2, P3)] // good enough — one segment else: // split and fit two sub-segments recursively return fitCurve(points[0…splitIdx], error) + fitCurve(points[splitIdx…end], error)
Tangent estimation. At each end of a segment the algorithm estimates a tangent direction from the first (or last) few points. This constrains the handle to lie along that tangent, ensuring continuity at segment joins — the curve looks smooth where segments meet.

Cubic Bézier explorer

A cubic Bézier is defined by four control points. Drag any point to see how the curve and its parameterisation change. The small tick marks show equal parameter steps — bunching means the curve slows down there.

Drag any control point. Dashed lines connect endpoints to their handles.

Fitting to sampled points

Draw a freehand path or use the generated one below. The fitter finds the minimum number of Bézier segments needed to stay within the error threshold.

8 px

Draw on the canvas, or press Reset to use the sample path.

Step-by-step

Watch the algorithm try a single segment, measure the error, then split and recurse. The amber point is the current worst-error candidate for splitting.

Step 0 / 0
Press Next to begin.