Each "magnet" here is a circular current loop lying flat in the same plane as everything else on screen. That geometry has a useful exact property: because both the loop's current elements dl and the vector r to any point in that same plane are themselves in-plane, their cross product in Biot–Savart's law is always purely perpendicular to the page — so the field this simulation needs is a single scalar Bz(x,y), computed honestly by summing 24 discretized current elements per loop:
dB_z = (μ0 I / 4π) · (dl_x·r_y − dl_y·r_x) / |r|³ (Biot–Savart, summed over segments & loops)
The charged test particle then feels the exact 2D Lorentz force, with no approximation beyond the numeric integration step:
Fx = q·vy·Bz, Fy = −q·vx·Bz
a = F / m, v += a·dt, x += v·dt (semi-implicit Euler, fixed sub-steps)
Because F is always perpendicular to v, speed never changes — only heading. Where the summed Bz is strong the path curls tightly (small radius r = mv/(qB)); where loops of opposite current cancel, the path runs straight.
- Rings — colored contour lines of equal Bz: warm colors circle a loop whose current is one way, cool colors the other.
- Arrow on the particle — the instantaneous Lorentz force vector, scaled to |F|.
- Trail — the charge's recent path, brighter where the local field is stronger.