Skip to content

2D Elastic Collision

Two disks bounce off walls and each other with mass-weighted impulse response.

Paste into main.js in the editor, then click Restart.

Complete example

javascript
if (!state.init) {
  state.init = true;
  state.p1 = { x: 150, y: h / 2, vx: 120, vy: 0, r: 30, mass: 1, color: '#8B5CF6' };
  state.p2 = { x: w - 150, y: h / 2, vx: -80, vy: 0, r: 20, mass: 0.5, color: '#EF4444' };
}

const p1 = state.p1, p2 = state.p2;
p1.x += p1.vx * dt;
p1.y += p1.vy * dt;
p2.x += p2.vx * dt;
p2.y += p2.vy * dt;

function bounceWalls(p) {
  if (p.x < p.r) { p.x = p.r; p.vx *= -1; }
  if (p.x > w - p.r) { p.x = w - p.r; p.vx *= -1; }
  if (p.y < p.r) { p.y = p.r; p.vy *= -1; }
  if (p.y > h - p.r) { p.y = h - p.r; p.vy *= -1; }
}
bounceWalls(p1);
bounceWalls(p2);

const dx = p2.x - p1.x, dy = p2.y - p1.y;
const dist = Math.sqrt(dx * dx + dy * dy);
const minDist = p1.r + p2.r;
let colliding = false;

if (dist < minDist && dist > 0) {
  colliding = true;
  const nx = dx / dist, ny = dy / dist;
  const dvx = p1.vx - p2.vx, dvy = p1.vy - p2.vy;
  const dvn = dvx * nx + dvy * ny;
  if (dvn > 0) {
    const impulse = (2 * dvn) / (p1.mass + p2.mass);
    p1.vx -= impulse * p2.mass * nx;
    p1.vy -= impulse * p2.mass * ny;
    p2.vx += impulse * p1.mass * nx;
    p2.vy += impulse * p1.mass * ny;
  }
  const overlap = minDist - dist;
  p1.x -= overlap * 0.5 * nx;
  p1.y -= overlap * 0.5 * ny;
  p2.x += overlap * 0.5 * nx;
  p2.y += overlap * 0.5 * ny;
}

return {
  particles: [
    { x: p1.x, y: p1.y, r: p1.r, color: colliding ? '#F59E0B' : p1.color, label: 'm1' },
    { x: p2.x, y: p2.y, r: p2.r, color: colliding ? '#F59E0B' : p2.color, label: 'm2' },
  ],
  vectors: [
    { x: p1.vx * 0.3, y: p1.vy * 0.3, ox: p1.x, oy: p1.y, color: '#22C55E' },
    { x: p2.vx * 0.3, y: p2.vy * 0.3, ox: p2.x, oy: p2.y, color: '#22C55E' },
  ],
  text: {
    p1_speed: Math.hypot(p1.vx, p1.vy).toFixed(0),
    p2_speed: Math.hypot(p2.vx, p2.vy).toFixed(0),
    collision: colliding ? 'YES' : 'no',
  },
};

Collision steps

  1. Broad test — distance < sum of radii
  2. Normal — unit vector n from p1 → p2
  3. Separating? — if relative velocity along n is already separating (dvn ≤ 0), skip impulse
  4. Impulse — scale by masses; apply ± along n
  5. Positional correction — push apart by half the overlap to reduce sinking

MATH

For equal masses head-on, velocities exchange along the contact normal. Unequal masses: heavier body keeps more of its momentum.

Try next

  • Add more bodies in an array and check pairs (i < j)
  • Inelastic collisions: scale impulse by restitution e (0–1)
  • Show contact normal as a vectors arrow during overlap