Studio web viewer is unusable: studio.web.headless_ui is missing from the wheel
Intro
Hi!
I'm from AWS. I use MuJoCo for headless physics simulation and offscreen rendering distributed across many workers, and I have been trying Studio's Python bindings to add an in-viewer UI.
My setup
Reproduced on both:
- MuJoCo 3.13.0 (PyPI wheel), Python 3.11.5, arm64, macOS 26.6.2
- MuJoCo 3.13.0 (PyPI wheel), Python 3.14.4, x86_64, Ubuntu 26.04
API: Python.
What's happening? What did you expect?
web_viewer.py imports headless_ui from mujoco.experimental.studio.web, but
that module is not present in the wheel. Selecting the web or webgl graphics
mode therefore fails.
viewer_protocol.GFX_MODES advertises both modes:
('classic', 'classic_headless', 'opengl', 'opengl_headless',
'opengl_software', 'vulkan', 'vulkan_software', 'web', 'webgl')so they are selectable but cannot work. I expected either a working web viewer, or for the unavailable modes not to be offered.
The failure also arrives awkwardly. It happens on the viewer thread, so
launch_passive returns a handle and is_running() reports True at first,
turning False only later:
launch_passive returned: ViewerHandle is_running: True
Exception in thread Thread-1 (target):
Traceback (most recent call last):
...
File ".../studio/launch_web.py", line 39, in run_web_viewer
from mujoco.experimental.studio import web_viewer
File ".../studio/web_viewer.py", line 51, in <module>
from mujoco.experimental.studio.web import headless_ui
ImportError: cannot import name 'headless_ui' from
'mujoco.experimental.studio.web' (unknown location)
is_running after 5s: FalseProcess exit status is 0, so a script that does not poll is_running() will not
notice at all.
Cause
The shipped web package contains only web_server.py:
$ ls site-packages/mujoco/experimental/studio/web/
__pycache__ web_server.pyThere is also no __init__.py, which is why the message says "unknown location".
Why it matters beyond the web viewer
On macOS this is currently the only Studio backend that avoids the main-thread window problem (filed separately), because rendering happens in the browser. With it unavailable, there is no working way to run Studio from Python on macOS without a workaround.
Suggested fix
Ship headless_ui, and an __init__.py for the web package. Alternatively,
remove web and webgl from GFX_MODES until they ship, so the failure surfaces
at configuration time rather than as a traceback on a background thread.
Steps for reproduction
pip install mujoco==3.13.0- Confirm the module is absent:
python -c "from mujoco.experimental.studio.web import headless_ui" - Or go through the documented path: save
minimal.xmlandrepro.pybelow into the same directory and runpython repro.py
Minimal model for reproduction
The model is not material to this bug, since the failure occurs during viewer startup before the model is used. Included for completeness, loadable as-is with no binary assets:
<mujoco model="studio_repro">
<option timestep="0.002"/>
<worldbody>
<light pos="0 0 2"/>
<camera name="fixed" pos="0.8 -0.8 0.6" xyaxes="1 1 0 -0.4 0.4 1"/>
<geom name="floor" type="plane" size="2 2 0.05" rgba="0.4 0.45 0.5 1"/>
<body name="box" pos="0 0 0.3">
<freejoint name="box_free"/>
<geom name="box" type="box" size="0.05 0.05 0.05" rgba="0.8 0.2 0.2 1"/>
</body>
</worldbody>
</mujoco>Code required for reproduction
Two lines are enough:
from mujoco.experimental.studio.web import headless_uiOr via the public API, repro.py:
import time
import mujoco
from mujoco.experimental.studio import launch_passive, viewer_protocol
model = mujoco.MjModel.from_xml_path("minimal.xml")
handle = launch_passive.launch_passive(
viewer_protocol.ViewerConfig(title="studio web repro", gfx="web", http_port=8099)
)
print("launch_passive returned:", type(handle).__name__, "is_running:", handle.is_running())
time.sleep(5) # give the viewer thread time to fail
print("is_running after 5s:", handle.is_running())The time.sleep matters: without it the script exits before the viewer thread
reaches the failing import, and nothing is printed at all.
Confirmations
- I searched the latest documentation thoroughly before posting.
- I searched previous Issues and Discussions, I am certain this has not been raised before.
Source: google-deepmind/mujoco