ProjectsPlaygroundMini GamesAboutContact
Mini GamesDeutsche Version
Tech Mini Games · Pinball

Pinball physics in the browser.

Why the ball never falls through the flipper: fixed 960 Hz timesteps, a flipper as a tapered capsule and the surface velocity ω × r – explained with the code of both tables.

Invasoren and Schafschiesser look like engineering drawings, but underneath runs a complete pinball simulation: about 750 lines of vanilla JavaScript per table, with no physics engine, no framework and no build step. This article shows how it works – and why it runs at 960 steps per second.

Why 960 Hz?

The ball has a radius of 12.5 units and may reach 2,800 units per second. On a 60 Hz display it travels about 47 units per frame – almost two ball diameters. A thin post or the tip of a flipper can simply be skipped over: the classic tunneling bug.

So physics is decoupled from rendering. Every frame fills a time accumulator, which is then drained in fixed steps of 1/960 s. Per step the ball moves at most about 3 units, well below its radius. The result is the same whether the monitor runs at 60, 120 or 144 Hz.

var acc = 0, STEP = 1 / 960;
function frame(now) {
  var dt = Math.min(0.05, (now - last) / 1000); last = now;
  acc += dt;
  while (acc >= STEP) { step(STEP); acc -= STEP; }
  draw();
  requestAnimationFrame(frame);
}

The ball: slope force instead of gravity

A pinball playfield is tilted by roughly 6.5 degrees, so the ball does not feel full gravity, only the component along the playfield – 1,150 units/s² in the model. Add a little rolling damping and a speed cap. If the ball almost comes to rest, it gets a tiny random nudge so it never balances on an edge forever.

Walls, posts, bumpers

Every line of the drawing is a segment, every post a circle. On each step the closest point of every element to the ball is computed. If the distance is smaller than the radius, the ball is pushed out along the normal and the normal component of its velocity is reflected with a restitution factor – 0.42 for walls. Slingshots and pop bumpers add an active kick of 560 to 620 units/s: that is the coil firing in the real machine.

The flipper as a tapered capsule

ωpivotnv = ω × r ballr₀ → r₁tip
Fig. 1 – Flipper as a capsule with a radius tapering from r₀ to r₁. At the contact point the surface velocity v = ω × r adds to the ball’s motion.

The flipper is a capsule: an axis from the pivot to the tip whose radius shrinks from r₀ to r₁. Collision works like a wall, with one crucial difference: the flipper moves. At the contact point its surface has the velocity ω × r – the further out the ball is hit, the faster. The response is therefore computed from the relative velocity between ball and surface.

function collideFlipper(b, f) {
  var tx = f.x + f.len * Math.cos(f.a), ty = f.y + f.len * Math.sin(f.a);
  var q = closest(f.x, f.y, tx, ty, b.x, b.y);          // closest point on the axis
  var rr = f.r0 + (f.r1 - f.r0) * q[2];                 // radius at that point
  var dx = b.x - q[0], dy = b.y - q[1], dist = Math.hypot(dx, dy), lim = R + rr;
  if (dist >= lim || dist === 0) return;
  var nx = dx / dist, ny = dy / dist;
  b.x += nx * (lim - dist); b.y += ny * (lim - dist);   // push the ball out
  var cx = q[0] + nx * rr - f.x, cy = q[1] + ny * rr - f.y;
  var sx = -f.w * cy, sy = f.w * cx;                    // surface velocity ω × r
  var rvx = b.vx - sx, rvy = b.vy - sy, vn = rvx * nx + rvy * ny;
  if (vn < 0) {                                         // only when approaching
    var e = Math.abs(f.w) > 1 ? 0.55 : 0.25;            // lively when swinging, damped at rest
    b.vx -= (1 + e) * vn * nx; b.vy -= (1 + e) * vn * ny;
  }
}

Two details define the feel. First, restitution: a swinging flipper bounces with 0.55, a resting one damps with 0.25. Only this makes it possible to catch and cradle the ball – the key technique for aimed shots. Second, friction is deliberately minimal. Too much and the ball sticks at the pivot; that was a real bug in an early version, and players noticed immediately.

The captive ball

The clone chamber in Invasoren holds a second ball that can only roll along its channel. When the playing ball hits it, the impulse is split along the contact normal – but only the component along the channel moves the captive ball. It is a one-dimensional constrained collision, and it produces the typical behaviour: a straight hit sends the clone ball all the way up, a glancing one barely moves it.

Nudging and TILT

A nudge gives the ball a small kick upwards and sideways and fills a tilt meter that slowly decays. Nudge too often and you get TILT: flippers dead, bonus lost – just like the real thing.

What is not simulated

The ball does not spin, and there are no ramps in the third dimension. Flippers, slingshots and bumpers fire instantly without coil delay. For tables from 1979 and 1980 that is enough – their mechanics are essentially two-dimensional.