From 6c78c98418f2958f8363ee6e61297f24c9f9be8a Mon Sep 17 00:00:00 2001 From: Matija Kecman Date: Fri, 31 Jul 2026 08:06:33 -0700 Subject: [PATCH] MuJoCo Web Viewer: update code to support the web viewer PiperOrigin-RevId: 957147035 Change-Id: Ibe5d68b2654a7faac23c297d4f492929209c078d --- python/mujoco/experimental/studio/launch_passive.py | 6 +++--- python/mujoco/experimental/studio/viewer.py | 7 +++++++ python/mujoco/experimental/studio/viewer_app.py | 3 ++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/python/mujoco/experimental/studio/launch_passive.py b/python/mujoco/experimental/studio/launch_passive.py index 215c940f..708e75f1 100644 --- a/python/mujoco/experimental/studio/launch_passive.py +++ b/python/mujoco/experimental/studio/launch_passive.py @@ -93,9 +93,7 @@ def run_viewer_target( Raises: ValueError: If the viewer mode requested in config is unknown. - NotImplementedError: If web mode is requested. """ - if config.viewer_mode == viewer_protocol.ViewerMode.NATIVE: from mujoco.experimental.studio import native_viewer # pylint: disable=g-import-not-at-top @@ -103,7 +101,9 @@ def run_viewer_target( config, viewer_endpoint, handlers=handlers ) elif config.viewer_mode == viewer_protocol.ViewerMode.WEB: - raise NotImplementedError('Web viewer not implemented yet') + from mujoco.experimental.studio import web_viewer # pylint: disable=g-import-not-at-top + + viewer = web_viewer.WebViewer(config, viewer_endpoint, handlers=handlers) else: raise ValueError(f'Unknown viewer mode: {config.viewer_mode!r}') diff --git a/python/mujoco/experimental/studio/viewer.py b/python/mujoco/experimental/studio/viewer.py index e774105d..35b4a525 100644 --- a/python/mujoco/experimental/studio/viewer.py +++ b/python/mujoco/experimental/studio/viewer.py @@ -31,6 +31,12 @@ _MJCF_PATH = flags.DEFINE_string('mjcf', None, 'Path to MJCF file.') _VIEWER = flags.DEFINE_enum_class( 'viewer', vp.ViewerMode.NATIVE, vp.ViewerMode, 'Viewer mode.' ) +_PORT = flags.DEFINE_integer( + 'port', + 0, + 'Web viewer port. 0 picks the first free port starting at 8080, so ' + 'several viewers can run side by side.', +) def main(argv: list[str]) -> None: @@ -39,6 +45,7 @@ def main(argv: list[str]) -> None: height=_HEIGHT.value, gfx=_GFX.value or '', viewer_mode=_VIEWER.value, + http_port=_PORT.value, ) # Resolve model path, if provided. diff --git a/python/mujoco/experimental/studio/viewer_app.py b/python/mujoco/experimental/studio/viewer_app.py index e294c3ab..d65c307d 100644 --- a/python/mujoco/experimental/studio/viewer_app.py +++ b/python/mujoco/experimental/studio/viewer_app.py @@ -15,6 +15,7 @@ import copy import dataclasses + import mujoco from mujoco.experimental.studio import messages from mujoco.experimental.studio import parser @@ -355,7 +356,7 @@ class ViewerApp: paused = ( self.step_control_state.get_pause_state() != sim.PauseState.UNPAUSED ) - ux.stats_gui(self.model, self.data, paused, 0.0) + ux.info_gui(self.model, self.data, paused, 0.0) imgui.End() # -- Status bar -----------------------------------------------------------