clline

Curvature, and what we do to it

Every stand on this page is drawn from real output of the engine that runs the app and the Figma plugin. Nothing here is a mock-up, and every number is measured, including the ones that are bad.

A one-person lab. The work is curvature-continuous editing of vector paths: the part of a curve that nobody draws and everybody sees.

01 · the problem

Your eye reads the second derivative

Below are two circular arcs joined end to end. Same point, same tangent, a textbook smooth join. Scroll past it in a logo and you would never call it a defect.

It is a defect. The left arc has radius 150, the right one 48. At the join the rate of turning jumps three times over, instantly. On a flat black outline that is invisible. Put a highlight on it, extrude it, letterpress it, mill it, or just stroke it with a thick brush, and a crease appears exactly there, a line nobody drew.

Stand 01 · two tangent arcs, R150 → R48

Tangent-continuous (G1), curvature-discontinuous (not G2). κ jumps 0.0067 → 0.0208 at one single point. The outline hides it; the comb cannot.

This is why curvature is not an academic nicety. It is the first quantity in the chain that the eye can detect but the drawing cannot show you.

02 · the instrument

What a curvature comb actually is

At every point of a curve there is exactly one circle that hugs it best, the osculating circle. Its radius R tells you how hard the curve is bending there. Curvature is simply κ = 1/R. A straight line has R = ∞, so κ = 0. A tight hairpin of radius 5 has κ = 0.2.

A comb draws that number as a hair. At each sampled point you stand on the curve, turn 90°, and walk out a distance proportional to κ. Bending left, you walk one way; bending right, the other. Join the tips and you get the envelope: a graph of curvature, bent around the shape it belongs to.

How to read one in three seconds

  • No hairs. That stretch is dead straight.
  • Long hairs. Tight bend. Longest hair = tightest point of the shape.
  • Hairs flip to the other side. An inflection. The curve changed which way it bends. Unavoidable in an S; suspicious anywhere you didn't mean to put one.
  • A smooth envelope. The surface will read as one piece.
  • A cliff in the envelope. A crease. This is the defect in Stand 01.
  • A sawtooth envelope. Ripple. Usually a hand-drawn path with too many nodes; every node is a tiny crease and together they shimmer.

Designers who already use combs know this instrument from industrial CAD. Alias, Rhino, CATIA have had curvature analysis for decades, because a car body with a curvature cliff shows it in the reflection on the showroom floor. In 2D vector tooling the instrument exists; what barely exists is a tool that fixes what the instrument shows you.

Stand 02 · the same join, before and after

shape + comb
κ ×10⁻³ vs arc length
original smoothed original outline (ghost)
Right-hand panel is the honest one: curvature plotted against distance travelled along the path. The original is two flat shelves with a vertical wall between them. The smoothed version is one continuous ramp, 69.4 units of arc long, and the path still delivers the same total turn: 179.93° against a true 180°, an error of 0.07°. The outline moved by at most 0.04 px outside the ramp.
03 · vocabulary

The continuity ladder, and where tools stop

There is a standard ladder for how smoothly two pieces of curve can meet. Most vector software gives you the first two rungs and calls it done.

G0

Positions match. The path doesn't break apart. That's all.

G1

Tangents match too, no visible corner. This is what "smooth node" means in every vector editor.

G2

Curvature matches. No crease in a highlight, no jump in lateral acceleration. This is the rung the eye is actually auditing.

G3

The rate of change of curvature matches as well. Matters for mirror-finish surfaces and for motion, where it is literally jerk.

Our windows are built to be curvature-continuous and curvature-derivative continuous across their own joins: the blending profile is chosen so that its value and its first two derivatives all land on zero at both ends. Whether the whole path ends up G3 depends on what the original was doing outside the window, which we deliberately do not touch.

04 · why rounding is not smoothing

A fillet moves the problem, it doesn't remove it

The standard answer to a hard corner is a fillet: cut the corner back and splice in a circular arc. Illustrator's Round Corners, every CAD fillet, the corner radius in your CSS. All the same move.

A circular arc has constant curvature. So the curvature profile of a filleted corner is a rectangle: zero on the straight, then an instantaneous jump up to 1/R, flat across the arc, then an instantaneous jump back to zero. You started with one defect at the corner. You now have two, one at each end of the fillet. They are smaller and further apart, which is why a fillet is usually good enough, and why on a reflective surface, or in a thick stroke, or under an extrusion, you can still see exactly where the arc starts.

Below: the same 90° corner, cut back by the same 45.08 units of arc length, done both ways.

Stand 03 · 90° corner: circular fillet vs. curvature window, equal setback

shape + comb
κ ×10⁻³ vs arc length
circular fillet, R = 45.08 curvature window
The fillet's graph is a box: two curvature jumps of 0.0222 each, and the comb shows them as two hard shoulders where the hairs appear out of nowhere. Ours peaks higher (0.0604, a radius of 16.6 instead of 45.1) but it arrives there and leaves continuously, with zero jumps, over 79.2 units of support. Both deliver exactly 90.000° of turn. That trade is the whole idea: concentrate more curvature in the middle so that nowhere does it have to step.

