Skip to content

Welcome to mjswan!

mjswan Header
Real-time Interactive AI Robot Simulation in Your Browser

What is mjswan?

mjswan turns a MuJoCo model and a trained policy into an interactive web app that runs entirely in the browser — no server, no backend, no install for whoever you send it to. Physics comes from mujoco wasm, inference from ONNX Runtime Web, and rendering from three.js. The output is a static site, so GitHub Pages is enough to host it.

What makes it more than a model viewer is that the whole environment comes along. The observations your policy reads, the action term it drives, the terminations that end an episode, the events that randomize a reset — mjswan compiles those from mjlab's own Python functions to ONNX at build time and runs them beside the policy, so the browser reproduces the environment the policy was trained in.

Try it

The live demo, embedded with a single <iframe> — see Embedding. Drag to orbit, drag the robot to push it, and use the control panel to steer the policy.

Key Features

  • Real-time Simulation


    Run MuJoCo simulations and policy control in real time

  • Interactive


    Change the state of objects by applying forces with intuitive controls

  • Client-only


    All computation runs in the browser - no server required for simulation

  • Easy Sharing


    Host as a static site for effortless demo distribution (e.g., GitHub Pages)

  • Cross-platform


    Works seamlessly on desktop, mobile, and VR devices

  • Portable


    Embed the simulation in any web page or Google Colab notebook output cell

Use Cases

mjswan is perfect for:

  • Research Demos: Share your robot learning research with interactive visualizations
  • Education: Create interactive physics and robotics tutorials
  • Prototyping: Quickly test and visualize different MuJoCo models and policies
  • Portfolio: Showcase your robotics projects in an accessible way

Live Demos

Quick Example

import mujoco

import mjswan

builder = mjswan.Builder()
project = builder.add_project(name="My Robot")

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" rgba=".9 0 0 1"/>
    <body pos="0 0 1">
      <joint type="free"/>
      <geom type="box" size=".1 .2 .3" rgba="0 .9 0 1"/>
    </body>
  </worldbody>
</mujoco>
""")

project.add_scene(spec=spec, name="My Scene")

app = builder.build()
app.launch()
import mjswan

# Scene, policies, observations, actions, commands, terminations —
# all from the task and its W&B checkpoints.
app = mjswan.Builder.from_mjlab(
    "Mjlab-Velocity-Flat-Unitree-G1",
    run_path="<entity>/<project>/<run_id>",
).build()
app.launch()

Where to next

  • Quickstart


    Zero to a running simulation in two minutes

  • Core Concepts


    Builder → Project → Scene → Policy, and what each one owns

  • Using mjlab


    Visualize a trained mjlab task, checkpoints and all

  • How the Build Works


    Why your MDP terms end up as ONNX graphs, and what to do when one won't trace

License

mjswan is licensed under the Apache-2.0 License. When using mjswan, please retain attribution notices in the app to help other users discover the project.