Picking a physics simulator is usually a first-week decision for a robot learning team, and it quietly locks in the robot description format, the GPU budget and the shape of the training code. This article compares MuJoCo, Isaac Lab, PyBullet and Genesis World on the same criteria and ends with a shortlist per task type. Moving a simulation-trained policy onto real hardware is a separate problem, covered in closing the sim-to-real gap.
Five criteria that actually separate the candidates
Most simulator comparisons stop at “which one is fastest”, but speed rankings flip depending on the robot model, the number of contacts, whether rendering is on and which GPU is used. This comparison therefore sticks to structural differences that can be confirmed in official documentation:
- Contact model and solvers — what mathematical problem contact is reduced to, and which solvers you can choose.
- GPU parallelism — whether thousands of environments can run on one GPU, and on which hardware.
- Sensors and rendering — whether RGB, depth or ray-cast observations can be produced inside the training loop.
- License and maintenance — whether the code can ship in a commercial product and whether it is still actively updated.
- Install requirements — operating system, GPU memory and driver constraints.
The four simulators side by side
| Criterion | MuJoCo (+MJX) | Isaac Lab | PyBullet | Genesis World |
|---|---|---|---|---|
| License | Apache 2.0 | BSD-3 (some workflows pull in Isaac Sim or other proprietary terms) | zlib | Apache 2.0 |
| Contact | Convex-optimization soft contact; Newton, CG and PGS solvers | Multiple physics backends including Newton and OVPhysX | Bullet rigid-body and collision engine | Rigid, FEM, MPM, PBD and SPH solvers sharing one scene |
| GPU parallelism | MJX-JAX (NVIDIA/AMD GPUs, Apple Silicon, TPU); MJX-Warp (NVIDIA GPUs) | GPU-accelerated framework with 30+ example environments | Experimental OpenCL GPU pipeline | Quadrants compiler targets CUDA, ROCm, Metal, Vulkan, x86 and ARM64 |
| Sensors and rendering | OpenGL visualizer; MJX focuses on physics | RTX cameras (RGB, depth, segmentation), IMU, contact sensors, ray casters | Basic renderer | Nyx, the Luisa ray tracer and Pyrender plug in as camera sensors |
| Differentiability | Yes on MJX-JAX, no on MJX-Warp | Not a primary goal | No | Quadrants carries autodiff machinery (check the docs for coverage) |
| Install | pip install mujoco | Isaac Sim via pip or binaries, Isaac Lab from source | pip install pybullet | Install PyTorch, then pip install genesis-world |
Every cell was checked against the project README or official docs on 2026-10-19. This area moves quickly, so re-check right before you commit to one.
MuJoCo: careful contact math and two GPU paths
According to the MuJoCo computation docs, MuJoCo drops the strict complementarity condition at the heart of the linear complementarity problem (LCP) used by many engines and instead solves contact as a convex optimization. The result is a soft contact model that tolerates a tiny amount of penetration in exchange for a well-posed problem that solves reliably. You choose between Newton (the default, using a Cholesky factorization of the Hessian), nonlinear conjugate gradient (CG) for harder problems, and projected Gauss-Seidel (PGS), which updates one constraint at a time. Friction is approximated with elliptic or pyramidal cones, and the per-contact condim value decides whether a contact carries only a normal force or also sliding, torsional and rolling friction.
For GPU work the MJX documentation describes two backends. MJX-JAX re-implements MuJoCo’s algorithms in pure JAX, supports gradients, and runs on NVIDIA and AMD GPUs, Apple Silicon and Google Cloud TPUs. The docs say it works best with thousands to tens of thousands of scenes in parallel and that a single scene can be 10x slower than CPU MuJoCo, which is heavily optimized. MJX-Warp uses MuJoCo Warp, which the docs call the most fully featured MuJoCo implementation for hardware accelerators; it targets NVIDIA GPUs and does not support automatic differentiation.
pip install mujoco # CPU MuJoCo (Python 3.10+)
pip install mujoco-mjx # MJX-JAX
pip install "mujoco-mjx[warp]" # MJX-Warp (NVIDIA GPUs)
Feature coverage is where migrations stall. MJX-JAX only supports the CG and Newton solvers, and MJX-Warp lacks the PGS solver, noslip and some geom and sensor types. Copying a model that references an unsupported feature to the device raises an exception, so audit the solver setting, the convex hull vertex limit (maxhullvert) and explicit collision pairs before moving an MJCF file that was tuned on the CPU. The docs recommend keeping convex meshes at 64 vertices or fewer and listing collision pairs explicitly to keep the contact count down.
Isaac Lab: massive parallelism with sensors in the loop
The Isaac Lab README presents the project as a GPU-accelerated robot learning framework and lists Newton, OVRTX, OVPhysX and Isaac Sim as physics and rendering backends. The current 3.0-series documentation notes that not every workflow requires Isaac Sim, while those that do must use a compatible Isaac Sim version. It ships more than 30 example environments and plugs directly into RL libraries such as RSL RL, SKRL, RL Games and Stable Baselines.
Sensors are the biggest differentiator. RTX-based RGB, depth and segmentation cameras, IMUs, contact sensors, ray casters and LiDAR are available inside the training loop, which makes Isaac Lab a natural fit for pixel-input policies and for tasks where terrain and contact feedback matter, such as bipedal locomotion and whole-body control. The trade-off is hardware. The installation guide lists Ubuntu 22.04 or Windows 11 (x64), 32 GB of RAM and 16 GB of GPU VRAM as the minimum, and warns that rendering-heavy workflows may need more. The recommended Isaac Sim version shown in the page body and in the sidebar badge can differ, so confirm compatibility in the release notes right before installing.
One of those backends, Newton, is a Linux Foundation project started by Disney Research, Google DeepMind and NVIDIA. It is built on NVIDIA Warp and integrates MuJoCo Warp as its primary backend, which means MuJoCo-style contact handling on the GPU is now reachable from inside Isaac Lab as well. Newton’s README notes that fresh installs pick Warp 1.18, which needs an NVIDIA Turing-or-newer GPU and an R580+ driver.
PyBullet: an easy start with limits at scale
The bullet3 repository releases its source under the permissive zlib license and recommends PyBullet for robotics, reinforcement learning and VR. A single pip install pybullet gets you a simulator that loads URDF robots immediately, which is why it remains common in courses and quick algorithm prototypes.
Two facts belong in the decision. First, the README describes GPU execution as an experimental OpenCL pipeline that does not run on OpenCL CPU devices, so it was not designed for batching thousands of environments on a GPU the way Isaac Lab or MJX are. Second, the repository’s Releases section is empty and the issue tracker is closed until it is cleaned up, which matters if the project you are starting will run for years.
Genesis World: many physics solvers and renderers in one scene
According to the Genesis documentation, Genesis World began as an academic project in December 2024 and is now supported by Genesis AI. Rigid bodies, FEM, MPM and particle methods (PBD and SPH) share a single scene and state, so manipulation tasks that mix cloth, liquids and deformables can be built in one simulator. Renderers — the in-house Nyx, the Luisa ray tracer and the Pyrender rasterizer — attach like camera sensors.
Kernels are compiled by Quadrants, which lowers them to CUDA, AMD ROCm, Apple Metal, Vulkan, x86 and ARM64, so the same code can start on a non-NVIDIA GPU or a laptop. The README says to install PyTorch first, then run pip install genesis-world, and requires Python 3.10 or newer but below 3.14. Speed figures from launch materials use measurement conditions that differ across public documents, so they are not repeated here. Measure performance with your own robot model under identical settings instead.
Which simulator to evaluate first, by task
| Task | Evaluate first | Why |
|---|---|---|
| Quadruped or biped RL with thousands of parallel envs | Isaac Lab, MJX-Warp | Massive GPU parallelism plus terrain and contact sensors |
| Vision policies trained from camera input | Isaac Lab | RTX cameras rendered inside the training loop |
| Debugging one contact-rich manipulation scene | CPU MuJoCo | Fine control over solver and friction parameters |
| Trajectory optimization through a differentiable simulator | MJX-JAX | JAX automatic differentiation |
| Manipulation mixing cloth, liquids or deformables | Genesis World | Multiple solvers coupled in one scene |
| Teaching, prototyping, low-spec laptops | PyBullet, CPU MuJoCo | pip install and modest requirements |
If you plan to turn simulated demonstrations into policy training data, read this together with real teleoperation data versus synthetic data, which covers how to mix the two. For where Isaac Lab sits inside NVIDIA’s stack, see the Cosmos, Isaac and GR00T three-computer architecture.
A minimal first-week check before you commit
Running the same robot through a short routine in two candidate simulators exposes differences that documentation alone will not show.
- Load your robot model (URDF or MJCF) in two candidates and confirm that joint limits and masses are read identically.
- Build the same pick scene, match the friction coefficients and solver iterations, and log the contact forces at the moment of grasp.
- Increase the number of parallel environments step by step and record simulation steps per second and GPU memory use in a table.
- If the task needs cameras, repeat step 3 with rendering enabled to see whether physics or rendering is the bottleneck.
import mujoco
xml = """
<mujoco>
<option solver="Newton" cone="elliptic"/>
<worldbody>
<geom type="plane" size="1 1 0.1"/>
<body pos="0 0 0.2">
<freejoint/>
<geom type="box" size="0.03 0.03 0.03" condim="4" friction="0.8 0.02 0.001"/>
</body>
</worldbody>
</mujoco>
"""
model = mujoco.MjModel.from_xml_string(xml)
data = mujoco.MjData(model)
for _ in range(500):
mujoco.mj_step(model, data)
print(data.ncon, data.qpos[:3]) # contact count and box position
Varying solver, cone and condim in a scene this small and noting how the contact result changes gives you a baseline. When results later diverge in another simulator, you can tell whether the cause is model conversion or contact settings.