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.
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
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.
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
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.
Positions match. The path doesn't break apart. That's all.
Tangents match too, no visible corner. This is what "smooth node" means in every vector editor.
Curvature matches. No crease in a highlight, no jump in lateral acceleration. This is the rung the eye is actually auditing.
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.
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
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.
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
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.
| Case | Events | Turn error | Arm drift | Residual | Length | Time |
|---|---|---|---|---|---|---|
| Two tangent arcs, R150→R48 | 1 curvature step | 0.072° | 0.039 px | 0.042 | 311.06 → 311.05 | 137 ms |
| Hard 90° corner | 1 corner | 0.000° | 0.036 px | 0.001 | 300.00 → 289.05 | 98 ms |
| Hand-drawn open path | 1 step + 1 corner | 0.029° | 0.068 px | 0.019 | 540.14 → 539.56 | 143 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%.
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.
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.
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.
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.
Large doses on small concave features can make the path cross itself. We don't currently detect it. Back the slider off.
Smoothing is global. You cannot yet say "round this vertex hard, leave that one alone." Per-vertex control is designed and not built.
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.
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.
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.
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
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.
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.
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.