The Problem
Mechanical linkages are pure applied geometry. Chebyshev's lambda mechanism traces an approximate straight line from pure rotation. Peaucellier's linkage produces a mathematically exact straight line — it took 80 years after Watt's parallel motion for someone to prove this was even possible. These mechanisms are beautiful and you can't interact with them on the web.
I wanted to build a playground where you can grab a joint with your mouse and watch constraints propagate in real time. See how coupler curves change shape as you adjust link lengths. Feel the difference between a four-bar and a slider-crank through direct manipulation — not equations, not textbook diagrams.
The Approach
The core is a Position-Based Dynamics relaxation solver — the same philosophy as my cloth simulation, but for rigid linkages instead of fabric. Each link is a bilateral distance constraint between two nodes. Every frame, the solver runs 20 relaxation iterations, splitting corrections equally between connected nodes (or applying full correction to one node if the other is a fixed ground point).
for each link [nodeA, nodeB, targetLength]:
delta = nodeB.pos - nodeA.pos
dist = length(delta)
diff = (dist - targetLength) / dist
correction = delta * diff * 0.5
if both free: A += correction, B -= correction
if A is ground: B -= correction * 2
if B is ground: A += correction * 2Motors drive mechanisms by directly positioning the first connected free node in a circular arc: x = cx + r*cos(angle), y = cy + r*sin(angle). The angle accumulates linearly with time, and the constraint solver propagates that motion through the rest of the linkage. This is the trick that makes it feel like a real mechanism — one controlled input, everything else following from geometry.
I implemented seven preset mechanisms, each chosen because it demonstrates something interesting about linkage kinematics:
A four-bar linkage with Grashof-satisfying proportions (48/160/120/100) — shortest + longest < sum of other two, guaranteeing full crank rotation. A Chebyshev linkage with the classic 2:5:5:4 proportions for approximate straight-line motion. A Peaucellier-Lipkin cell with rhombus topology (140/60/100) for exact straight-line conversion. A Strandbeest leg using Theo Jansen's actual proportions — 8 links, one motor, traces a walking path. Plus slider-crank, scissor lift, and pantograph.
For trace visualization, I record coupler points at arbitrary parametric positions along links (not just at joints) using lerp(nodeA, nodeB, t) where t ranges from 0 to 1. These get smoothed with Catmull-Rom spline interpolation — 4 subdivisions between each raw sample — and rendered with a three-layer compositing approach: a wide glow pass, a thin core line, and a hot white segment for the most recent trace.
Eight procedural material shaders render links with distinct visual identities — chrome (gradient reflections), holo (animated color shift), neon (multilayer bloom), gummy (translucent caustics) — all drawn with Canvas 2D gradient and compositing operations. No WebGL, no textures, just math and layered draws.
The Hard Parts
Stability under adversarial input. Users will drag nodes into configurations that are geometrically impossible — pulling a four-bar linkage past its toggle points, creating near-zero-length degenerate states. A direct solver (Newton-Raphson with a Jacobian) would hit singularities and explode. The relaxation solver handles this gracefully because it's soft — it distributes constraint violations across all links rather than inverting a matrix that's becoming singular.
As a safety net, I clamp velocity to 500 units/frame and positions to ±8000 units. If either limit is exceeded, the node reverts to its previous position. This costs almost nothing and catches the edge cases where even relaxation struggles — like dragging a node at high speed through a fully constrained configuration.
Making it feel rigid despite iterative convergence. With 20 iterations, the solver doesn't fully converge every frame — there's always residual error in the constraint satisfaction. The trick is that the error is distributed evenly and is small enough that the mechanism looks and feels rigid. The user perceives a rigid linkage because the visual error is subpixel.
This only works because PBD's relaxation approach degrades gracefully — it never produces the catastrophic failure modes of direct solvers hitting singular configurations. A mechanism that can't satisfy all its constraints simultaneously just looks slightly soft, never broken.
Coupler curve tracing at parametric positions. The interesting curves in mechanism design aren't traced by joints — they're traced by arbitrary points on the coupler link between joints. Chebyshev's approximate straight line lives at the coupler midpoint (t=0.5). But the full family of coupler curves a four-bar can produce is wild — varying t from 0 to 1 sweeps through figure-eights, cardioids, and loops.
Getting the trace recording right meant sub-sampling (skip points closer than 0.5 units to avoid overdraw), maintaining a rolling buffer of 1,500 points, and applying Catmull-Rom smoothing only at render time so the raw data stays clean for parametric queries.
What I'd Do Differently
The solver doesn't compute degrees of freedom. Gruebler's equation (DOF = 3m - 2c for planar mechanisms) would let me display the mechanism's mobility, detect over-constrained and under-constrained states, and give meaningful feedback when users create impossible topologies. Right now the solver just does its best — fine for play, but misses an educational opportunity.
A Newton-Raphson solver (or at least a hybrid approach) would give tighter convergence for mechanisms that need precision — particularly the Peaucellier linkage where the mathematical exactness of the straight line is the whole point. PBD's soft constraints mean the output is approximate, even if the approximation is visually indistinguishable.
I'd add assembly animation — watching a mechanism construct itself piece by piece, each added constraint visibly reducing the system's degrees of freedom. This would make the relationship between topology and motion intuitive in a way that the finished mechanism alone doesn't communicate.