One public step contains several physical substeps
An RL policy commonly acts at 60 Hz, but LavenderSim limits the native physics timestep to 1/240 s. One control step therefore performs four physical substeps. The control is held while forces, collision detection, constraint solving, integration, correction, and sensing repeat. At the end, telemetry is copied into flat buffers for Python or JavaScript.
Here is complete simulator state, is the command held for the control interval, is one substep, and at 60 Hz with 240 Hz physics. Changing the ordering changes even when every individual formula stays the same.

Experiment 1: single-step the architecture
The reference lab writes every read and write explicitly. Select a stage below and ask: what would become one-step stale if it moved earlier?
Control
- Reads
- policy command, joint state
- Writes
- controller targets
Trace a transparent toy substep
Where does time advance, and which pose reaches the sensors?
for stage in pipeline:
event = stage(state, control, dt)
trace.append({"reads": event.reads,
"writes": event.writes})Expected: nine ordered events; time advances at integration; sensing follows position projection. Failure modes: sample sensors before correction, integrate before contact impulses, treat telemetry copying as physics, or assume one policy action means one native substep.
- Expected result
- The command exits successfully and reproduces the numerical or visual relationship described in this card.
- Failure modes
- Non-finite values, a reversed trend, a failed assertion, or a materially different plot means the assumptions, seed, timestep, build, or backend should be inspected before continuing.
sim_step() and substep() define this order. The public pipeline observation is a 9 × 3 matrix: stage ID, backend-comparable work units, and optional native microseconds.Experiment 2: profile work, not your intuition
Native timing is opt-in because clocks perturb tiny workloads. Calling set_profiling(True) fills the third trace column with elapsed microseconds. The second column is a deterministic work counter—bodies, candidate pairs, solver row-visits, samples, or copied records depending on the stage. Work units compare runs; they are deliberately not interchangeable durations.
Profile the quadruped pipeline
Which stages grow with articulated contact?

env.sim.set_profiling(True)
observation, reward, terminated, truncated, info = env.step(action)
trace = env.sim.pipeline_trace()
# columns: stage_id, work_units, native_microsecondsFailure modes: benchmark a debug build, infer time from work units, include process startup, compare different scenes, or report one sample without distribution or hardware context.
- Expected result
- The command exits successfully and reproduces the numerical or visual relationship described in this card.
- Failure modes
- Non-finite values, a reversed trend, a failed assertion, or a materially different plot means the assumptions, seed, timestep, build, or backend should be inspected before continuing.
Experiment 3: one contract, two backends
The browser build runs the same C++ source compiled to freestanding WebAssembly. It exposes stage IDs and work units, but records zero in the timing column because the module has no host clock import. Browser wall-clock profiling belongs around the JavaScript call; semantic engine parity belongs inside the trace.
Compare a resting sphere in native and WASM
Do both builds execute the same stage contract?

Failure modes: compare native time with a missing WASM clock, verify only exported symbol names, use different scene defaults, or generalize one exact trace to all hardware and all trajectories.
- Expected result
- The command exits successfully and reproduces the numerical or visual relationship described in this card.
- Failure modes
- Non-finite values, a reversed trend, a failed assertion, or a materially different plot means the assumptions, seed, timestep, build, or backend should be inspected before continuing.
Experiment 4: rewind, replay, branch
A snapshot turns simulation into an experiment tree. Save at state , apply action sequence , restore, and repeat to test determinism. Restore again and apply to isolate the action's causal effect.
Replay one force and branch to its opposite
Can serialized state reproduce the same trajectory before a counterfactual diverges?

The current snapshot covers bodies, joints, controls, actuator activation, Python sensor pipeline state and RNG, solver controls, task state, and time. The demonstrated exact replay intentionally avoids persistent contacts; contact-cache and native sensor-filter serialization remain a boundary to test before claiming arbitrary contact-rich bitwise replay.
- Expected result
- The command exits successfully and reproduces the numerical or visual relationship described in this card.
- Failure modes
- Non-finite values, a reversed trend, a failed assertion, or a materially different plot means the assumptions, seed, timestep, build, or backend should be inspected before continuing.
Experiment 5: make one environment become 32
PPO needs many decorrelated transitions. ProcessVectorEnv creates 32 independent registry instances, sends a batch of actions, then stacks their flat observations and rewards. It is 32 copies of one registered task—not 32 different task types.
Roll out 32 registry-created workers
Does the environment contract remain finite and rectangular across processes?

with ProcessVectorEnv(32, task="QuadrupedCommand-v0",
control_hz=60, horizon_seconds=3) as env:
obs = env.reset(seed=12) # (32, 37)
obs, reward, done, info = env.step(actions)Failure modes: reuse one RNG stream in every worker, forget to close processes, silently flatten the environment axis, launch a browser per worker, or confuse simulator throughput with PPO sample quality.
- Expected result
- The command exits successfully and reproduces the numerical or visual relationship described in this card.
- Failure modes
- Non-finite values, a reversed trend, a failed assertion, or a materially different plot means the assumptions, seed, timestep, build, or backend should be inspected before continuing.
Capstone: follow one quadruped action end to end

- The environment samples a body-relative planar direction and speed, then assembles 37 proprioceptive and command values.
- A controller or policy emits 12 joint actions at 60 Hz.
- Actuator forces, gravity, damping, and user wrenches update four 240 Hz substeps.
- Collision manifolds and joint rows feed the warm-started sequential-impulse solve.
- Velocity integration and position projection produce the physical state sampled by IMU and joint sensors.
- The task computes command-tracking reward; telemetry and overlays can be published to the Python-controlled browser UI.
- The process vectorizer repeats that contract across 32 isolated workers for PPO.
Paw-check: reason across stage boundaries
- Move sensing before projection in the toy lab. Which quantities become inconsistent?
- Profile a contact-free scene and explain the changed work vector.
- Add host-side browser timing around
sim_stepwithout changing the backend parity contract. - Snapshot a noisy delayed sensor and prove its replay behavior.
- Increase physics frequency while holding 60 Hz control constant; predict work and stability changes.
- Profile 32 workers without including process creation in per-step latency.
- Replace the deterministic quadruped gait with a checkpoint and keep the engine assertions unchanged.
What should I be able to say now?
A simulation step is an ordered state transformation, not a monolithic black box. Controls affect forces; collision creates constraint data; iterative impulses change velocity; integration changes pose and time; projection corrects drift; sensors and telemetry observe that resulting state. Deterministic counters test backend semantics, clocks measure platform-specific cost, snapshots enable causal branches, and a vector environment batches the same verified contract for RL.