Skip to content

Recording & Replay

Quantum state lives in WASM memory and cannot be directly serialized. The recording system solves this by logging every state-mutating quantum operation. On replay, the log recreates identical quantum state — measurements are forced to their recorded outcomes via forcedMeasureProperties.

Starting a Recording

typescript
// Begin recording — all gate wrappers will log operations
registry.startRecording();

// ... gameplay happens ...
// registry.cycle(), registry.iSwap(), registry.measureProperties()
// — all automatically logged

// Stop and get the log
const log: QuantumOperation[] = registry.stopRecording();

Save / Load

typescript
import type { QuantumOperation } from "quantum-forge-framework/quantum";

// SAVE
const saveData = {
  gameState: engine.getState(),
  quantumLog: registry.stopRecording(),
};
localStorage.setItem("save", JSON.stringify(saveData));

// LOAD
const loaded = JSON.parse(localStorage.getItem("save")!);
engine.setState(loaded.gameState);
registry.replayLog(loaded.quantumLog);
// Quantum state is now identical to the moment of save

How Replay Works

replayLog() performs these steps:

  1. Clears all existing properties, ID mappings, and pool
  2. Replays each operation in order:
    • acquire → creates a fresh property or pops from the replay pool
    • cycle, hadamard, iSwap, etc. → applies the gate to recreated handles
    • measure → calls forcedMeasureProperties with the recorded outcome
    • release → resets to |0⟩ and returns to pool
    • assign / unassign → restores ID-to-handle mappings
  3. After replay, getProperty("ball-1") returns the correct handle in the correct quantum state

The key insight: because measurements are forced to recorded outcomes, the quantum state evolves identically. The same gates + same measurement results = same final state.

Recording API

MethodDescription
startRecording()Begin recording; resets log; assigns indices to existing handles
stopRecording()Stop recording; return QuantumOperation[]
isRecording()Check if recording is active
getOperationLog()Get a copy of the log (works even while recording)
replayLog(ops)Clear all state and replay from scratch

Operation Types

The QuantumOperation union type covers all state mutations:

typescript
type QuantumOperation =
  | { op: "acquire"; index: number }
  | { op: "release"; index: number; value: number }
  | { op: "assign"; index: number; id: string }
  | { op: "unassign"; id: string }
  | { op: "cycle"; index: number; fraction: number }
  | { op: "shift"; index: number; fraction: number }
  | { op: "clock"; index: number; fraction: number }
  | { op: "hadamard"; index: number; fraction: number }
  | { op: "inverse_hadamard"; index: number }
  | { op: "i_swap"; index1: number; index2: number; fraction: number }
  | { op: "swap"; index1: number; index2: number }
  | { op: "phase_rotate"; indices: number[]; values: number[]; angle: number }
  | { op: "y"; index: number; fraction: number }
  | { op: "measure"; indices: number[]; outcomes: number[] }
  | { op: "measure_predicate"; indices: number[]; values: number[]; outcome: number }
  | { op: "reset"; index: number; value: number };

Each WASM property handle is mapped to a sequential integer index for serialization. On replay, fresh handles are created and mapped back.

What Gets Recorded

Recorded (state-mutating gate wrappers):

  • this.cycle(), this.shift(), this.x()
  • this.hadamard(), this.inverseHadamard()
  • this.clock(), this.z()
  • this.y()
  • this.iSwap(), this.swap()
  • this.phaseRotate()
  • this.measureProperties(), this.measurePredicate()
  • this.forcedMeasureProperties()
  • this.resetProperty()
  • this.acquireProperty(), this.releaseProperty()
  • this.setProperty(), this.deleteProperty()

Not recorded (read-only queries):

  • this.getModule().probabilities()
  • this.getModule().reduced_density_matrix()
  • this.getProperty(), this.hasProperty()

This is correct — read-only operations don't change state, so they don't need to be replayed.

Snapshot vs Continuous Recording

You can get a snapshot of the log without stopping recording:

typescript
registry.startRecording();

// ... gameplay ...

// Periodic auto-save without interrupting recording
const snapshot = registry.getOperationLog();
autoSave({ gameState: engine.getState(), quantumLog: snapshot });

// ... more gameplay ...

// Final save
const finalLog = registry.stopRecording();

Tips

When to start recording

Start recording at the beginning of a "saveable" state — typically when the game begins or when entering a new level. Everything before startRecording() is lost.

Recording overhead

Recording adds minimal overhead — it's just appending to an array. Only enable it when you need save/load or replay. Normal gameplay without save/load doesn't need recording.

Log size

The log grows linearly with the number of quantum operations. For long game sessions, consider periodic checkpoints: stop recording, save the log, start a new recording.

Powered by Quantum Forge