Box3D: The Physics Engine That Treats Determinism Like a Feature

A 3D successor to Box2D that swaps raw pointers for IDs, rewrites math for repeatability, and uses soft stepping to keep stacks, joints, and large worlds from falling apart.

8 to 10 min read • View on GitHub • More from erincatto

A stacked tower of blocks straddles a split floor. On one side, the stack trembles above a cracked grid and loose joints. On the other, the same stack is held upright by a lattice of constraint lines and a compact soft-step mechanism below it, explaining the engine's focus on stability over spectacle.
Box3D's pitch is not prettier physics. It is fewer ways for a simulation to go wrong.
Key Takeaways

Box3D is easy to misread if you start from the usual physics-engine checklist. It is not trying to impress you with a giant feature surface first. It is trying to keep a simulation honest when the world gets noisy, the frame budget gets tight, and tiny numeric differences start showing up as bugs.

That matters because physics failures are rarely dramatic at the root. They show up as a stack that slowly jitters, a joint that behaves differently on another machine, or a ray cast that costs too much once the world gets crowded. Box3D is built to narrow those failure modes before they metastasize.

I started Box3D to provide a 3D physics engine that is efficient and accurate, suitable for games and other interactive applications.

Erin Catto, Creator · Box3D - Erin Catto

Why physics engines fail in the same boring ways

Most physics bugs are not about exotic math. They are about drift, stale references, and broadphase work that scales badly when the scene gets busy. The problem is not that the engine cannot simulate a box. The problem is that it cannot keep simulating the same box the same way everywhere.

Box3D’s appeal starts there. It is a response to the unglamorous reality that games need physics to be repeatable, cheap to query, and predictable under stress. Stability is not a side effect. It is the product.

A close-up mechanical scene shows one physics step being divided into several small pulses. A lever feeds motion through three checkpoints, while sealed envelopes representing contact events move outward from the mechanism, explaining how Box3D stabilizes simulation by slicing time into controlled substeps.
Soft stepping turns one risky leap into several smaller, easier-to-control motions.

The API is built around trust, not pointers

Box3D does something quietly radical for a physics engine: it centers the public API on IDs instead of raw pointers. That sounds like a small implementation detail, but it changes the contract between the engine and your game. You ask the world to step. You get back events. You do not keep dangling ownership in your own code.

b3WorldId world = b3CreateWorld(&def);
b3World_Step(world, timeStep, subStepCount);

b3ContactEvents events = b3World_GetContactEvents(world);
for (int i = 0; i < events.count; ++i) {
    // Consume transient contact data here.
}

The engine treats a frame as a controlled sequence, not a single brittle leap.

This is where Box3D starts to feel like a system designed for integration, not just simulation. IDs are easier to serialize, easier to invalidate, and easier to move through a host engine's job system. Transient events also keep the API honest. If a contact only exists for the current step, the API should not pretend otherwise.


Soft stepping is the engine’s real personality

If there is one idea that explains Box3D, it is soft stepping. The engine exposes a `subStepCount` because it is willing to spend a frame in smaller pieces rather than demand that one aggressive solve do everything at once. That is a stability trade. It lowers the chance that contact impulses spike, joints explode, or stacks collapse from one bad update.

This is not the same as blindly turning up iterations. More iterations can help, but they do not change the shape of the contract. Soft stepping changes the shape. It slices time, narrows the error introduced per slice, and gives the solver a calmer problem to solve.

That distinction matters in 3D, where rotational coupling and large piles of bodies make instability show up faster. Box3D is less interested in pretending the solver is perfect than in making each step easier for the solver to survive.

Box3D fights floating-point chaos at the math layer

Determinism is not just a networked-multiplayer concern. It is also a debugging concern. If the same scene behaves differently on different hardware, the engine becomes harder to trust. Box3D responds with custom trigonometric functions, precision switches such as `BOX3D_DOUBLE_PRECISION`, and a math layer designed to stay repeatable across platforms.

#ifdef BOX3D_DOUBLE_PRECISION
typedef double b3Pos;
#else
typedef float b3Pos;
#endif

b3Vec2 p = b3MakeVec2(100000.0, 3.0);
auto angle = b3Atan2(p.y, p.x);
auto cs = b3ComputeCosSin(angle);

That combination is what makes large worlds possible without inviting the usual edge-of-the-map wobble. Doubling precision helps, but the real discipline is that the math is chosen to behave like a system, not a collection of library calls with platform-specific quirks.

The dynamic tree keeps the world searchable

A physics engine lives or dies on broadphase performance. Box3D uses a dynamic AABB tree with proxies so it can answer queries without dragging the whole world into every check. Ray casts, overlap queries, and box casts stay local, even when the simulation itself is large.

The proxy model matters because it separates moving things from the data structure that indexes them. That is the difference between a world that stays responsive and one that starts feeling expensive as soon as the scene gets busy.

Box3D is modular on purpose

The repository layout matches the philosophy. Public headers live in `include/box3d/`, the core implementation sits in `src/`, samples are isolated in `samples/`, and docs stay in `docs/`. The core is written in portable C17. The samples can use C++20, graphics helpers, and UI tools without contaminating the engine itself.

That separation is more than housekeeping. It signals what Box3D wants to be: a compact engine core with a host-controlled runtime around it. Multithreading is handled by callbacks into the application, which keeps the engine portable without pretending every game uses the same scheduler.


Where Box3D sits among Bullet, PhysX, and Newton

EnginePositioningStrengthTrade-off
Box3DDisciplined 3D successor to Box2DDeterminism, soft stepping, clean APISmaller ecosystem and narrower scope
BulletBroad open-source physics toolkitFeature breadth and communityMore surface area to tune and integrate
PhysXIndustrial-scale commercial enginePerformance and platform reachLicensing and heavier integration gravity
NewtonOpen-source stability-focused engineSolid realism and simplicitySmaller community and less momentum

Box3D is not trying to win by being everything. It is trying to win by being legible. The design choices line up around one promise: if you understand the contract, the engine will stay out of your way more often than it surprises you.

Box3D seems promising, but I'm curious about its performance compared to other established engines like Bullet.

Sources and references