# Box3D — upstream research notes *Findings from the evaluation session that led to this testbed existing (2026-08-06, M3 Ultra). Complements [README.md](README.md), which covers this project's own A/B probe results. Nothing here is about the table — it's about the engine underneath it and the hardware it runs on.* ## 1. Cap physics workers at 24 on this machine, not 32 The single most actionable finding, and it applies to **any** threaded physics on this fleet — Box3D, Jolt, or anything else. The M3 Ultra is **24 performance + 8 efficiency cores**. Box3D's own benchmark, swept across worker counts on this machine, gets *slower* once work spills onto the E-cores: | workers | ms | | |---|---|---| | 30 | 5,158 | | | 31 | 5,310 | | | 32 | 5,646 | **10% worse than 30** | A physics step is a barrier — every worker waits for the slowest one — so handing a share of the work to a core that runs at roughly a third the speed makes the whole step wait on it. More threads is not more throughput past the performance-core count. **So:** don't set worker count from `OS.get_processor_count()` / `hw.ncpu` on Apple Silicon. Read the performance-core count instead: ```sh sysctl -n hw.perflevel0.physicalcpu # 24 on this box — use this sysctl -n hw.ncpu # 32 — do NOT use this ``` Within the performance cores the threading scales well: upstream's M2 Air `joint_grid` figures are 1,089 ms single-threaded → 309 ms on 4 threads (**3.52×**). > **Status in godot-box3d (verified 2026-08-06 against the commit vendored in `bin/`):** the > binding hardcodes `def.workerCount = 1` in `src/spaces/box3d_space_3d.cpp` and wires no task > system, so Godot runs Box3D **single-threaded** — there is no worker setting to cap, and the > E-core trap can't trigger through Godot today. This section applies to the raw benchmark > harness below, and becomes load-bearing the day the binding grows threading. Caveat on the table above: it is one scene (8,002 bodies / 40,444 contacts), best-of-4 runs, and the low-thread-count rows of that particular sweep were lost to a truncated capture. The *direction* is unambiguous and reproduces; treat the exact millisecond figures as indicative. A pinball table is ~1 ball and a few dozen bodies, so none of this is a bottleneck **here** — it matters when this testbed is used to answer questions for the bigger physics games. ## 2. The benchmark harness, and how to re-run it Box3D ships a real benchmark suite. It is not built by default — note the option is `BOX3D_BENCHMARKS`, **plural** (an easy 20 minutes to lose): ```sh git clone --depth 1 https://github.com/erincatto/box3d && cd box3d cmake --preset macos -DBOX3D_BENCHMARKS=ON cmake --build --preset macos-release --target benchmark -j 24 ./build/bin/Release/benchmark --list ``` Flags: `-b ` one scene · `-w ` a single worker count · `-t ` max threads · `-r ` repeats (default 4) · `-nc` disable continuous collision · `-s` record step times. Scenes: `convex_pile`, `joint_grid`, `junkyard`, `large_pyramid`, `large_world`, `many_pyramids`, `rain`, `trees100/50/25`, `washer`. Upstream keeps reference results in `benchmark/m2air_neon/` (Apple Silicon) and `benchmark/amd7950x_*` — useful baselines to compare a run against. **Budget the time.** A full sweep is ~11 scenes × every worker count × 4 repeats and runs for well over an hour on 32 cores. Always pin `-b` and `-w`, and capture with `tee`, not `tail`. The library itself builds clean on Apple Silicon (`cmake --preset macos`) and its whole test suite passes in **0.61 s** — a fast, honest smoke test after any upstream bump. ## 3. What godot-box3d does NOT support From the binding's own README. Hitting one of these looks like an engine bug and isn't: - **Generic6DOF and ConeTwist joints** — not implemented - **`SoftBody3D`** — not implemented - Separation ray shapes - Per-pair collision exceptions (use layers/masks) - Changing a `PinJoint3D` anchor after creation — recreate the joint instead And one behavioural divergence that will bite silently rather than error: > **`Area3D` does not detect trimesh or heightmap bodies.** In Box3D a concave or heightmap shape > can never be a sensor *visitor*, by design. No `body_entered` / `body_exited` fires for such a > body. Godot's built-in physics and Jolt both report it. **Give anything that must be > area-detected a convex shape.** Pinball is unaffected — it needs hinge joints (supported) and CCD (Box3D's strength), and none of the above. That is exactly why this became the testbed game. ## 4. Why pinball, and not the ragdoll game `~/Documents/alright` (the fall-down-the-stairs game) is the portfolio's strongest *fit* for Box3D — an active ragdoll of 14 rigid bodies and 12 joints with PD motor control, already paying `physics_ticks_per_second=120` and `solver_iterations=16` and hand-picking `continuous_cd` on head/torso/pelvis, which is the signature of fighting a solver. **It is blocked:** that ragdoll is built from `Generic6DOFJoint3D`, which the binding does not implement. Watch that single line item upstream — when Generic6DOF lands, `alright` becomes the real experiment, and the question to answer is whether the 120 Hz / 16-iteration tax can come back down. Elsewhere in the portfolio physics is 2D, absent, or deliberately bespoke (Shitbox's car is a raycast-suspension `RigidBody3D` with a hand-written tyre model — its feel lives in *its* code, not the solver; Destroyulator's cascade is a support graph). `toastsim` is the only browser game with a real 3D solver need and it already runs Rapier. ## 5. Provenance and maturity — worth being clear-eyed about **Box3D** — Erin Catto (author of Box2D), MIT, portable C17, data-oriented, ARM NEON SIMD, cross-platform determinism, record/replay. Repo created 2026-05-10, announced 2026-06-30, ~5,900 stars, actively developed. Shipping adoption: Facepunch's **s&box** and the **Esoterica** engine. This is a serious engine by someone with 20+ years of solver experience. **godot-box3d** (bearlikelion) — created 2026-07-02, ~134 stars, **zero releases**, and its README says plainly: *"Status: early and experimental."* Performance benchmarking is still on its own to-do list, so no one has yet shown it beats Jolt at anything. Modelled on godot-jolt's approach. There are **no prebuilt binaries** — build from source (~30 s on this machine). Practical consequence: this project is a **testbed**, not a bet. Jolt remains the sane default for anything shipping; the value here is being able to answer engine questions with measurements instead of opinions. ## 6. Browser note Box3D builds to **WASM** — its CMake maps the SSE2 path onto wasm SIMD128 and supports pthreads. But it ships **no JS bindings**, so a browser game would mean hand-writing that layer. Rapier already gives you a polished JS API. Relevant if a web pinball or web physics toy is ever floated. --- *Cross-reference: this project's own measured results — Box3D never tunnelling even with CCD off, and the inverted hinge-motor sign versus Jolt/GodotPhysics — are in [README.md](README.md). That sign inversion is a genuine drop-in-compatibility bug worth reporting upstream.*