# MuJoCo JavaScript Bindings

These are the canonical JavaScript and TypeScript bindings for the MuJoCo
physics engine.

This package provides a high-level API that allows you to interact with the core
MuJoCo engine compiled into a high-performance WebAssembly (WASM) module. These
bindings are developed and maintained by Google DeepMind and are always up to
date with the latest developments in MuJoCo. For brevity, the documentation
below will often refer to “JavaScript” but the concepts apply equally to
TypeScript.

> [!IMPORTANT]
> _These bindings are still a WIP. For details, see the [Future Work](#future-work)
> section. Also note that development has primarily taken place on Linux using
> Google Chrome. If you're working on a different OS or browser, you may
> encounter some rough edges. We have successfully tested the bindings on MacOS
> in CI but as of November 13th 2025, Windows support remains experimental
> (installation succeeded on one Windows 11 machine but failed on others)._

## Installation

The easiest way to use the MuJoCo JavaScript bindings is to install the
`@mujoco/mujoco` package from npm:

```sh
npm install @mujoco/mujoco
```

This package is ESM (`type: module`) and includes the pre-compiled WebAssembly
module, JavaScript bindings, and TypeScript declarations. Ensure your bundler or
dev server serves the `.wasm` asset at runtime.

### Threading Models

The `@mujoco/mujoco` package includes two distinct builds of the engine to
support different browser environments and performance needs.

#### 1. Single-Threaded (Default)

The standard single-threaded version is located at the root of the package. It
is compatible with all modern browsers and does not require special security
headers.
```typescript
import loadMujoco from '@mujoco/mujoco';
```

#### 2. Multi-Threaded (MT)

The multi-threaded version is located in the `/mt` subfolder. It utilizes Web
Workers and `SharedArrayBuffer` to parallelize physics computations.
```typeScript
import loadMujoco from '@mujoco/mujoco/mt';
```

> [!NOTE]
> Due to the use of `SharedArrayBuffer`, browsers require Cross-Origin Isolation
> to enable multi-threading. Your web server must send the following HTTP
> headers:
>    - `Cross-Origin-Opener-Policy: same-origin`
>    - `Cross-Origin-Embedder-Policy: require-corp`
>
> If these headers are missing, the module will fail to initialize.

## Build from source

### Prerequisites

> [!NOTE]
> Run all the commands in this README from the top-level directory.

- To compile the [`bindings.cc`](codegen/generated/bindings.cc) file, which
  generates the `.wasm` WebAssembly file, `.js` JavaScript import, and `.d.ts`
  TypeScript declaration file, you will need Emscripten SDK version `4.0.10`.
  Later versions may work but are untested. To set up the SDK, do the
  following, you can run this anywhere but the rest of the commands in this
  README only work in the shell where you source the `emsdk_env.sh` script.

  ```sh
  git clone https://github.com/emscripten-core/emsdk.git
  ./emsdk/emsdk install 4.0.10
  ./emsdk/emsdk activate 4.0.10
  source ./emsdk/emsdk_env.sh
  ```

- To easily run the JavaScript tests and the demo application, `node` and `npm`
  are required. We recommend managing these using
  [nvm](https://github.com/nvm-sh/nvm). There are also various JavaScript
  dependencies needed for the tests, demo, and bindings build process. These
  dependencies are expected to be located in the `wasm` folder. To install
  them and ensure they can be found by later commands, run the following:

  ```sh
  npm install --prefix ./wasm
  export PATH="$(pwd)/wasm/node_modules/.bin:$PATH"
  ```

- To modify the bindings `python3` is required because the [`bindings.cc`](codegen/generated/bindings.cc)
  file is generated by a Python script. To run the bindings generator tests,
  `absl` is required and `pytest` will be helpful. Set up a Python environment
  with these dependencies as follows:

  ```sh
  python3 -m venv .venv
  source .venv/bin/activate
  pip install -r python/build_requirements.txt
  ```

> [!TIP]
> _Emscripten is well-documented. We recommend reading the sections covering the
> [Emscripten Compiler Settings](https://emscripten.org/docs/tools_reference/settings_reference.html),
> the [Emscripten SDK](https://emscripten.org/docs/tools_reference/emsdk.html),
> and the [Embind](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html)
> library. To understand the limitations and caveats related to using the
> browser as a platform, see the
> [Porting](https://emscripten.org/docs/porting/index.html#porting) section._

The [`bindings.cc`](codegen/generated/bindings.cc) file is compiled to generate
the `.wasm` WebAssembly file, `.js` JavaScript import, and `.d.ts` TypeScript
declaration file. These are the files you'll use to call MuJoCo from JavaScript.
To generate them ensure the npm and Emscripten SDK prerequisites are set up and
then run the following:

```sh
emcmake cmake -B build && cmake --build build
```

This command will generate the following folders under the project root:

- `build`: contains MuJoCo compiled using Emscripten.
- `wasm/dist`: contains the WebAssembly module, `.js` and `.d.ts` files.

The assets inside those folders are compiled and prepared to run as
single-threaded. If you need to operate with a multi-threaded version of the
module make sure to pass the `-DMUJOCO_WASM_THREADS=ON` flag like:

```sh
emcmake cmake -B build -DMUJOCO_WASM_THREADS=ON && cmake --build build
```

### Example Application

After generating the bindings you will be ready to write web applications using
MuJoCo. We have provided a basic web application that uses Three.js to render a
simple simulation, to try it run this command:

```sh
npm run dev:demo --prefix ./wasm
```

You may prefer to write your entire app in C++ and compile it using Emscripten.
If you do this, you won’t need to use these bindings, since you’ll be writing
minimal JavaScript, and the granularity of these bindings may be inappropriate
(e.g., you might want to call multiple MuJoCo functions in the C++ callback
invoked by `requestAnimationFrame`).

We have also found that a hybrid approach can be helpful, as it is often more
convenient to work with browser APIs directly in JavaScript. If you choose to
write your application in C++ and compile it using Emscripten, you may want to
copy a subset of the `EMSCRIPTEN_BINDINGS` from `bindings.cc` into your
application’s source file.

## User Guide
### Named Access

The bindings support named access methods, similar to the Python bindings,
allowing convenient access to model and data elements by name or index. For
example, you can access a geometry by name using `model.geom('mygeom')` or a
joint using `data.jnt('myjoint')`.

For more details and examples of how to use named access, please refer to the [named access tests](tests/bindings_test.ts#L1876-L2378) and [documentation](https://mujoco.readthedocs.io/en/stable/python.html#named-access).

### Memory Management
Embind-wrapped C++ object handles created or returned into JavaScript live on
the WebAssembly heap and are **not** garbage-collected by the JS runtime.

Any heap-allocated C++ object exposed to JS (e.g. via `new Module.MyClass(...)`
or returned as a pointer/reference from a binding) must be explicitly freed
when no longer needed to avoid memory leaks.

Use the generated `.delete()` method on wrapped instances to destroy the
underlying C++ object:

```typescript
const obj = new Module.MyClass(...);
// ... use obj ...
obj.delete(); // free the C++ memory
```

Be careful to call `.delete()` exactly once per created object (double-delete
is an error). In JS code paths that may throw or return early, ensure
deletion happens in finally blocks or wrap lifetime management to avoid leaks.

> [!IMPORTANT]
> _Embind's documentation strongly recommends that JavaScript code explicitly
> deletes any C++ object handles it has received._

### Copy vs. Reference

When interacting with MuJoCo objects through the WASM bindings, it's important
to understand how data is accessed. Properties on objects like `MjModel` and
`MjData` can expose data in two ways: by copy or by reference.

#### 1. By Copy (Value-based access)

Some properties return a copy of the data at the time of access. This is common
for complex data structures that need to be marshalled from C++ to JavaScript.

A key example is `MjData.contact`. When you access `data.contact`, you get an
object containing a copy of the contacts at that specific moment in the
simulation.

If you step the simulation forward, they will not be updated. You must access
`data.contact` again to get the new contact information.

The object you get is a JavaScript proxy interface generated by [Emscripten’s Embind library](https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#built-in-type-conversions) when you expose a `std::vector` using `register_vector<T>`. It is essentially a "bridge" object.

```typescript
export interface MjContactVec extends ClassHandle {
  /** Appends a new element to the end of the vector, increasing its length by one. */
  push_back(_0: MjContact): void;
  /** Resizes the vector to contain the specified number of elements, filling new slots with the provided value. */
  resize(_0: number, _1: MjContact): void;
  /** Returns the total number of elements currently stored in the vector. */
  size(): number;
  /** Retrieves the element at the specified index, or returns undefined if the index is out of bounds. */
  get(_0: number): MjContact | undefined;
  /** Overwrites the element at the specified index; returns true if successful or false if the index is invalid. */
  set(_0: number, _1: MjContact): boolean;
}
```

Example:
```typescript
// Gets contacts at the current time.
const contacts = data.contact;

// Step the simulation
mujoco.mj_step(model, data);

// `contacts` is now stale. To get the new contacts, you must access the property again:
const newContacts = data.contact;

// Remember to delete all created objects when they are no longer needed.
contacts.delete();
newContacts.delete();
```

#### 2. By Reference (View-based access)

Many properties, especially large numerical arrays, return a live view directly
into the WebAssembly memory. This is highly efficient as it avoids copying large
amounts of data.

A key example is `MjData.qpos` (joint positions). When you get a reference to
this array, it points directly to the simulation's state data. Any changes in
the simulation (e.g., after a call to `mj_step`) will be immediately reflected
in this array.

```typescript
// `qpos` is a live view into the simulation state.
const qpos = data.qpos;

console.log(qpos[0]); // Print initial position

// Step the simulation
mujoco.mj_step(model, data);

// `qpos` is automatically updated.
console.log(qpos[0]); // Print new position

// Remember to delete all created objects when they are no longer needed.
data.delete();
```

### Data Layout: Row-Major Matrices

When a function from the MuJoCo C API returns a matrix (or needs a matrix as
input), these are represented in the JavaScript bindings as flat,
one-dimensional `TypedArray`'s. The elements are stored in row-major order.

For example, a 3x10 matrix will be returned as a flat array with 30 elements.
The first 10 elements represent the first row, the next 10 represent the second
row, and so on.

Example: Accessing an element at `(row, col)`
```typescript
// A 3x10 matrix stored as a flat array.
const matrix: Float64Array = ...;
const nRows = 3;
const nCols = 10;

// To access the element at row `i` and column `j`:
const element = matrix[i * nCols + j];
```

### Working with Out Parameters

Many functions in the MuJoCo C API use "out parameters" to return data. This
means instead of returning a value, they write the result into one of the
arguments passed to them by reference (using pointers). In our JavaScript
bindings, you'll need to handle these cases specifically.

There are two main scenarios you'll encounter:

#### 1. Array-like Out Parameters

When a function expects a pointer to a primitive type (like `mjtNum*` or `int*`)
to write an array of values, you need to pre-allocate memory for the result on
the JavaScript side. We provide helper classes for this: `mujoco.Uint8Buffer`,
`mujoco.DoubleBuffer`, `mujoco.FloatBuffer`, and `mujoco.IntBuffer`.

Here's how to use them:

1.  Create a buffer: Instantiate the appropriate buffer class with an initial array of the correct size (e.g., an array of zeros).
2.  Call the function: Pass the buffer instance to the function as the out parameter.
3.  Access the result: Use the `.getView()` method on the buffer to get a `TypedArray` view of the data written by the C++ function.
4.  Free the memory: When you are done with the buffer, you must call the `.delete()` method to free the underlying memory and prevent memory leaks.

Example: Rotating a vector

The function `mju_rotVecQuat` rotates a vector `vec` by a quaternion `quat` and
stores the result in the `res` out parameter.

```typescript
// Create a buffer to hold the 3D vector result.
const res = new mujoco.DoubleBuffer([0, 0, 0]);

const vec = [1, 0, 0];
const quat = [0.707, 0, 0, 0.707]; // 90-degree rotation around z-axis

try {
  // Call the function with the buffer as the out parameter.
  mujoco.mju_rotVecQuat(res, vec, quat);

  // Get the result as a Float64Array.
  const resultView = res.getView();
  console.log(resultView); // Expected: approximately [0, 1, 0]

} finally {
  // IMPORTANT: Free the memory allocated for the buffer.
  res.delete();
}
```

#### 2. Struct Out Parameters (e.g., mjvCamera*, mjvScene*)

When a function modifies a struct passed by pointer, you should pass an instance
of the corresponding JavaScript wrapper class. The underlying C++ struct will be
 modified in place.

Example: Updating a scene

The function `mjv_updateScene` populates an `mjvScene` object with information
from `mjModel` and `mjData`.
```typescript
// Create instances of the necessary structs.
const model = mujoco.MjModel.from_xml_string(xmlContent);
const data = new mujoco.MjData(model);
const scene = new mujoco.MjvScene(model, 1000);
const option = new mujoco.MjvOption();
const perturb = new mujoco.MjvPerturb();
const camera = new mujoco.MjvCamera();

// ... (step simulation, etc.)

// Update the scene. The 'scene' object is modified by the function.
mujoco.mjv_updateScene(
    model,
    data,
    option,
    perturb,
    camera,
    mujoco.mjtCatBit.mjCAT_ALL.value,
    scene
);

console.log('Number of geoms in scene:', scene.ngeom);

// Remember to delete all created objects when they are no longer needed.
scene.delete();
camera.delete();
perturb.delete();
option.delete();
data.delete();
model.delete();
```

As with buffers, you are responsible for managing the memory of these struct
 instances and must call `.delete()` on them when you are finished.

### Enums
Access via `.value`:
```javascript
mujoco.mjtDisableBit.mjDSBL_CLAMPCTRL.value
```

### Constants
Scalar constants can be accessed as properties:

```javascript
mujoco.mjNEQDATA
```

Non-scalar constants like `mjFRAMESTRING` are also accessed as properties, and
return JavaScript arrays:

```javascript
mujoco.mjFRAMESTRING
```

This will return a javascript array representation of the values in MuJoCo
`mjFRAMESTRING`.

> [!NOTE]
> You will notice constants like `mjFRAMESTRING` are typed as `any`. This is
> because they are bound using `emscripten::val::array()` in C++, and Embind
> maps `emscripten::val` to `any` in TypeScript definition files. While
> `EMSCRIPTEN_DECLARE_VAL_TYPE(StringArray)` could be used to define
> `StringArray` as an alias for `emscripten::val` and hint to Embind how to
> handle conversions in function signatures or when using `.as()` it does not
> change how `emscripten::constant` infers types for properties.

## Development

In order to change the bindings you will need to change the [`bindings.cc`](codegen/generated/bindings.cc)
file but this should not be done manually. The file is generated using the
Python scripts and template files in the [`codegen`](codegen) folder, to edit
the bindings you will need to change those files and re-generate [`bindings.cc`](codegen/generated/bindings.cc)
using this command:

```sh
PYTHONPATH=python/mujoco python3 -m wasm.codegen.update
```

The codegen scripts use MuJoCo’s Python introspect library to generate the
Embind `EMSCRIPTEN_BINDINGS` block that binds C++ functions and classes to
JavaScript. The functions and classes that are bound are wrappers around
MuJoCo's C API. These wrappers provide a convenient place to add features like
bounds checking and nice error reporting.

### Testing

1. **JavaScript API tests.**
   These verify that a wide variety of MuJoCo functions and classes work
   correctly when called from JavaScript. Run the tests as follows:

   ```sh
   npm run test --prefix ./wasm
   ```

2. **JavaScript API benchmark tests.**
   The current benchmark tests check JavaScript/C++ shared memory buffers
   performance. We will increase the coverage of the benchmarks overtime. Run
   the benchmarks using this command:

   ```sh
   npm run benchmark --prefix ./wasm
   ```

3. **Bindings generator tests.**
   These are relevant when developing or extending the bindings. The following
   command finds and runs all `test_*.py` or `*_test.py` files in the `wasm`
   folder:

   ```sh
   PYTHONPATH=python/mujoco python3 -m pytest ./wasm
   ```

### Debugging

We provide a “sandbox” app where you can quickly write code to run in your
browser. Write your code in the [`main.ts`](tests/sandbox/main.ts) file and use
the following command to execute it in your browser:

```sh
npm run dev:sandbox --prefix ./wasm
```

The page will be blank since the script only logs to the console output. You
can add your code at the indicated placeholder and use Chrome DevTools for
debugging. It is possible to set up a debug workflow where stack traces and
stepping through code across language boundaries work correctly. Our current
method to do this only works internally at Google, but it should be possible to
replicate the experience with open-source tooling — community suggestions are
welcome!

## Versioning
Package versions follow the official MuJoCo release versions.
For example:

| npm version | MuJoCo version |
|-------------|----------------|
| 3.5.0       | 3.5.0          |

## Future Work

1. **Bind all useful APIs.**
   These bindings are not yet complete. While the main MuJoCo APIs (`mj_step`,
   `mj_loadXML`, etc.) are well tested, other APIs (e.g., functions from
   `mjspec.h`) remain untested in real web applications (though test code for
   the `mjspec` bindings does exist).

2. **Improve the developer experience.**
   There is still work to be done to improve the developer experience when
   developing the WASM bindings. The most obvious issue is that bindings
   generation is not yet fully automated. As a result, it is currently less
   convenient than we'd like to identify and apply the changes needed to update
   the bindings. The goal is to eventually automate all binding code generation
   and clearly communicate what changes are required in the WASM bindings as a
   result of C++ updates. This problem should only affect developers working on
   the MuJoCo engine in C++, not end users writing JavaScript.

3. **Improve the documentation.**
   The documentation in this README will eventually be merged into the main
   MuJoCo documentation once the bindings are complete and named access is
   implemented. We also intend to review the bindings APIs and make adjustments
   to minimize differences with the Python bindings (while respecting language
   idioms) to reduce the amount of additional documentation required.

4. **Improve the [example](#example-application).**
   We aim to provide an example application that can be easily modified and
   embedded into a paper project page (see [this example](https://kzakka.com/robopianist/)).
   This could be achieved by extending the Three.js example or by compiling the
   MuJoCo platform C++ code using the Emscripten toolchain. Community
   suggestions and contributions are welcome!
