Task 3 of 5
Two of this task's bodies sit 0.001 apart. Plug that into
1 / r² and their mutual pull is about a million — one tick of the
clock later they're flung out of the galaxy. That's not physics; it's what happens when a
point-mass model meets a finite time step.
The standard fix is Plummer softening: replace r² with
r² + ε². Far away, ε changes nothing; up close, the force
flattens out instead of diverging. Bonus: the j !== i self-check becomes dead
weight — your own term has dx = dy = 0, so it contributes exactly zero. Drop
the branch; GPUs run happiest when every thread takes the same path.
r² + soft², drop the
self-check, and return the full [ax, ay] pair.dx·dx + dy·dy + soft·softj !== this.thread.x guard — the self term is now zero[ax, ay]For j === i: dx and dy are 0, so the
contribution is 0 · something. With soft² > 0 the
denominator is never zero, so that something is a plain finite number.
Compute const w = mass[j] / (r2 * Math.sqrt(r2)); once, then
ax += dx * w; ay += dy * w; — one denominator, two components.
Infinity and then
NaNs spread through every sum they touch, on Metal and WebGPU alike.
This page is an interactive exercise — the editor, the GPU runner and your saved progress need JavaScript. The text above is the full brief.