Hugging Face published a guide to NVIDIA Warp and MJWarp for accelerating robotics simulation, demonstrating how to scale a single robot task from one CPU world to 2,048 parallel GPU environments while maintaining physics parity.

NVIDIA Warp is a Python framework that compiles statically typed kernels to CUDA, letting developers write high-performance GPU code without leaving Python. The framework handles kernel fusion, CUDA graph capture, and differentiable execution—meaning simulation kernels can sit inside ML training loops. Warp kernels are explicitly parallel: a single logical thread owns one point, contact, body, or world, so the same code scales from two entities to millions without GPU-specific control flow. The framework also provides built-in support for vectors, matrices, quaternions, and sparse data structures.

MJWarp layers MuJoCo's physics pipeline onto Warp, taking compatible MJCF robot models and running batches of independent simulation states on GPUs. Where classic MuJoCo excels at developing and inspecting one or a few CPU worlds, MJWarp's value lies in aggregate throughput—the total world-steps completed per second across a batch. The guide walks through migrating an SO-101 robot arm performing a pick-and-place task: grasping a 44 mm red cube and stacking it on a blue cube. The same MJCF model runs on both backends; the difference is how work is organized.

The migration path is concrete. First, establish a CPU baseline: load the model, run the controller at 50 Hz with 10 physics substeps per frame (0.002 second timestep), and verify task success by checking that the cubes end up within 0.015 m horizontally and 0.035 to 0.055 m vertically. Then upload the model to GPU with mjw.put_model(), allocate batched state with mjw.make_data(), seed the initial conditions across all worlds, and step the batch with mjw.step(). The guide emphasizes that contact and constraint buffers must be sized correctly—the SO-101 profile uses nconmax=128 and njmax=300—and that exceeding these limits invalidates results even if execution continues with a warning.

Scaling to 2,048 worlds requires only changing the nworld parameter and replicating the initialized state across the batch using np.tile(). CUDA graph capture wraps the step call, so subsequent replays reuse the compiled graph without re-launching kernels. Measuring throughput correctly demands synchronization: warm up first to pay compilation costs, then synchronize immediately before and after the timed region. The guide does not report absolute numbers for the SO-101 task; instead it directs readers to run scaling_study.py on their own hardware to measure the throughput curve and identify where additional worlds stop improving performance.

The hard part is that MJWarp's throughput advantage applies only to workloads where batching matters—reinforcement learning, large-scale sampling, or policy search where collecting experience across many parallel worlds outweighs the latency of a single step. For single-robot model predictive control or teleoperation, classic MuJoCo on CPU remains the right choice. The guide also notes that deterministic execution, introduced in Warp 1.15, trades performance for reproducible GPU atomics, which matters for validation and regression testing but is not enabled by default. Differentiability is a Warp capability, not a guarantee for an entire MJWarp rollout; teams building differentiable physics must verify their specific use case.

If you are scaling embodied-agent training or sampling-heavy RL workloads, measure your scene's contact and constraint budget, then benchmark batched throughput on your target hardware before committing to the stack.