Ramer–Douglas–Peucker for Cleaner SVG Paths
Simplify an open SVG stroke with deterministic finite-segment geometry, then inspect retained corners, sampled error, length, and topology before export.
Ramer–Douglas–Peucker can turn a noisy sampled gesture into a compact SVG path, but epsilon is an artistic decision disguised as a distance. This tutorial makes every retained point, measured deviation, and topology warning visible before export.
Ramer–Douglas–Peucker turns tolerance into authorship
Ramer–Douglas–Peucker accepts an ordered polyline and removes interior points whose deviation from a replacement segment does not exceed epsilon. That sounds like cleanup, but epsilon decides which tremors, corners, and narrow gestures survive. For a creative coder, the tolerance is an editorial control expressed in coordinate units.
The method is useful after sampling and before export. It does not smooth a stroke, infer Bézier handles, close a contour, or make two paths compatible for animation. SVG path morphing needs structural alignment after shapes have been chosen. This SVG path simplification guide owns the earlier question: how can an already sampled open stroke become smaller without hiding what the simplification removed?
A point count alone is a poor verdict. Fewer vertices can reduce payload and make editing calmer while also flattening a leaf tip or changing a crossing. The useful artifact is a contact sheet containing the original ghost stroke, simplified edition, retained original indices, sampled deviation, length change, intersection change, equality rule, tie rule, and an explicit accept or reject note.
The downloadable lab keeps that edition reproducible. It accepts bounded integer points and a numeric epsilon, executes a stable stack algorithm, and exports accessible SVG plus canonical JSON. Its measurements describe the submitted samples. They do not prove a global Hausdorff or Fréchet bound over an unknown continuous source curve.
- Keep endpoints 0 and 6.
- Measure indices 1–5 against finite segment 0–6.
- Index 3 has the greatest distance, 196 units.
- Because 196 is strictly greater than epsilon 80, retain index 3.
- Push ranges 0–3 and 3–6; original indices remain identities.
Measure a point against a finite segment
The distance test is from an interior point P to the finite segment AB, not to the infinite line through A and B. Compute the projection parameter t = dot(P−A, B−A) ÷ |B−A|², clamp t to the interval [0,1], then measure from P to A + t(B−A). Clamping matters when the closest point lies beyond an endpoint.
If A and B coincide, the denominator is zero. The correct bounded behavior is the Euclidean distance from P to that single endpoint. Duplicate consecutive points and all-identical fixtures therefore remain ordinary inputs instead of producing NaN. An independent mutation replaces the finite-segment oracle with an infinite-line distance and fails on a point whose projection lies outside the segment.
Epsilon uses the same coordinates as the stroke. A tolerance of four means four SVG user units in a viewBox or four plotter units in a machine-space export, depending on the declared contract. That epsilon tolerance must travel with the coordinate system. Scaling every point by three while scaling epsilon by three should preserve retained indices. Translation should preserve them without changing epsilon. Both invariants are tested.
Ramer–Douglas–Peucker compares the farthest measured distance strictly against epsilon. A point exactly on the threshold is discarded under this edition's frozen rule. Replacing the greater-than comparison with greater-than-or-equal changes output and is treated as a version-breaking mutant. The visible receipt names “strict-greater-v1,” so a future implementation cannot quietly move the boundary.
Keep the farthest point, then recurse
Begin with the first and last points as a candidate segment. Measure every interior point against that finite segment. If the maximum distance is strictly greater than epsilon, retain the farthest point and repeat the operation on the left and right ranges. If it is not, the endpoints replace the whole range.
The lab uses an explicit stack instead of recursive JavaScript calls. Each frame stores original start and end indices. When two points share the same maximum distance, the lower original index wins. Frames are pushed in reverse order so the left range is processed first, but the final result is always sorted by original index. These rules turn an algorithm sketch into stable bytes.
For a six-point fixture, endpoints 0 and 5 define the first chord. Suppose indices 2 and 3 tie at distance 12. The lower-index rule retains 2, splitting the problem into 0…2 and 2…5. Every later decision references original indices; filtered-array positions never become identities. Figure 1 repeats this anatomy with a seven-point gesture and an ordered semantic list.
The classic algorithm can take quadratic time on adversarial orderings. Hershberger and Snoeyink's technical report analyzes Douglas–Peucker behavior and faster approaches in line generalization. This browser edition caps input at 512 points and checks a declared work estimate before processing. Ramer–Douglas–Peucker is deterministic here because boundaries, equality, ties, and work limits are part of the interface.
Read epsilon as a contact sheet
One tolerance rarely communicates enough. A contact sheet renders the same stroke at epsilon 0, 4, 10, and 22 with identical framing. Each panel shows original sample dots, retained vertices, simplified segments, point count, measured maximum sampled deviation, length change, and an editorial verdict. The visual rhythm becomes comparable instead of relying on memory.
At epsilon zero, collinear and duplicate points may disappear because their distance is not strictly greater than zero, while genuine nonzero deviations remain. At a small tolerance, hand jitter falls away and major direction changes survive. At a medium tolerance, the gesture may become confident. At a large tolerance, a legal two-point result can become compositionally dead.
Point reduction is not monotonic evidence of quality, even though retained counts are nonincreasing as epsilon grows for a fixed deterministic implementation. Length usually decreases when corners are removed, but that scalar cannot name which feature vanished. The paired drawing supplies the missing review surface.
Flow-field calligraphy creates strokes from a vector field, while this process edits samples after generation. Pen-plotter hatching adds physical mark and route constraints that may justify keeping additional corners. Ramer–Douglas–Peucker should feed those workflows with an edition receipt, not replace their artistic or machine-specific judgment.
| Epsilon | Retained points | Max sampled deviation | Length change | Editorial verdict |
|---|---|---|---|---|
| 0 | 15 | 0 | −1.2% from duplicates | keep detail |
| 4 | 10 | 3.6 | −3.8% | accept |
| 10 | 6 | 8.8 | −8.9% | review leaf tip |
| 22 | 3 | 18.4 | −21.7% | reject silhouette |
Reading rule: labels, markers, and the table carry every conclusion; color is supplementary.
Measure what the algorithm does not guarantee
The implemented maximum-deviation metric walks every original sample and finds its nearest simplified segment. That is a useful sampled-vertex-to-polyline audit, but it is not the full Hausdorff distance between two continuous curves. It is also not Fréchet distance, which respects traversal along curves, and it does not prove that the simplified result uses the minimum possible number of vertices.
The distinction matters when samples are sparse or uneven. A continuous source curve could bow between recorded points, yet the receipt only knows the submitted coordinates. The cited curve simplification paper discusses optimality and distance-model boundaries that should not be collapsed into the classic heuristic's local rule.
Topology is another separate audit. An open polyline can self-intersect. Removing a narrow feature may erase a crossing, or a replacement segment may introduce one. The lab counts pairwise intersections between nonadjacent segments before and after simplification and emits a warning when counts differ. It does not claim a topology-preserving algorithm.
Figure 3 deliberately shows two mathematically legal but editorially rejected results: a narrow spike disappears, and a crossing count changes. Hatching and numbered markers carry the difference without depending on color. Closed paths are excluded because duplicated endpoints, cyclic start choice, and wraparound adjacency require another contract. Ramer–Douglas–Peucker in this article is strictly an open-polyline edition tool.
Export honest SVG path data
A simplified open polyline becomes path data with one M command followed by L commands in retained order. It does not receive a closing Z. Coordinates stay as full validated numbers during measurement and simplification; deterministic decimal formatting occurs only when bytes are exported. Rounding earlier can move a point across the epsilon boundary.
The standalone SVG includes a viewBox derived from bounded coordinates with padding, a visible original ghost path, the simplified path, retained-point markers, title and description elements, and text metadata. The lab UI keeps a semantic table of retained indices and coordinates. An SVG image may be visually accessible while still needing adjacent article text, so both representations remain available.
The SVG 2 paths specification defines path commands and path-data grammar. It does not choose epsilon, guarantee topology, or certify a plotter workflow. The receipt therefore separates standards-backed serialization from the authored simplification decision.
Exported JSON preserves normalized points, epsilon, algorithm version, equality and tie-break versions, input hash, retained indices, point and length metrics, sampled deviation, intersection counts, warning flags, and SVG hash. Replaying the same fixture produces byte-for-byte identical SVG and JSON. Ramer–Douglas–Peucker becomes publishable when the final line and the evidence for making it are both portable.
| Case | Before | After | RDP guarantees | Separate audit |
|---|---|---|---|---|
| Narrow spike | visible apex | straight baseline | sample tolerance only | silhouette verdict: reject |
| Open loop | 1 crossing | 0 crossings | sample tolerance only | intersection warning |
| Vertex count | 5 | 2 or 3 | endpoints retained | not minimum-vertex proof |
Reading rule: labels, markers, and the table carry every conclusion; color is supplementary.
Test degenerate and adversarial strokes
A two-point stroke must preserve both endpoints. A straight stroke should collapse to endpoints at zero epsilon because no interior distance is strictly greater than zero. Duplicate consecutive points and an all-identical stroke must not produce NaN. A V corner and narrow spike should expose the tolerance boundary. A looped open stroke should exercise intersection counting.
Exact-epsilon tests freeze the strict comparison. Equal-distance tests freeze the lower-index tie. Translation and proportional scaling test geometric invariants. Maximum input tests confirm the 512-point cap without overflowing the document. Malformed rows, non-integers, non-finite values, coordinates outside −4096…4096, too few points, and excess work are rejected before rendering.
The independent oracle computes finite-segment distances, retained indices for small fixtures, polyline lengths, maximum sampled deviation, and segment intersections without calling the UI renderer. Mutation fixtures fail infinite-line distance, missing endpoints, greater-than-or-equal equality drift, reverse ties, and pre-simplification rounding. Deterministic export is checked by running the same normalized input twice and comparing bytes.
Bézier plotter curvature studies curve construction and machine-aware curvature after a path representation is chosen. It should not be used as evidence that this polyline simplification preserves a smooth source. Ramer–Douglas–Peucker has a narrow promise: remove samples under declared finite-segment tolerance while retaining a reviewable history.
Publish the simplified edition
The final proof sheet should name the original point source, coordinate system, epsilon, strict equality rule, lower-index tie rule, retained indices, length change, sampled deviation, intersection change, algorithm version, input hash, SVG hash, and visual verdict. If a feature matters, say which retained index protects it rather than describing the result as merely cleaner.
Reject output when the topology warning conflicts with the illustration's meaning, a signature corner disappears, or the plotted scale makes the chosen epsilon nonsensical. Accept it when the smaller path preserves the intended rhythm under both visual review and the declared metrics. Mathematical legality is necessary, not sufficient, for an edition.
Revisit this Ramer–Douglas–Peucker guide on 2027-02-02, or sooner if SVG path rules change, a source or linked route breaks, search intent shifts toward excluded closed paths or Bézier fitting, a new adversarial fixture changes topology, or responsive rendering clips the semantic equivalent. Update code, figures, exported examples, and hashes together.
The meaningful result is not the lowest point count. It is a compact, accessible stroke whose removed information is visible enough for a designer to defend.
Runnable local artifact — Reported error is original sampled vertices to the simplified polyline; the lab does not prove global Hausdorff or Fréchet distance, minimum vertices, smoothing, fitting, or topology preservation.
Parse 2–512 bounded integer points, measure finite segments, keep endpoints and stable lower-index ties, audit sampled deviation and intersections, then export accessible SVG and canonical JSON.