Skip to content

Animation and movies

Peter Corke edited this page Aug 19, 2026 · 1 revision

Animation and movies

Live animation and movie recording are two independent things in bdsim:

  • Live animation (animation=True, +a) updates the display window during a run, one frame per integration step or clock tick.
  • Movie recording (movie= on a block, or --movies DIR/-m DIR globally) writes MP4 output at a steady frame rate, and works whether or not you're watching a live window — including fully headless, on a server with no display at all.

Recording a movie

Two ways to enable it:

  • Globally: --movies DIR / -m DIR (default . if no directory is given) automatically assigns a movie file to every graphics block in the diagram, named after the block.
  • Per block: any GraphicsBlock subclass (SCOPE, SCOPEXY, ANIMATION, ...) accepts movie="filename.mp4" directly, eg. bd.ANIMATION(init=..., update=..., movie="out.mp4").

Some things worth knowing:

  • Requires ffmpeg on PATH — bdsim raises a clear error ("cannot save movie, please install ffmpeg") rather than failing silently if it's missing.
  • Frame rate follows the animation_rate option (default 20 Hz, see Runtime options). If movies are enabled and you haven't set dt explicitly, bdsim sets it to 1/animation_rate automatically so output samples land cleanly on the frame grid. This same rate also paces how often the integrator gets interrupted and restarted — see Time stepping for the mechanism and why a needlessly high rate costs real integrator overhead.
  • --movies/movie= imply graphics=True — they can't be combined with -g/graphics=False.
  • Pass timestamp=True to any graphics block to burn the simulation time into each rendered frame.

Headless / server / CI operation

You don't need a live animated window to record a movie — useful on a server with no display, or in CI. Set a non-interactive Matplotlib backend (backend="Agg" in code, --backend Agg on the command line, or MPLBACKEND=Agg in the environment) and bdsim drives the frame-grab loop at animation_rate even with animation=False:

import bdsim

sim = bdsim.BDSim(backend="Agg", animation=False, hold=False)
bd = sim.blockdiagram()

# ... build the diagram ...
anim = bd.ANIMATION(init=my_init, update=my_update, movie="out.mp4")
# ... connect it ...

bd.compile()
sim.run(bd, T=10, dt=1 / 20)
bd.done()   # flush and finalize the movie writer

Headless movie recording without a live window — exactly this pattern — was broken until PR #36 (thanks, @PhotonicVelocity), which also fixed several related frame-timing bugs: a missing t=0 frame, duplicated/missing timestamps around long runs, and an MP4 frame-rate mismatch that made a 5 s simulation render as a 6.9 s video. If movie output looks off in timing or frame count, make sure that fix has landed.

Don't use BDSIM_NO_GRAPHICS=1 for this — see Environment variables. That variable unconditionally forces graphics=False, animation=False and movies=None, killing movie output along with everything else. It's for CI runs that want no graphical output whatsoever, not for headless movie generation — use backend="Agg" instead.

Worked example

examples/cartpole.py drives an ANIMATION block with a custom init/update pair — a cart-pole rendered with Matplotlib patches and transforms. See it running here: cart-pole animation video. To render a movie instead of (or as well as) watching it live, add movie="cartpole.mp4" to its ANIMATION(...) call.

See also

  • Notebook animation for how live animation and the notebook-specific display path work.
  • Graphics blocks for GraphicsBlock internals (_start_movie, lifecycle hooks) if you're writing a custom animated block.
  • Runtime options for the full CLI options table.

Clone this wiki locally