Box3D can record a simulation into a memory buffer and replay it later, reproducing the original run exactly. A recording is a snapshot of the world at the moment recording starts followed by a log of every world-mutating API call after that. Replaying it re-runs the engine from the snapshot over those same calls, so the bodies follow the same paths and end up in the same state.
The main use is for debugging. You can record a session, save the buffer to a file, then load it in the sample app and view the Box3D state as the simulation progresses.
You own the recording buffer. Box3D records into it and grows it as needed; you save it to disk and free it. The library does no file I/O of its own for recording, beyond two optional convenience helpers (b3SaveRecordingToFile / b3LoadRecordingFromFile).
A recording can start before the first step, capturing the whole session, or mid-session for the "a bug appeared after 30 seconds and only then did I turn recording on" case. Either way the buffer opens with a snapshot of the world as it stands when recording starts, so there is one code path for both.
A recording is self-contained. Shape geometry that cannot ride along as plain data, hulls, meshes, heightfields, and compounds, is interned once into a registry stored in the recording, so the file carries everything needed to rebuild the world. No external mesh assets are required to replay.
Create a recording buffer, start recording the world into it, then run the simulation as usual. Start before the first step to capture everything.
b3CreateRecording takes a byte capacity to pre-size the buffer. The buffer still grows on demand, so any value is safe; pre-sizing just avoids reallocations during a long session. Pass 0 for the default.
b3World_StartRecording must be called at a step boundary. It serializes a snapshot of the current world as the seed, so it works whether you call it before any bodies exist or deep into a running simulation. It has no effect if the world is already recording. The buffer is reset on each call, so a single b3Recording can be reused for several sessions.
When you are done, save the buffer and free it. File I/O is yours to do; the convenience helper writes the raw bytes:
Stopping is optional: b3DestroyWorld detaches an active recording for you. The recording buffer outlives the world, so you can still save it after the world is gone. You can also keep simulating after b3World_StopRecording without recording, and reuse the same handle for a fresh recording with another b3World_StartRecording.
The simplest way to check a recording is the headless validator. It re-runs the engine over the recorded bytes and confirms every recorded id and per-step state reproduces.
b3ValidateReplay runs the replay on a single worker; pass 1 for the workerCount. To exercise multi-worker replay, use the player and b3RecPlayer_SetWorkerCount (see below).
To replay a recording from disk, load it into a buffer first:
For stepping through a recording frame by frame, for example to drive a viewer or inspect the world between steps, use the player handle. The player copies the bytes it is given, so you can free the source buffer immediately after creating it.
b3RecPlayer_Create returns NULL if the bytes are malformed or fail the layout gate (see the determinism contract below). b3RecPlayer_IsAtEnd reports when the recording is exhausted, and b3RecPlayer_HasDiverged reports whether a recorded state hash failed to reproduce. b3RecPlayer_GetDivergeFrame returns the first frame that diverged, or -1. Divergence is non-fatal during playback so the viewer can keep playing and show where the run starts to differ.
b3RecPlayer_GetInfo returns a b3RecPlayerInfo read once at open: frame count, recorded time step and sub-step count, the length scale in effect when recorded, and the accumulated world bounds, enough to frame and label the recording before the first step.
b3RecPlayer_SeekFrame jumps to any frame. A forward seek steps op by op; a backward seek restores the nearest keyframe and re-steps the remaining gap. A keyframe is a periodic snapshot the player keeps so backward seeking does not replay from the start. b3RecPlayer_SetKeyframePolicy trades memory for seek speed: it caps the bytes spent on kept snapshots and sets the finest spacing in frames. The spacing widens automatically as the ring evicts to stay under budget, so the effective backward-seek granularity is reported by b3RecPlayer_GetKeyframeInterval.
b3RecPlayer_Create takes a worker count for the replay world; pass 1 to match a serial recording. b3RecPlayer_SetWorkerCount changes it on the live player and is reused whenever the player rebuilds its world on restart or a backward seek. Replaying at a different worker count than was recorded re-partitions the constraint graph, so the state-hash check becomes a cross-thread determinism test.
A recording is seeded by a snapshot: a serialized image of the world's simulation state at a step boundary. It captures everything the engine needs to continue the simulation: bodies, shapes, joints, contacts with their warm-start impulses, the island and sleep partition, the broad-phase trees, the id pools, and the interned shape geometry. It does not capture host wiring (worker count, task callbacks, user data, the friction and restitution mixers); that is rebuilt or reinstalled at restore.
This machinery is currently internal, used only to seed and replay recordings. Box3D does not yet expose a standalone save-state / restore API.
The player's replay world is created internally, so a renderer has no chance to build per-shape draw resources as shapes appear. b3RecPlayer_SetDebugShapeCallbacks wires host callbacks into the replay world for exactly that: one is called when a replayed shape is added (returning a user draw handle), one when it is removed. Call it once right after b3RecPlayer_Create and re-read the world id afterward, since installing the callbacks rebuilds the world and rewinds to frame 0. The callbacks persist across restart and backward seeks. The 3D sample needs this or the replay world draws nothing.
The samples app has a Replay category with a Viewer. Open a recording from its file row, choose a keyframe budget and minimum interval in the load dialog, then play, pause, single-step, seek with the scrubber, restart, and change the replay worker count. A ****DIVERGED**** marker appears, and the scrubber marks the diverge frame, if a recorded state hash fails to reproduce, which is a real determinism break, not a viewer bug. The info panel lists the recorded bodies, shapes, joints, and per-frame spatial queries for inspection.
You can also record any sample: open the Recording controls in the sample's panel, set a file name, and press Record (restart) to capture from the start or Record Now to capture from the current step. Stop saves the buffer to the file.
Replay reproduces the original run exactly only when the replaying build matches the recording build in the ways that affect the math. Some of this the format enforces on load, and the rest is your responsibility:
See the Determinism section of the Simulation page for what "same inputs" means across platforms and thread counts.
Overlap and cast queries issued during a recorded step (ray casts, shape casts, overlap tests, and the character mover casts) are recorded too. On replay each query is re-issued against the replayed world and its results are compared against what was recorded, so a query that returns different hits is flagged like any other divergence. The player exposes the recorded queries of the most recently replayed frame through b3RecPlayer_GetFrameQueryCount, b3RecPlayer_GetFrameQuery, and b3RecPlayer_GetFrameQueryHit, and b3RecPlayer_DrawFrameQueries draws them layered on top of the world; call it after b3World_Draw.