---
title: "Start crowd replay from a snapshot in C#"
url: https://recursiveai.org/onepath/cookbook/start-crowd-playback-from-snapshot/
description: "A setup snapshot lets a new playback crowd start at the current step instead of simulating the run from its beginning."
version: "1.2.0"
---

# Start crowd replay from a snapshot in C#

**OnePath** is a free, open-source pathfinding library for .NET from Recursive AI. MIT or Apache-2.0, no native dependencies.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OnePath) · [GitHub](https://github.com/recursive-ai-oss/onepath-csharp) · [Docs](https://recursiveai.org/onepath/docs/) · [Download example (.zip)](https://recursiveai.org/onepath/cookbook/start-crowd-playback-from-snapshot/start-crowd-playback-from-snapshot.zip)

A setup snapshot starts a new playback crowd at the current step. It holds the time step, crowd options, navigation mesh and crowd state.

```shell
dotnet add package RecursiveAI.OnePath
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

```csharp
using System.Buffers;
using RecursiveAI.OnePath.Replay;

// recorder is your live CrowdRecorder.
var snapshot = new ArrayBufferWriter<byte>();
recorder.WriteSnapshot(snapshot);
var player = new CrowdPlayer(snapshot.WrittenSpan);
Console.WriteLine(player.Crowd.StepCount);
```

```text
10
```

Unzip [the example](https://recursiveai.org/onepath/cookbook/start-crowd-playback-from-snapshot/start-crowd-playback-from-snapshot.zip), then `dotnet run`.

With the full program’s live recorder at step 10, the snippet takes a current setup snapshot and starts another player at that step. The program also saves that snapshot as `setup.bin` in the example zip.

## The full program

The program creates its own floor geometry and crowd in code; it needs no external assets. `RecursiveAI.OneMath`, used for the geometry types, is installed automatically as a dependency of OnePath. It creates the live recorder used by the snippet at the top and writes its snapshot to `setup.bin`.

```csharp
using System.Buffers;
using RecursiveAI.OneMath;
using RecursiveAI.OnePath;
using RecursiveAI.OnePath.Replay;

var floor = new[]
{
    new Triangle3D(new(0, 0, 0), new(0, 0, 40), new(40, 0, 0)),
    new Triangle3D(new(40, 0, 0), new(0, 0, 40), new(40, 0, 40))
};
var crowd = NavMesh.Build(floor, NavSettings.Humanoid).CreateCrowd();
var recorder = new CrowdRecorder(crowd, 1.0 / 30, keepSteps: true);
var walker = recorder.Add(new(5, 0, 5));
recorder.SetDestination(walker, new(30, 0, 30));

for (int i = 0; i < 10; i++) recorder.Step();
var snapshot = new ArrayBufferWriter<byte>();
recorder.WriteSnapshot(snapshot);
File.WriteAllBytes("setup.bin", snapshot.WrittenSpan.ToArray());
var player = new CrowdPlayer(File.ReadAllBytes("setup.bin"));
Console.WriteLine($"Starts at step: {player.Crowd.StepCount}");
recorder.Step();
player.Step();
Console.WriteLine($"Continued at step: {player.Crowd.StepCount}");
Console.WriteLine($"Hash matches: {player.Crowd.StateHash() == crowd.StateHash()}");
```

`dotnet run` prints:

```text
Starts at step: 10
Continued at step: 11
Hash matches: True
```

Malformed setup or replay bytes throw `CrowdReplayException`; edits to the mesh while recording make `recorder.Step()` throw `InvalidOperationException`.

## Variations

Read the time step restored from the setup. This prints `True`.

```csharp
// After creating player in the full program:
Console.WriteLine(player.Dt == recorder.Dt);
```

## Related recipes

- [Record and play a crowd simulation replay in C#](https://recursiveai.org/onepath/cookbook/record-and-play-crowd-replay/)
- [Find a crowd replay state hash mismatch in C#](https://recursiveai.org/onepath/cookbook/find-crowd-replay-hash-mismatch/)
- [Publish live crowd simulation replay records in C#](https://recursiveai.org/onepath/cookbook/publish-live-crowd-replay-records/)
- [Restore a crowd’s saved state](https://recursiveai.org/onepath/cookbook/crowd-rollback/)

## The short answer

Call `CrowdRecorder.WriteSnapshot` to write the current setup, then pass those bytes to `new CrowdPlayer(...)`. The player restores a new mesh and crowd at the saved step. It does not rewind the existing crowd. Use this when a consumer joins a running simulation and needs its present state before receiving future commands.

## How do I start crowd replay from the current simulation state?

The full program first records movement for ten steps. It then writes the setup to an `ArrayBufferWriter<byte>` and saves those bytes as `setup.bin`. Constructing the player restores step 10. One more step on both sides produces step 11 with matching state hashes, because neither side receives another command between those steps.

The setup is more than a list of positions. It contains the recorder’s time step, start-step number, crowd options, a full navigation-mesh snapshot and the crowd’s saved state. `CrowdPlayer` reconstructs them together. It also checks that the restored crowd’s step count equals the setup’s start step. You do not reset numbering to zero when attaching mid-run.

## Is a replay snapshot the same as crowd rollback?

No. A player owns a newly restored crowd and mesh. Restoring saved state into an existing crowd is a separate rollback workflow. A setup snapshot is useful when the consumer does not already have the same mesh and simulation options, because those are embedded in the setup.

`WriteSnapshot` captures the crowd as it is now. By contrast, a retained replay’s `Setup` is the state from when its recorder was constructed. If that recorder has advanced, exporting its replay does not turn its original setup into a current snapshot. Choose current snapshot for a new live attachment; choose the replay’s original setup when you intend to play all its saved steps.

## How do I continue playback after a snapshot?

The next commands must describe changes after the captured state. Apply those operations in order and advance the player once for each corresponding host step. Do not resend commands already reflected in the snapshot; that would apply them twice. The example uses an unchanged destination after the snapshot, so a single step on each side is enough to demonstrate continuation.

For a live consumer, take the snapshot at a consistent boundary: the crowd must be exactly as it was after the last record sent to that consumer. Serialize snapshot creation and recorder operations on one thread. The [live-record documentation](https://recursiveai.org/onepath/docs/replay/) describes that boundary. A snapshot does not subscribe the player to later changes automatically, and it supplies no network connection. Your application must deliver subsequent records and retain their order. Query filters are not serialized; the restored crowd uses none, so filtered runs are not reproduced by this setup alone.
