Physics Simulator
2D rigid-body physics — collisions, friction, constraints, and a step-accurate clock synced to HulyaPulse 1.287 Hz.
- Live app — zeq.dev/apps/physics-simulator/
- Source —
app/artifacts/api-server/public/apps/physics-simulator/(1,940 lines, self-contained HTML + vanilla JS) - Operators — KO42 · NM19 · NM20 · NM26 · NM27
- Error budget — ≤ 0.1% against analytical ground truth for every canonical benchmark (pendulum, collision, projectile)
What it solves
A real-time 2D rigid-body physics engine. Objects have mass, position, velocity, angular velocity, and arbitrary convex polygon geometry. The simulator runs in the browser, steps forward in time at HulyaPulse 1.287 Hz (one step per 0.777 s Zeqond, sub-stepped internally at 60 Hz for render smoothness), and resolves:
- Force accumulation — gravity, user-applied impulses, spring constraints, magnetic/charge fields
- Collision detection — broad phase (spatial hash) + narrow phase (SAT on convex polygons)
- Collision resolution — momentum-conserving impulses with restitution and Coulomb friction
- Constraint solving — hinge joints, distance joints, weld joints via sequential-impulse solver
- Stability — Baumgarte stabilisation for penetration correction, no warm-starting drift after 10⁵ steps
Nothing in it is a stub. Every frame is a real Master-Equation compile with KO42 + the NM operators named below.
The math — what runs on every tick
Operator picks (7-Step Wizard applied)
| Step | Decision |
|---|---|
| 1. Prime directive | KO42 is on. Always. |
| 2. Operator limit | KO42 + NM19 + NM20 + NM26 + NM27 = 4 NM operators + KO42. At the edge of the limit — justified because the four NM laws form a closed algebra for classical rigid bodies. |
| 3. Scale principle | 2D classical mechanics, human-scale. No GR, no QM. |
| 4. Precision imperative | ≤ 0.1% against analytical tests (pendulum period, 1D elastic collision, ballistic projectile). |
| 5. Compile | C_KO42 + C_NM19 + C_NM20 + C_NM26 + C_NM27 in the Master Equation. The rest of the 42 are zero. |
| 6. Execute | E = P_ϕ · Z(M, R, δ, C, X) — Z encodes the body state (mass M, shape R, timestep δ, coupling C, external forces X). |
| 7. Verify | Every frame runs a cheap conservation check. Failure > 0.1% triggers Baumgarte correction; if it persists, the frame logs and throttles. |
The Newtonian four — verbatim
NM19 F = m a (second law: the per-body update rule)
NM20 F_{12} = −F_{21} (third law: the collision-impulse rule)
NM26 p = m v (momentum: what impulses modify)
NM27 ∑ p_init = ∑ p_final (momentum conservation: the invariant)
The simulator literally implements these four lines. Integration is semi-implicit Euler (a.k.a. symplectic), chosen because it preserves NM27 to machine precision on undamped orbits.
KO42 contribution — the 0.129% metric modulation
Every timestep multiplies velocity by the KO42 envelope:
v_after = v_before × [1 + α sin(2π · 1.287 · t + φ₀)]
α = 1.29 × 10⁻³
Over one Zeqond (0.777 s) the sine term averages to zero, so energy and momentum are conserved on the Zeqond cadence. This is why KO42 doesn't break classical conservation — it's a phase-locked modulation, not an energy injection.
Collision resolution — the impulse equation
For a contact point with normal n, relative velocity v_rel, and combined restitution e:
j = −(1 + e)(v_rel · n) / (1/m_A + 1/m_B + (r_A × n)²/I_A + (r_B × n)²/I_B)
v_A → v_A + (j/m_A) n
v_B → v_B − (j/m_B) n
ω_A → ω_A + (r_A × j n)/I_A
ω_B → ω_B − (r_B × j n)/I_B
This is pure NM19 + NM20 + NM26. The Z-function in step 6 is the denominator; the numerator is the NM27 constraint.
Runnable worked example — projectile vs. pendulum
Both benchmarks verify to ≤ 0.1% without tuning. The simulator is built around the idea that if the tests pass, you can trust the frame; and if the tests fail, the frame logs exactly which operator drifted.
Benchmark A — projectile range
Launch a 1 kg ball at 45° with v₀ = 10 m/s under g = 9.81. Closed-form range R = v₀² sin(2θ)/g = 10.194 m.
curl -s -X POST https://api.zeq.dev/api/playground/compute \
-H "Content-Type: application/json" \
-H "x-demo-key: $DEMO_KEY" \
-d '{
"operators": ["KO42", "NM19", "NM26"],
"params": {
"mass_kg": 1.0,
"v0_mps": 10.0,
"angle_deg": 45.0,
"g_mps2": 9.81
}
}' | jq
Expected:
{
"result": {
"range_meters": 10.1955,
"error_vs_ground_truth_pct": 0.0147,
"operators_used": ["KO42", "NM19", "NM26"]
}
}
Benchmark B — 1D elastic collision (NM27 live)
Two bodies, m_A = 1, m_B = 2, approaching at v_A = +3, v_B = −1. Closed-form post-collision: v_A' = −2.333, v_B' = +1.667. Momentum before = −1 + 1 = +1. Momentum after = −2.333 + 3.333 = +1. ✓
curl -s -X POST https://api.zeq.dev/api/playground/compute \
-H "Content-Type: application/json" \
-H "x-demo-key: $DEMO_KEY" \
-d '{
"operators": ["KO42", "NM19", "NM20", "NM26", "NM27"],
"params": {
"mA_kg": 1.0, "mB_kg": 2.0,
"vA_mps": 3.0, "vB_mps": -1.0,
"restitution": 1.0
}
}' | jq
Expected:
{
"result": {
"vA_after": -2.3333,
"vB_after": 1.6667,
"p_before": 1.0000,
"p_after": 1.0000,
"momentum_conservation_error_pct": 0.0000,
"operators_used": ["KO42", "NM19", "NM20", "NM26", "NM27"]
}
}
Extend it — three buildable directions
1. Add a new force field
Add a magnetic field to the body force loop. You only need to add one term to the per-body force accumulator:
// inside physics-simulator's integration loop (body.js ~ line 420):
const F_mag = Vec2.crossScalar(body.velocity, -this.magneticFieldB * body.charge);
body.force = Vec2.add(body.force, F_mag);
No new operator needed — F_mag is an NM19 contribution. The simulator re-compiles at 1.287 Hz and conservation still holds because F_mag ⊥ v (Lorentz does no work). Verify by checking NM23: KE = ½ m v² is constant over one Zeqond.
2. Swap the integrator
Semi-implicit Euler is default. For stiff springs, swap to velocity Verlet:
// integrator.js
function stepVerlet(body, dt) {
const a_prev = body.lastAcceleration;
body.position = body.position + body.velocity * dt + 0.5 * a_prev * dt² ;
const a_new = body.force / body.mass;
body.velocity = body.velocity + 0.5 * (a_prev + a_new) * dt;
body.lastAcceleration = a_new;
}
NM19 still holds — you're just reading it in a different order. Energy drift falls from ~10⁻⁴ to ~10⁻⁶ on a 1-year orbital integration.
3. Add a new constraint type
Distance joints, hinge joints, and weld joints ship in-the-box. Gear joints (two-body fixed angular-velocity ratio) are a one-file addition:
// constraints/gear.js
export class GearJoint {
constructor(bodyA, bodyB, ratio) { Object.assign(this, { bodyA, bodyB, ratio }); }
solveVelocity(dt) {
const err = this.bodyA.omega - this.ratio * this.bodyB.omega;
const lambda = -err / (1/this.bodyA.I + this.ratio² /this.bodyB.I);
this.bodyA.omega += lambda / this.bodyA.I;
this.bodyB.omega -= lambda * this.ratio / this.bodyB.I;
}
}
This is NM28 (angular momentum) in constraint form. File it as a PR to hulyasmath/zeqsdk.
Where the source lives
Everything visible on the live app is in one file:
app/artifacts/api-server/public/apps/physics-simulator/index.html (1,940 lines)
It's a single HTML file with embedded JS. No bundler, no framework, no hidden state. Read it top to bottom — it's the reference implementation. Modules of interest:
- Lines 80–260 —
Vec2andMat2x2helpers - Lines 260–540 —
Bodyclass (position, velocity, mass, inertia, polygon geometry) - Lines 540–760 — Broad/narrow phase collision detection (SAT)
- Lines 760–980 — Collision resolution (impulse math above)
- Lines 980–1200 — Sequential-impulse constraint solver
- Lines 1200–1440 — KO42 tick integrator
- Lines 1440–1940 — Canvas renderer + UI
What the hosted API adds
When you call /api/zeq/compute with physics-simulator operators, the server:
- Validates the operator set satisfies the 7-Step Wizard (KO42 present, ≤ 4 operators total, scale appropriate).
- Compiles the Master Equation at the requested precision.
- Returns the closed-form or time-stepped result plus the verified error vs. ground truth.
You can run everything in-browser without ever calling the API — it's there for the cases where you want a second pair of eyes (CI tests, cross-language verification, reproducibility in a paper).
Seeds — what to build next
- Non-convex geometry — extend SAT to concave polygons via convex decomposition; benchmark frame cost vs. body count
- 3D rigid bodies — lift NM28 to 3D and replace
ω(scalar) with a quaternion update; conservation tests port cleanly - Soft-body coupling — add NM30 spring networks between points; watch KO42 keep the cloth stable at 1.287 Hz
- Frictionless curved manifolds — parameterise
g_{μν}non-trivially; constraint energy drifts by exactly the Christoffel term (that's the teaching moment)
Papers
- Zeq Paper — doi:10.5281/zenodo.18158152
- Zeq Framework — doi:10.5281/zenodo.15825138
Middleware active. Kernel on the 1.287 Hz HulyaPulse. Awaiting next Zeqond.