Forget springs - use constraints
An obvious way to simulate a rope is a chain of masses connected by stiff springs: the spring force pulls neighbouring points back toward a rest distance. In practice this is a bad idea. A spring stiff enough to feel like an inextensible rope also demands a tiny time step to stay numerically stable, and any reasonable frame budget will make the rope visibly bouncy or explode outright. The alternative that every modern engine actually uses treats the links as hard geometric constraints - "these two points must be exactly d apart" - and enforces them directly on position, with no force or stiffness parameter at all.
Verlet integration: position remembers velocity for free
Each rope node is stored as just a current position and a previous position, with no explicit velocity variable. Position Verlet advances a point using only those two positions plus acceleration, and the implied velocity is simply the difference between them divided by the time step. That property is what makes the constraint solver simple: to satisfy a distance constraint you just move the current position - the "velocity" updates itself automatically on the next step, with no separate bookkeeping.
// per node, per frame newPos = pos + (pos - oldPos) * damping + acceleration * dt * dt oldPos = pos pos = newPos
Jakobsen relaxation: satisfy every link, again and again
Thomas Jakobsen's 2001 method (used in the original Hitman engine) solves the whole chain of constraints with brute simplicity: loop over every link, and for each one push the two connected points directly apart or together until they sit exactly the rest distance d apart, splitting the correction between them by their inverse mass. Do this for every link, then repeat the whole pass several times per frame. Each pass only partially resolves the tangle of overlapping constraints, but a handful of iterations converges quickly enough to look completely rigid at real-time frame rates.
for iteration in 1..N: // e.g. N = 8-15 passes per frame
for each link (a, b, restLen):
delta = b.pos - a.pos
dist = length(delta)
diff = (dist - restLen) / dist
a.pos += delta * 0.5 * diff * a.invMass / (a.invMass + b.invMass)
b.pos -= delta * 0.5 * diff * b.invMass / (a.invMass + b.invMass)
Pinning an end of the rope is trivial in this framework: give that node an inverse mass of zero, and every correction that would move it is redirected entirely onto its neighbour instead. The same trick pins a rope to a moving hand, wraps it around a peg by adding extra constraints against the peg's surface, and turns the exact same code into cloth simply by adding a 2D grid of links instead of a single chain.
The catenary: what a rope looks like at rest
Left to hang under its own weight between two fixed points, a real rope or chain settles into a specific curve called a catenary (from the Latin for chain): y = a·cosh(x/a), where a is set by the rope's weight per unit length and the horizontal tension. It looks like a parabola but is not one - the difference is that a chain's weight is distributed per unit of its own arc length, while a parabola is the correct shape only when the load is distributed per unit of horizontal span, as with a suspension bridge deck hanging from vertical cables. A well-tuned rope simulation reproduces the catenary shape naturally, simply from gravity acting on the particles plus the distance constraints - nobody needs to hard-code the cosh formula.
Frequently asked questions
Why does a simulated rope stretch instead of staying rigid?
Because the distance constraints are solved with a limited number of relaxation passes rather than exactly. Each pass only partially corrects the error between two linked points, so with too few iterations the rope behaves like a stiff spring instead of an inextensible cord. Raising the iteration count (or applying the same pass more than once per frame) tightens it toward true rigidity, at the cost of more computation.
Is a hanging chain the same shape as a parabola?
No, although the two look similar and are easy to confuse. A chain hanging under its own weight settles into a catenary, y = a·cosh(x/a), because the tension at every point must balance a load that itself depends on arc length. A parabola is the correct shape for a suspension bridge cable instead, because there the load (the deck) is distributed evenly per unit of horizontal distance rather than per unit of rope length.
Why use Verlet integration instead of tracking velocity directly?
Storing only the current and previous position (with velocity implied as their difference) makes enforcing a constraint trivial: you simply move a point to satisfy it, and the implied velocity updates itself automatically on the next step. With an explicit velocity variable, every constraint correction would also have to separately patch the velocity, which is exactly the extra bookkeeping Verlet-based solvers are built to avoid.
Try it live
Everything above runs in your browser — open Rope and Chain Physics and change the parameters while it is running. Nothing is installed, nothing is uploaded, the whole model lives in one tab.
▶ Open Rope and Chain Physics simulation