Implementing Mass-Spring Systems in Physics Simulation

Mass-spring systems are fundamental in physics-based simulations, governed by Newton's second law (F = ma). The core process involves calculating forces on each mass, determining acceleration, and updating positions over small time steps. After each iteration, forces are reset before recalculating for the next frame.

Core Data Structures

Each mass point requires storage for position, mass, velocity, and accumulated forces. A pinned flag indicates fixed positions.

struct PointMass {
  PointMass(Vector2 pos, float m, bool fixed)
      : start_pos(pos), pos(pos), prev_pos(pos),
        mass(m), fixed(fixed) {}

  float mass;
  bool fixed;

  Vector2 start_pos;  // Original position
  Vector2 pos;        // Current position

  // Verlet integration
  Vector2 prev_pos;

  // Euler integration
  Vector2 velocity;
  Vector2 forces;
};

Euler Integration Methods

Explicit Euler updates position before velocity, leading to instability:

// Explicit Euler
m->pos = m->pos + m->velocity * dt;
m->velocity = m->velocity + acceleration * dt;

Semi-implicit Euler (more stable) updates velocity first:

// Semi-implicit Euler
m->velocity = m->velocity + acceleration * dt;
m->pos = m->pos + m->velocity * dt;

Verlet Integration

Derived from semi-implicit Euler, Verlet uses position history:

Vector2 temp = m->pos;
m->pos = m->pos + (m->pos - m->prev_pos) + acceleration * dt * dt;
m->prev_pos = temp;

Spring Constraints

Springs connect masses and enforce rest lengths. Adjust positions based on pin constraints:

Vector2 delta = spring->mass2->pos - spring->mass1->pos;
float current_length = delta.norm();
float diff = current_length - spring->rest_length;

if (!spring->mass1->fixed && !spring->mass2->fixed) {
  Vector2 correction = delta.unit() * diff * 0.5f;
  spring->mass1->pos += correction;
  spring->mass2->pos -= correction;
}
else if (spring->mass1->fixed && !spring->mass2->fixed) {
  spring->mass2->pos -= delta.unit() * diff;
}
else if (!spring->mass1->fixed && spring->mass2->fixed) {
  spring->mass1->pos += delta.unit() * diff;
}

Implementation Note: When chaining springs, apply constraints sequentially. After updating a spring, temporarily fix its endpoint to prevent cascading errors before processing the next spring. Release temporary fixes after all constraints are rseolved.

Tags: Physics Simulation mass-spring system numerical integration verlet euler method

Posted on Sun, 26 Jul 2026 17:05:25 +0000 by n5tkn