A simulator advances state
Suppose the complete state of a moving object is collected in a vector . A differential equation tells us the instantaneous rate of change:
For a particle, a useful state is , containing position and velocity. Its derivative contains velocity and acceleration:
The function tells us what is happening now. An integrator estimates what the state will be after a finite timestep . Every real-time simulator performs some version of this approximation.
Explicit Euler and velocity-first Euler
Explicit Euler samples the derivative at the beginning of the interval:
Applied to position and velocity, both updates use old information:
Semi-implicit, symplectic, or velocity-first Euler changes only the order:
That small change makes the position update respond to the impulse accumulated during the current step. It remains a first-order method, yet on many mechanical systems it avoids the relentless energy growth of explicit Euler.
def semi_implicit_step(acceleration, time, position, velocity, dt):
next_velocity = velocity + dt * acceleration(time, position, velocity)
next_position = position + dt * next_velocity
return next_position, next_velocityExperiment 1: constant acceleration
Measure convergence in free fall
When the timestep is halved, how quickly does final position error shrink?
Drop a point from 10 metres for two seconds while ignoring the floor. Constant gravity has an exact solution, so it gives us trustworthy reference data:
Euler's final position error halves when halves: first-order global convergence. Midpoint and RK4 reproduce this particular constant-acceleration polynomial to floating-point roundoff. That does not mean they are exact for arbitrary dynamics.

- 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 2: energy exposes instability
Watch an oscillator gain energy
All four curves initially look plausible—so which integrator is quietly corrupting the physics?
For an ideal mass–spring oscillator, total energy should remain constant:
At , explicit Euler adds energy on every orbit. Semi-implicit Euler's energy oscillates around the correct level instead of drifting away. Midpoint drifts slowly for this setup; RK4 stays closest over the measured interval.

- 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: timestep meets stiffness
Map the stability boundary
Why can a controller become unstable after increasing spring stiffness without changing any other code?
The oscillator's angular frequency is . The dimensionless product compares the numerical step with the system's fastest timescale. Increasing either timestep or stiffness moves the experiment toward the bright unstable region.

This is why stiff contacts, strong PD gains, and tiny inertias can require smaller substeps even when the policy itself only acts at 60 Hz.
- 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.
Where LavenderSim performs the update
A LavenderSim policy can act at 60 Hz while the native engine divides each control interval into steps no larger than 1/240 second. Within a native substep, the engine:
- adds gravity and external forces to velocity,
- applies velocity damping,
- detects and solves constraints and contacts,
- updates position from the resulting velocity,
- integrates and normalizes orientation.
substep function and the 240 Hz subdivision loop. This is velocity-first integration plus constraint solving and mild damping—not the idealized gravity-only equation from Experiment 1.from lavendersim import Scene
from lavendersim.runtime import CodeSceneEnv
scene = Scene("Falling body", ground=False)
scene.sphere("ball", radius=.08, position=(0, 2, 0), mass=1)
with CodeSceneEnv(scene, control_dt=1/60) as sim:
sim.reset(seed=1)
for _ in range(6):
sim.step()
print(sim.time, sim.body_state()[0, [1, 8]])Run the complete version with:
python examples/tutorials/simulation/ch01_numerical_foundations/lavendersim_fall.pyPaw-check: change one thing
- Change the oscillator timestep from 0.05 to 0.10 seconds. Predict the energy plot before running it.
- Set stiffness to 160 N/m. Find a timestep where semi-implicit Euler becomes usable again.
- Give the free-fall particle an initial upward velocity. Which methods remain exact to roundoff?
- In
lavendersim_fall.py, compare control rates of 20, 60, and 120 Hz. Explain why the native maximum substep is still 1/240 second.
What should I be able to say now?
A simulator repeatedly approximates a differential equation over a finite timestep. Explicit and semi-implicit Euler are both first order, but their long-term energy behavior can differ sharply. Stability depends on the fastest system timescale, so control frequency and native substep frequency are separate design choices.