Early-access beta · part of XRForge

Record an XR take once. Replay it forever.

Record XR movement in Unity Play Mode, save it as a take, and replay or loop it through the XR Device Simulator without putting the headset back on.

A take recorded once, looping in the Device Simulator.

Headset fatigue is a development cost

You have put the headset on forty times to test the same grab. Every iteration on a VR interaction costs the same tax: headset on, find the object, perform the motion, headset off, read the console. Change one line, pay it again. The motion is never quite identical, so neither is the bug.

How XR replay works in Unity

  1. 01

    Record a take

    Start recording, enter Play Mode, perform the interaction once. Head and controller motion is captured frame by frame.

  2. 02

    Save it

    Takes are saved into the project, so they can be committed next to the scenes they exercise.

  3. 03

    Loop it in the Simulator

    Replay the take through the XR Interaction Simulator — identically, every time, with the headset on the desk.

Built for XR testing, QA and debugging in Unity

Debugging Implemented

Reproduce a bug by replaying the exact take that caused it, instead of trying to perform the same motion by hand.

QA and regression Implemented

Run yesterday's take against today's build and see what changed.

Develop anywhere Implemented

Leave the headset on the desk. Develop and test XR interactions from your keyboard, anywhere — plane, cafe, couch.

Automated testing Implemented

Takes become fixtures. Replay them in play-mode tests and in CI, and fail the build when a scene edit changes what a take does.

Trailer capture Planned

Repeatable camera and hand motion, so you can retake the lighting without retaking the performance.

MCP agent control Planned

Let AI agents drive the simulator through MCP — automate testing and exploration without human input.

Implemented

Automated XR regression testing in CI

A take replayed against the wall clock is a demo. Replayed against a virtual clock it is a fixture: the same events on the same frames, on your desk and on a loaded build runner. That is what makes a recording assertable. Replay records what the scene did as a signal trace, compares it against an approved golden, and exits non-zero when a scene edit changes the answer.

SixShots.golden.json approved

  1. 0.000 select.enter Revolver <- RightHand
  2. 0.514 activate Revolver
  3. 0.520 rounds Revolver -> 5
  4. 1.208 activate Revolver
  5. 1.214 rounds Revolver -> 4
  6. 1.902 activate Revolver
  7. 1.908 rounds Revolver -> 3
  8. not in the golden
  9. 2.640 select.exit Revolver <- RightHand

SixShots.actual.json this run

  1. 0.000 select.enter Revolver <- RightHand
  2. 0.514 activate Revolver
  3. 0.520 rounds Revolver -> 5
  4. 1.243 activate Revolver drifted +0.035s
  5. 1.214 rounds Revolver -> 4
  6. 1.902 activate Revolver
  7. missing — the round never dropped
  8. 2.114 activate Revolver extra
  9. 2.640 select.exit Revolver <- RightHand

FAIL SixShots — 3 mismatches: 1 drifted past tolerance, 1 missing, 1 extra. Exit code 1.

A comparison reports four distinct failures — missing, extra, out of order, and drifted beyond tolerance. Default tolerance is one frame. On a mismatch the run writes the actual trace beside the golden, so CI can publish it and you can read the diff locally.

Signals come from XRI itself

Select, hover and activate are read off the XR Interaction Toolkit's own event stream, including objects spawned mid-take. Nothing to instrument, nothing added to your build.

Goldens review like code

A golden is plain JSON beside the take. It reads in a pull request, and re-baselining is always an explicit act — never a silent rewrite on failure.

A report that travels

Every run can be copied as a markdown report: the versions to reproduce it, each take's first divergence, rig drift, and the errors the game logged. Paste it into a ticket as it is.

Headless, at machine speed

A 60-second take does not cost 60 seconds of CI. Playback steps a fixed delta per frame, so physics and timers advance identically and the take runs as fast as the runner allows.

// One test covers every take that has a golden.
[UnityTest]
public IEnumerator EveryTake_MatchesItsGolden(
        [ValueSource(typeof(XRReplayTest), nameof(XRReplayTest.TakesWithGoldens))] string take) {
    yield return new EnterPlayMode();
    var run = XRReplayTest.Run(take);   // opens the capture scene
    yield return run;
    run.AssertMatchesGolden();
    yield return new ExitPlayMode();
}

Before that job runs, a batchmode preflight checks in seconds that every take still has its golden, its recording and its capture scene — so a take committed without its trace fails fast instead of after a full play-mode run.

Git-friendly takes your team can share

A take is an .asset profile and its .inputtrace recording, plus a .golden.json once it has a baseline — written into a project-relative folder. They commit to version control next to the scenes they exercise, which turns a bug report into something a teammate can replay rather than re-enact. A reviewer runs the take instead of reading repro steps and hoping they perform the same motion.

Requirements

XRForge Replay is an Editor-only Unity package. It needs Unity 6.3 (6000.3) or newer and XR Interaction Toolkit 3.4.1 or newer. The XR Interaction Simulator it drives ships with XRI as an importable sample.

Early access

The documentation is open to early-access users now. If you have the access password, it is behind this link.

Read the docs