Civil engineers settled this argument a century ago. You cannot run a railway from a straight into a curve: the lateral acceleration would step from zero to full in one sleeper. They insert a transition curve where curvature rises gradually, and they accept the consequence: the circular part of the track has to shift sideways to make room. The same shift happens here, and for the same reason. A smoothed corner is not the old corner with softer edges; it is a slightly different path that goes the same way.

05 · the approach

We don't move handles. We work in the curvature domain.

Almost every smoothing tool you've used is a handle tool. It nudges Bézier control points (by heuristic, by least-squares fit, by hand) and hopes the resulting curvature comes out acceptable. Curvature is the output of the process, never its subject.

We invert that. The pipeline, honestly and in order:

  • Read. The path is parsed to cubic Béziers: arcs, quadratics, lines, relative commands, transforms, groups and all. True cusps are detected and split out, because a cusp is a different animal from a corner and has to be handled separately.
  • Measure. We build an arc-length chain: a map from distance travelled along the path to a point on it. Everything from here on is expressed in s, real measured distance, never in Bézier parameter t. That distinction turns out to matter enormously, see §08.
  • Find the events. Two kinds. A corner is a jump in direction. A step is a jump in curvature with no jump in direction. Stand 01, the invisible one. Both are treated as the same kind of object: a discontinuity sitting at one value of s.
  • Leave geometry. We stop thinking about x and y and look only at the function κ(s), curvature against distance. In this domain a corner is not a corner. It is a dose of turning, delivered instantaneously: an infinitely tall, infinitely narrow spike whose area is exactly the corner's angle.
  • Replace the spike with a bell. This is the core of the method. Inside a window around the event, the original κ(s) is replaced by an analytic profile shaped like a bell: it rises, peaks and falls, with no plateau anywhere, and it has compact support, so outside the window nothing changes at all. The bell's area is set equal to the spike's area, so the total turn is preserved exactly: the path still points the same way when it comes out the far side, and a closed shape still closes. At the two ends of the window the profile meets the untouched original curvature in value and in slope, so the window doesn't introduce new steps of its own while removing the old one.
  • Integrate back. Curvature integrates to heading, heading integrates to position. We do that numerically, pinned at the window's entry point, and absorb the residual inside the window rather than letting it leak down the path. That is why the parts of the shape you did not ask us to touch move by hundredths of a pixel instead of drifting.
  • Re-fit. Finally the result is fitted back to cubic Béziers, G2 at the joins where a G2 solve exists and G1 where it doesn't, splitting a span only when the position error demands it. You get a normal, editable path, not a 4000-point polyline.

What we are not publishing

The exact bell profile, how its parameters are solved against the window, how the window's two setbacks are balanced on curved arms, and how collisions between neighbouring windows are resolved. Those stay in the engine. What is above is the honest shape of the method, not a recipe.

For the record. Working directly in the curvature domain (detecting Béziers, converting to κ(s), replacing event neighbourhoods with analytic bell-shaped doses of turning, integrating back, re-fitting) is how this engine has worked since 2026, in production, in a published Figma plugin and in the web app on this domain. Writing it down here so the date is on record.

Stand 04 · the same idea on a hand-drawn path

shape + comb
κ ×10⁻³ vs arc length
Three segments, two events: a curvature step where κ crosses from +0.0082 to −0.0123 without any kink, and a 29.05° corner where the last curve meets a straight line. Both become continuous. Turn delivered: 45.03° against a true 45.00°. Untouched stretches moved by ≤ 0.07 px. Total time: 143 ms.
06 · receipts

What it does, measured

Three cases, run through the same engine that serves the app. Turn error is how much net direction the output lost or gained against the exact value: the quantity that decides whether a closed shape still closes. Arm drift is the largest distance any point outside the smoothing window moved. Residual is the error the window had to hide inside itself.

CaseEventsTurn errorArm drift ResidualLengthTime
Two tangent arcs, R150→R481 curvature step 0.072°0.039 px0.042 311.06 → 311.05137 ms
Hard 90° corner1 corner 0.000°0.036 px0.001 300.00 → 289.0598 ms
Hand-drawn open path1 step + 1 corner 0.029°0.068 px0.019 540.14 → 539.56143 ms

The corner case loses 3.6% of its length. That is not an error. It is the setback. You asked for the corner to be cut; cutting a corner shortens the path. The other two hold length to within 0.1%.

07 · the part nobody writes

Where it still fails

Every item below is reproducible in the app right now. They are listed because a tool that claims curvature continuity and doesn't tell you where it loses it is worth less than one that does.

Out of band

At a junction between a straight and a circular arc, the smoothed curvature can leave the interval between the two end values instead of staying monotone inside it. On a stadium shape we measure a −7.1% dip before the arc and a +6.3% overshoot inside it. The path is still G2 (no jumps) but it is not monotone, and on a clean geometric primitive that is visible to a trained eye. We know the cause and we know a geometric construction that solves it exactly; the solver for it is not fast or stable enough to ship. It is the open problem.

