Skip to content

Add Viser frame controls for trajectory replay - #466

Merged
yuecideng merged 3 commits into
mainfrom
feat/viser-replay-control
Aug 7, 2026
Merged

yuecideng merged 3 commits into
mainfrom
feat/viser-replay-control

Conversation

@yuecideng

Copy link
Copy Markdown
Contributor

Description

This PR adds an expanded Replay control panel to Viser when run-env uses --replay_mode control --viser. Its integer frame slider seeks directly to recorded trajectory steps, follows terminal-driven playback, and pauses auto-play when the browser requests a frame.

Browser callbacks feed a latest-only integer queue, while ReplayWrapper.go_to_step() remains on the replay thread. Viser handle creation and updates remain on the visualization worker thread. The implementation intentionally avoids new protocol exports and external dependencies.

Dependencies: None.

Issue reference: N/A.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Enhancement (non-breaking change which improves an existing functionality)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (existing functionality will not work without user modification)
  • Documentation update

Screenshots

Not included. The Viser slider lifecycle and browser callback path are covered by backend tests.

Validation

  • black .
  • black --check --diff --color ./
  • pytest -q tests/visualization tests/lab/scripts/test_run_env.py -x — 60 passed
  • pytest -q tests/gym/envs/test_replay.py::test_control_loop_uses_single_keys_and_auto_can_be_interrupted -m slow — 1 passed
  • sphinx-build -b html docs/source <temp-dir> — succeeded with 668 pre-existing warnings

Checklist

  • I have run the black . command to format the code base.
  • I have made corresponding changes to the documentation.
  • I have added tests that prove this feature works.
  • Dependencies have been updated, if applicable.

Synchronize control-mode trajectory replay with a latest-only browser frame slider while keeping simulation writes on the replay thread.
Copilot AI lite review requested due to automatic review settings August 6, 2026 17:01
@yuecideng yuecideng added enhancement Improvements to existing capabilities; use feature for new capabilities. visualization Browser viewers, markers, trajectory previews and interactive debugging displays. labels Aug 6, 2026
@greptile-apps

greptile-apps Bot commented Aug 6, 2026 •

Copy link
Copy Markdown

Greptile Summary

The PR adds browser-based frame seeking and synchronization for trajectory replay while retaining replay-state mutations on the replay thread and Viser updates on its worker thread.

  • Adds an integer Replay control slider that follows terminal playback and submits latest-only browser seeks.
  • Extends runtime and backend interfaces to transport replay state and commands across threads.
  • Updates replay documentation and adds loop, runtime, and Viser lifecycle tests.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
embodichain/lab/scripts/run_env.py Multiplexes terminal and browser replay commands, centralizes clamped seeking, and publishes slider visibility and progress.
embodichain/lab/visualization/runtime.py Adds latest-only replay-state and command queues while preserving backend updates on the visualization worker.
embodichain/lab/visualization/backends/viser.py Adds the Replay control slider, lifecycle handling, command dispatch, and suppression of server-originated callback feedback.
embodichain/lab/visualization/backends/base.py Extends the backend contract with replay command-sink registration and replay-state publication.
tests/lab/scripts/test_run_env.py Verifies that a browser seek is consumed while terminal replay is paused.
tests/visualization/test_runtime.py Verifies latest-only replay transport and visualization-thread publication.
tests/visualization/test_viser_backend.py Covers client seeks, suppression of server-originated updates, slider synchronization, and removal.
docs/source/guides/run_env.md Documents browser-based control replay and its interaction with auto-play.
docs/source/overview/sim/viser_visualization.md Documents the Viser Replay control panel and cross-thread command behavior.

Sequence Diagram

sequenceDiagram
    participant Browser
    participant Viser as Viser worker
    participant Queue as Latest-only queues
    participant Replay as Replay thread
    Browser->>Viser: Change Frame slider
    Viser->>Queue: Enqueue newest requested step
    Replay->>Queue: Drain browser seek
    Queue-->>Replay: Requested step
    Replay->>Replay: go_to_step(step)
    Replay->>Queue: Publish replay state
    Queue-->>Viser: Current step and range
    Viser->>Browser: Synchronize Frame slider
Loading

Reviews (2): Last reviewed commit: "wip" | Re-trigger Greptile

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds trajectory replay controls to the Viser visualization flow, enabling browser-based frame seeking during replay and synchronizing state between terminal replay control and the Viser UI.

Changes:

  • Introduces a replay “Frame” slider in ViserBackend and an event sink for browser seek commands.
  • Extends VisualizationRuntime to publish replay slider state asynchronously and coalesce browser seek commands.
  • Updates run-env replay control loop to consume browser seeks (including while paused) and keep UI state synchronized; documents the feature.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/visualization/test_viser_backend.py Adds unit tests for replay slider creation, seek emission, progress tracking, and hiding behavior.
tests/visualization/test_runtime.py Adds tests validating replay control state publishing and coalescing of seek commands.
tests/lab/scripts/test_run_env.py Adds a test ensuring browser seeks are consumed while terminal replay is paused.
embodichain/lab/visualization/runtime.py Adds runtime queues / APIs for replay control state publishing and seek command draining.
embodichain/lab/visualization/backends/viser.py Implements replay control folder/slider UI, event handling, and state updates in Viser.
embodichain/lab/visualization/backends/base.py Adds backend hooks for replay control seeks and replay slider state publishing.
embodichain/lab/scripts/run_env.py Integrates visualization runtime replay controls into terminal replay loop and updates headless messaging.
docs/source/overview/sim/viser_visualization.md Documents the Replay control panel behavior and synchronization semantics.
docs/source/guides/run_env.md Updates control-mode replay guide to mention terminal-or-Viser usage and behavior details.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +324 to +335
def read_key(timeout: float | None) -> str | None:
nonlocal terminal_active
if not terminal_active:
time.sleep(timeout or _REPLAY_CONTROL_POLL_INTERVAL)
return None
try:
return control_input.read_key(timeout=timeout)
except EOFError:
if visualization_runtime is None:
raise
terminal_active = False
return None
Comment on lines +1163 to +1169
elif event.category == "replay_control":
state = self._replay_control_state
sink = getattr(self, "_replay_control_command_sink", None)
if state is not None and state[2] and sink is not None:
target_step = max(0, min(int(event.value), state[1]))
self._replay_control_slider.value = target_step
sink(target_step)
Copilot AI review requested due to automatic review settings August 7, 2026 07:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Suppressed comments (1)

embodichain/lab/visualization/backends/viser.py:1531

  • publish_replay_control() applies _apply_gui_events() before syncing self._replay_control_slider.value. If a browser seek event is processed there, _apply_gui_events() sets the slider to the requested target_step, but the later self._replay_control_slider.value = step can immediately overwrite it with a stale server-side step (until the replay thread catches up). This can make the slider jump back while dragging.

One simple fix is to process queued GUI events after updating/removing/registering the replay controls so browser-originated updates win the last write for that tick.

        elif self._replay_control_slider is None or previous_max_step != max_step:
            self._remove_replay_control()
            self._register_replay_control()
        else:
            self._replay_control_slider.value = step

@yuecideng
yuecideng merged commit 8017f00 into main Aug 7, 2026
9 of 10 checks passed
@yuecideng
yuecideng deleted the feat/viser-replay-control branch August 7, 2026 09:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Improvements to existing capabilities; use feature for new capabilities. visualization Browser viewers, markers, trajectory previews and interactive debugging displays.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants