A differentiable linkage synthesizer.
Draw a path. Get a mechanism whose coupler point traces it.
▶ Live demo · How it works · CI
Burmester synthesizing a four-bar linkage that traces an egg-shaped target curve to within 1.8%
Path-generation synthesis is a classic, genuinely hard problem in mechanism design: you want a linkage — say a four-bar — whose coupler point (a point rigidly fixed to the floating link) traces a prescribed curve as the input crank turns. A straight line for a film advance. A D-shape for a knee. A figure-eight for a mixing blade. Ludwig Burmester laid the geometric groundwork for this in 1888, and it is still not easy.
Burmester (the tool) lets you freehand-draw the target curve and then solves for the entire mechanism — every ground-pivot coordinate, every link length, the coupler-point offset, the crank phase — by gradient descent through a fully differentiable kinematic model, wrapped in multi-start basin-hopping to escape local minima. It runs in the browser, off the main thread, and shows you the mechanism converging onto your drawing in real time.
The autodiff engine, the position solver, and the optimizer are all written from scratch in dependency-free TypeScript (~2k lines). The only runtime primitive is the browser's 2-D canvas.
A Stephenson-III six-bar linkage synthesized to trace a heart-shaped curve
A Stephenson-III six-bar tracing a heart — the extra loop buys curl a four-bar cannot produce.
| Tool | Simulate | Synthesize a path | Target input | Method |
|---|---|---|---|---|
| PMKS+ / MechAnalyzer / Linkage (Rectorsquid) | ✅ | ❌ | — | forward kinematics only |
| GIM, SAM, classical Freudenstein | ✅ | click points | solve polynomial systems | |
| DiffTaichi / Brax / Warp | ✅ | ✅ (rigid/soft dynamics) | code | differentiable physics, Python, research |
| Burmester | ✅ | ✅ whole freehand curve | draw it | end-to-end differentiable position analysis + Adam + basin-hopping, in the browser |
Precision-point methods hit a handful of exact points and let the curve do whatever it likes in between. Differentiable-physics frameworks target integrated dynamics, not closed-loop position synthesis, and live in Python notebooks. A browser-native tool that differentiates straight through an unrolled Newton position solver to fit a hand-drawn curve did not appear to exist. That's the contribution.
1. Position analysis (the assembly problem). For a given crank angle θ and a
set of design parameters, the moving joints sit wherever every rigid bar keeps
its length. Stack the unknown joint coordinates into q and each bar (a,b,L)
contributes one equation
g_k(q) = |p_a − p_b|2 − L2 = 0
For a well-constrained 1-DOF linkage there are exactly 2·(#free joints)
equations, so the system is square and solved by Newton–Raphson with an analytic
Jacobian, warm-started from the previous crank angle to stay on one assembly
circuit.
2. Differentiating through the solver. Unrolling every Newton iteration onto
the autodiff tape is wasteful and noisy. Instead we converge in plain floats,
then take exactly one Newton step under automatic differentiation from the
converged point q*:
q_diff = q* − J(q*, p)−1 g(q*, p)
with q* held constant and the parameters p live. Since g(q*, p) ≈ 0 in
value but not in derivative, this reproduces the implicit-function-theorem result
dq/dp = −J−1 (∂g/∂p) in a single small linear solve — cheap, exact to first
order, and stable. (Full derivation in docs/THEORY.md.)
3. The loss. The traced coupler curve is aligned to the target by a closed-form similarity fit (Procrustes + a few ICP iterations, re-fit every iteration) and compared with a symmetric Chamfer distance — no correspondence or arc-length matching needed, so it copes with open curves, uneven crank speed, and self-intersections. Soft penalties keep the linkage Grashof (input crank fully rotating), keep link lengths sane, and stop the curve collapsing to a point. Every term is a differentiable scalar; one backward pass gives the gradient with respect to all ~10 (four-bar) or ~16 (six-bar) parameters.
4. The optimizer. From-scratch Adam with gradient clipping and cosine learning-rate decay, inside multi-start basin-hopping: sample several random feasible linkages, polish each, then perturb-and-repolish the incumbent. Seeded RNG, so a given seed reproduces a given mechanism.
npm install
npm run dev # http://localhost:5173Then:
- Draw a target on the canvas (or pick one from the gallery).
- Choose Four-bar or Six-bar and a quality level.
- Hit Synthesize and watch it converge.
- Drag any pivot to explore; Export the mechanism as JSON or an SVG.
npm test # 33 tests: gradient checks, kinematics, mechanism recovery npm run build # production bundle in dist/ npm run typecheck
The core is UI-free and works in Node:
import { synthesize } from "./src/synthesis/optimize.js"; const target = [/* [x, y] points, open or closed */]; const result = synthesize(target, { topology: "fourbar", // or "stephenson3" seed: 7, restarts: 8, }); console.log(result.placedSpec); // the mechanism, positioned on the target console.log(result.grashof.crankRotates); // does the input crank fully rotate? console.log(Math.sqrt(result.parts.chamfer)); // fit error, in target units
Lower-level pieces are exported too:
import { Value, checkGrad } from "./src/autodiff/index.js"; // reverse-mode AD import { fourBar, grashof } from "./src/kinematics/factories.js"; // build a mechanism import { traceCoupler } from "./src/kinematics/trace.js"; // trace its coupler curve
src/
autodiff/ Value (reverse-mode AD scalar), Vec2, gradient + finite-difference checks
kinematics/ mechanism model, Newton position solver, differentiable trace, factories
synthesis/ curve alignment, Chamfer objective, Adam + basin-hopping
app/ canvas renderer, pointer interaction, worker, control panel
docs/ THEORY.md and figures
- Planar, single-DOF, kinematics only. No spatial linkages, no multi-input mechanisms, no forces or dynamics.
- Local minima. Basin-hopping mitigates but cannot eliminate them. Different seeds find different mechanisms; some targets a four-bar simply cannot trace well (try the six-bar, or accept the closest fit).
- Assembly-mode discontinuities. When a linkage passes through a dead configuration the coupler curve can jump between circuits. The tracer detects and reports this; it does not fully solve it.
- The similarity alignment means the synthesized mechanism reproduces the shape of your drawing at whatever position and scale fits best. Turn off "allow rescaling" to pin the drawn size.
- L. Burmester, Lehrbuch der Kinematik (1888).
- R. S. Hartenberg & J. Denavit, Kinematic Synthesis of Linkages (1964).
- J. M. McCarthy & G. S. Soh, Geometric Design of Linkages, 2nd ed. (2011).
- F. Freudenstein, "Approximate synthesis of four-bar linkages" (1955).
- D. P. Kingma & J. Ba, "Adam: A Method for Stochastic Optimization" (2014).
MIT — see LICENSE.