Overshoot

At a pure curvature step the peak of the smoothed profile overshoots the higher of the two shelves by roughly 3%: in Stand 01, 0.0216 against a shelf of 0.0210. Same root cause as above.

Feature eating

Push the slider to maximum on a small shape and a window can grow until it swallows a nearby curvature peak (a tight loop, a deliberate hairpin) and flattens something you meant to keep. There is a cap that stops the window at the nearest curvature extremum of each arm, but the cap is a heuristic with a floor, not a guarantee.

Self-intersection

Large doses on small concave features can make the path cross itself. We don't currently detect it. Back the slider off.

One slider

Smoothing is global. You cannot yet say "round this vertex hard, leave that one alone." Per-vertex control is designed and not built.

Cusps stay cusps

A true cusp, where the curve reverses direction and speed passes through zero, is split out and left as a cusp. We will not quietly turn it into something else, because usually it's there on purpose.

Refit is an approximation

The final Bézier fit has bounded position error, not zero error. If you need the exact smoothed geometry, take the point export instead of the path export.

Not real-time

100-150 ms for a path of a few hundred units with a handful of events. Fine for a slider. Not a 60 fps solver for a thousand nodes.

08 · motion

Even steps along the path, and why t lies to you

This section is for anyone who moves something along a curve rather than looking at it: motion designers, animators, gamedev, CNC and plotter people, robotics, anyone writing a spline follower.

A cubic Bézier is parameterised by t from 0 to 1. It is extremely tempting to treat t as progress along the curve. It is not. t is an internal coordinate of the polynomial, and the speed |dP/dt| varies along it. A lot. Step t evenly and your object crawls through the bends and sprints down the straights, for no reason that any animator asked for.

Stand 05 · 26 samples on the same curve, two ways

sample positions
gap between samples, units
uniform in t uniform in arc length
Same 458.24-unit path, same 26 samples. Stepping t evenly gives gaps from 12.84 to 30.09: the object travels 2.34× faster at one moment than another. Stepping by measured arc length gives 17.74 to 18.33, a spread of 3.3%, and that remainder is not error: it is the chord of an arc being shorter than the arc itself, exactly where the curve bends hardest.

Where this bites in practice

  • Tweening along a motion path. Constant-speed travel needs constant arc length per frame. Easing should be a deliberate choice, not an artefact of the parameterisation.
  • Camera rails and dollies. A camera on a t-sampled spline has a velocity curve nobody authored. Worse, if the path is only G1, the camera gets a step change in lateral acceleration at every node. That is the snap you feel and can't find.
  • Spline followers in games. Patrol routes, rail shooters, vehicle paths, track splines. Arc-length reparameterisation is the standard fix and it is routinely skipped because measuring the length is annoying.
  • Particles, trails and dashes. Emit-along-path, dash patterns, stroke reveal animations, text on a path. All of them distribute by distance, all of them look wrong when fed t.
  • CNC, laser, plotter, pen. The machine's feed rate is distance per unit time. Uneven spacing becomes uneven feed, and a curvature jump becomes a jolt the controller has to decelerate for, which is why toolpath smoothing exists in the first place.
  • Robotics. Same argument with more mass: curvature continuity is what keeps the lateral acceleration profile continuous, and jerk is dκ/ds · v². A path with a curvature step has, formally, infinite jerk at that point.

Both halves of this page are the same statement, which is why they live together. Smooth curvature is what the eye wants when the path is looked at; smooth curvature is what the actuator wants when the path is travelled. And in both cases the path has to be sampled by distance, not by parameter.

Our engine works in arc length from the second step onward, so the point export comes out evenly spaced by construction: you pick the step in units and you get points that far apart, each one carrying its own curvature value.

09 · verification

Don't take our word for it

The app gives you two exports. One is a Bézier path, to paste back into whatever you came from. The other is the raw point list: coordinates plus the curvature value at each point, evenly spaced along the path. Load that into NumPy, into a spreadsheet, into your own comb plotter, and check every claim on this page yourself: that the turn is preserved, that curvature has no jumps, that the untouched stretches didn't move, and where it goes out of band.

That is deliberate. A smoothing tool that only gives you a picture is asking for trust it hasn't earned.

10 · who

A small lab

One person. No company, no funding, no roadmap deck. The interest is narrow and specific: higher-order continuity of planar curves, and making it something you can apply with a slider instead of a dissertation.

The tool does one thing. The ambition is that it does that one thing properly, and that the places where it doesn't are written down in public, which is what §07 is for.

Next on this site: a curvature monitor, the same bench graphs used to develop the engine (κ, dκ/ds, turn accumulation, window boundaries), running in the browser on any path you paste.

Found something broken? Reproduce it in the app, keep the path, and send it over. Every defect on the limits list above was found by eye on a hand-drawn shape, not by a regression suite.