Examples¶
Copy-paste patterns for common use cases. For a step-by-step first run, see Quickstart.
Scene from an XML string¶
import mujoco
import mjswan
builder = mjswan.Builder()
project = builder.add_project(name="Demo")
spec = mujoco.MjSpec.from_string("""
<mujoco>
<worldbody>
<light diffuse=".5 .5 .5" pos="0 0 3" dir="0 0 -1"/>
<geom type="plane" size="1 1 0.1"/>
<body pos="0 0 1">
<joint type="free"/>
<geom type="sphere" size="0.1"/>
</body>
</worldbody>
</mujoco>
""")
project.add_scene(spec=spec, name="Sphere")
builder.build().launch()
Scene from a file¶
import mujoco
import mjswan
builder = mjswan.Builder()
project = builder.add_project(name="Robot")
project.add_scene(
spec=mujoco.MjSpec.from_file("robot/scene.xml"),
name="My Robot",
)
builder.build().launch()
Policy with velocity command sliders¶
import mujoco
import onnx
import mjswan
builder = mjswan.Builder()
project = builder.add_project(name="Robot")
scene = project.add_scene(
spec=mujoco.MjSpec.from_file("robot/scene.xml"),
name="G1",
)
scene.add_policy(
name="Locomotion",
policy=onnx.load("robot/locomotion.onnx"),
config_path="robot/locomotion.json",
).add_velocity_command(
lin_vel_x=(-1.5, 1.5),
lin_vel_y=(-0.5, 0.5),
default_lin_vel_x=0.5,
)
builder.build().launch()
config_path points to a JSON file describing the observation and action convention. See Policy Config Format for the schema.
Multiple policies on one scene¶
scene = project.add_scene(spec=spec, name="Go2")
scene.add_policy(
name="Policy A",
policy=onnx.load("policy_a.onnx"),
config_path="policy_a.json",
).add_velocity_command()
scene.add_policy(
name="Policy B",
policy=onnx.load("policy_b.onnx"),
config_path="policy_b.json",
).add_velocity_command()
The browser UI shows a selector for choosing between policies at runtime.
Custom command inputs¶
Pass a commands={...} dict to add_policy(). Each value is a CommandTermConfig built with mjswan.ui_command([...]) (manual UI) or mjswan.velocity_command(...) (locomotion shortcut).
scene.add_policy(
name="Locomotion",
policy=onnx.load("robot/locomotion.onnx"),
config_path="robot/locomotion.json",
commands={
"velocity": mjswan.ui_command([
mjswan.Slider("lin_vel_x", "Forward", range=(-2.0, 2.0), default=0.5, step=0.05),
mjswan.Slider("lin_vel_y", "Lateral", range=(-0.5, 0.5), default=0.0, step=0.05),
mjswan.Slider("ang_vel_z", "Yaw Rate", range=(-1.0, 1.0), default=0.0, step=0.05),
]),
},
)
See examples/tutorial/minimum_policy.py for a full example that builds a policy plus its observation / action / command config entirely from Python — no config_path required.
Multiple projects¶
Each add_project() call maps to its own URL. The first project is always the root (/); the rest live at /<id>/.
builder = mjswan.Builder(base_path="/demo/")
quadrupeds = builder.add_project(name="Quadrupeds")
quadrupeds.add_scene(spec=mujoco.MjSpec.from_file("go2/scene.xml"), name="Go2")
quadrupeds.add_scene(spec=mujoco.MjSpec.from_file("go1/scene.xml"), name="Go1")
humanoids = builder.add_project(name="Humanoids", id="humanoid")
humanoids.add_scene(spec=mujoco.MjSpec.from_file("g1/scene.xml"), name="G1")
builder.build().launch()
Gaussian Splat background (bundled)¶
Use source= to bundle a .spz file into the built application. This is the recommended approach: the file is copied into dist/ at build time, so the app works offline.
import mujoco
import mjswan
builder = mjswan.Builder()
project = builder.add_project(name="Robot")
scene = project.add_scene(
spec=mujoco.MjSpec.from_file("robot/scene.xml"),
name="G1",
)
scene.add_splat(
"Lab",
source="lab.spz", # bundled into dist/
scale=1.35,
z_offset=0.71,
)
builder.build().launch()
Gaussian Splat background (external URL)¶
Use url= to reference a .spz file hosted externally. The build stays small, but the browser must fetch the file at runtime.
Multiple splats on one scene¶
Add several splats to the same scene — the viewer shows a selector to switch between them at runtime.
scene.add_splat("Lab A", source="lab_a.spz", scale=1.35, z_offset=0.71)
scene.add_splat("Lab B", source="lab_b.spz", scale=1.20, z_offset=0.65)
Splat URL input without pre-configured splats¶
Call add_splat_section() to show the Splat selector in the control panel even when no splats are pre-configured. Users can then paste an arbitrary .spz URL at runtime.
Calibrating a new splat capture¶
Set control=True to expose live sliders for scale, offset, and rotation while you dial in the alignment. Remove control=True once the values are finalised.
scene.add_splat(
"Lab",
source="lab.spz",
scale=1.35,
z_offset=0.71,
control=True, # shows calibration controls in the viewer
)
Headless build (no browser)¶
Or without modifying the script:
See Deployment for GitHub Pages and CI/CD setup.