# How to Record and Playback Simulations in DART: A Complete Guide

> Learn to record and playback simulations in DART with this complete guide. Explore essential components like Recording, World, and Viewer for efficient simulation management.

- Repository: [DART: Dynamic Animation and Robotics Toolkit/dart](https://github.com/dartsim/dart)
- Tags: how-to-guide
- Published: 2026-02-28

---

**DART provides a lightweight record-and-replay mechanism built around three core components: `dart::simulation::Recording` for state storage, `dart::simulation::World` for automatic frame capture via `bake()`, and `dart::gui::Viewer` for image sequence recording and playback controls.**

The Dynamic Animation and Robotics Toolkit (DART) includes native facilities to record and playback simulations without external dependencies. This guide explains how to capture full physics states—including positions, velocities, and contact forces—and replay them frame-by-frame using the built-in API.

## Core Components for Recording and Playback

DART’s recording architecture separates state storage from simulation management and visualization. Understanding these three classes is essential before implementing record and playback simulations in DART.

### Recording Class (dart/simulation/recording.hpp)

The **`Recording`** class maintains a time-ordered vector of baked simulation states. Each frame stores all degrees of freedom (DOFs) for every skeleton followed by contact information (contact points and forces). Key methods include `addState()` for appending frames, `clear()` for resetting the buffer, and `getConfig()` for retrieving specific skeleton configurations during playback.

### World Integration (dart/simulation/world.hpp)

The **`World`** class owns a `Recording` instance accessible via `getRecording()`. The critical method **`World::bake()`** (implemented in [`dart/simulation/world.cpp`](https://github.com/dartsim/dart/blob/main/dart/simulation/world.cpp), lines 528-535) extracts every skeleton’s DOFs and current contacts, constructs a single `Eigen::VectorXd` state vector, and forwards it to the recording buffer. This method is designed to be called once per simulation step.

### Viewer Controls (dart/gui/viewer.hpp)

The **`Viewer`** class provides user-level recording utilities including `record()` for image capture, `pauseRecording()` for halting output, and `switchDefaultEventHandler()` for toggling interactive playback modes. These features are demonstrated in the Rigid Cubes example ([`examples/rigid_cubes/main.cpp`](https://github.com/dartsim/dart/blob/main/examples/rigid_cubes/main.cpp)), where the *p* key toggles playback state.

## Recording a Simulation in DART

To capture a simulation, retrieve the world's recording object and call `bake()` after each physics step. The following pattern works for both headless and interactive applications:

```cpp
#include <dart/dart.hpp>
#include <dart/io/io.hpp>

// Load a world from a .skel file
auto world = dart::io::readWorld("dart://sample/skel/cubes.skel");

// Access and prepare the recording buffer
auto* rec = world->getRecording();
rec->clear();  // Ensure empty buffer before starting

// Main simulation loop
for (int i = 0; i < 1000; ++i) {
  world->step();  // Advance physics by one time step
  world->bake();  // Capture current state to recording
}

```

Each call to `World::bake()` automatically serializes the current simulation state into the `Recording` object. You do not need to manually construct state vectors—the `World` class handles extraction of positions, velocities, and contact data according to the implementation in [`dart/simulation/world.cpp`](https://github.com/dartsim/dart/blob/main/dart/simulation/world.cpp).

## Playing Back Recorded Simulations

Playback requires manually retrieving frames from the `Recording` object and applying them to skeletons. This approach gives you full control over playback speed, frame skipping, or reverse playback:

```cpp
auto* rec = world->getRecording();
int nFrames = rec->getNumFrames();

// Playback loop at 60 Hz
const double dt = 1.0 / 60.0;
for (int i = 0; i < nFrames; ++i) {
  // Restore each skeleton's position
  for (size_t sk = 0; sk < rec->getNumSkeletons(); ++sk) {
    Eigen::VectorXd cfg = rec->getConfig(i, sk);
    world->getSkeleton(sk)->setPositions(cfg);
  }
  
  // Render or process the frame
  viewer.frame();
  std::this_thread::sleep_for(std::chrono::duration<double>(dt));
}

```

The `Recording::getConfig(frameIndex, skeletonIndex)` method returns the generalized coordinates for a specific skeleton at a specific frame. For per-DOF access, use `Recording::getGenCoord()` instead.

## Viewer-Level Playback Controls

For interactive applications, DART provides a simple playback toggle mechanism. The Viewer’s `switchDefaultEventHandler()` method enables or disables the default event handler, effectively pausing automatic simulation while allowing manual frame advancement:

```cpp
// Toggle playback with the 'p' key (from examples/rigid_cubes/main.cpp)
case 'p':
  eventHandlerOn = !eventHandlerOn;
  mViewer->switchDefaultEventHandler(eventHandlerOn);
  break;

```

When `switchDefaultEventHandler(false)` is called, the simulation stops stepping automatically, but the viewer continues to render, allowing you to manually step through frames using spacebar or GUI controls.

## Recording Image Sequences

To capture visual output alongside physics data, use `Viewer::record()` before running your simulation loop:

```cpp
// Configure headless viewer (optional)
dart::ui::ViewerConfig config = dart::ui::ViewerConfig::headless(640, 480);
dart::gui::Viewer viewer(config);

// Enable image capture with zero-padded filenames
viewer.record("output/frames", "frame_", false, 6);

// Run simulation with both physics and image recording
for (int i = 0; i < 200; ++i) {
  world->step();
  world->bake();   // Record physics state
  viewer.frame();  // Capture image
}

```

This writes PNG files to `output/frames/` with names like `frame_000001.png`. The sixth parameter specifies the zero-padding width for frame numbering.

## Analyzing Contact Data from Recordings

The `Recording` class stores collision information alongside kinematic data. Access contact points and forces for detailed analysis:

```cpp
for (int f = 0; f < rec->getNumFrames(); ++f) {
  int nContacts = rec->getNumContacts(f);
  for (int c = 0; c < nContacts; ++c) {
    Eigen::Vector3d point = rec->getContactPoint(f, c);
    Eigen::Vector3d force = rec->getContactForce(f, c);
    
    // Process contact data (logging, visualization, etc.)
    processContact(point, force);
  }
}

```

Contact data is packed into the state vector immediately after the DOF data for each frame, as defined in the serialization logic within [`dart/simulation/recording.cpp`](https://github.com/dartsim/dart/blob/main/dart/simulation/recording.cpp).

## Summary

- **`dart::simulation::Recording`** stores time-ordered simulation states including DOFs and contact forces in [`dart/simulation/recording.hpp`](https://github.com/dartsim/dart/blob/main/dart/simulation/recording.hpp).
- **`World::bake()`** in [`dart/simulation/world.cpp`](https://github.com/dartsim/dart/blob/main/dart/simulation/world.cpp) automatically captures the complete simulation state after each physics step.
- **Playback** requires manually retrieving frames via `Recording::getConfig()` and applying them with `Skeleton::setPositions()`.
- **`dart::gui::Viewer`** provides `record()` for image sequences and `switchDefaultEventHandler()` for interactive playback toggling.
- **Contact analysis** is supported through `getContactPoint()` and `getContactForce()` methods on the Recording object.

## Frequently Asked Questions

### How do I enable simulation recording in DART?

Recording is automatically available through any `World` instance. Simply call `world->getRecording()` to access the `Recording` object, optionally invoke `clear()` to reset the buffer, then call `world->bake()` immediately after each `world->step()` in your simulation loop. No additional setup or file I/O is required during the recording phase.

### What data is stored in a DART Recording?

Each frame stores all degrees of freedom (generalized coordinates) for every skeleton in the world, followed by contact information including contact points and contact forces. The data is stored as `Eigen::VectorXd` objects in a time-ordered vector, accessible via `getConfig()` for kinematic data or `getContactPoint()`/`getContactForce()` for collision data.

### How can I save video frames during DART simulation?

Use `dart::gui::Viewer::record(directory, prefix, overwrite, numDigits)` before your simulation loop to enable image capture. Each call to `viewer.frame()` during the loop saves a PNG file to the specified directory. This works in both interactive and headless modes configured through `ViewerConfig::headless()`.

### Can I playback a DART simulation at different speeds?

Yes. Since playback is manual—requiring you to retrieve frames via `Recording::getConfig()` and apply them with `setPositions()`—you control the timing. Insert `std::this_thread::sleep_for()` between frames for slow-motion playback, skip frames for fast-forward, or iterate backwards through the buffer for reverse playback. The `Viewer::switchDefaultEventHandler()` toggle only provides play/pause functionality, not speed control.