# Setting Up the Newton Viewer with GL, USD, or ReRun Backends: A Complete Guide

> Learn to set up the Newton viewer with GL, USD, or ReRun backends. This guide covers importing classes, attaching models, and using frame functions for your simulation loop.

- Repository: [Newton Physics/newton](https://github.com/newton-physics/newton)
- Tags: how-to-guide
- Published: 2026-03-19

---

**You can initialize any Newton viewer backend by importing the concrete class from `newton.viewer`, attaching your model with `set_model()`, and calling `begin_frame()` and `end_frame()` inside your simulation loop.**

Newton’s visualization layer in the `newton-physics/newton` repository provides a unified interface for rendering physics simulations across multiple backends. Whether you need real-time OpenGL debugging, USD archival for Maya or Houdini, or collaborative streaming via ReRun, the viewer architecture lets you swap implementations without modifying your simulation code.

## Newton Viewer Architecture Overview

The visualization system centers on `ViewerBase`, an abstract class defined in [`newton/_src/viewer/viewer.py`](https://github.com/newton-physics/newton/blob/main/newton/_src/viewer/viewer.py) that handles model management, time-step hooks, UI callbacks, picking, and wind simulation. All concrete backends inherit from this base and implement the same public API:

```python
log_mesh(name, points, indices, normals=None, uvs=None, texture=None, hidden=False, backface_culling=True)
log_instances(name, mesh, xforms, scales=None, colors=None, materials=None, hidden=False)
log_lines(...)
log_points(...)
log_scalar(...)
log_array(...)
begin_frame(time)
end_frame()
is_running()
close()

```

The public entry point [`newton/viewer.py`](https://github.com/newton-physics/newton/blob/main/newton/viewer.py) re-exports the concrete implementations:

```python
from ._src.viewer import ViewerFile, ViewerGL, ViewerNull, ViewerRerun, ViewerUSD, ViewerViser

```

This modular design means simulation code only interacts with the abstract interface, while the concrete classes in [`newton/_src/viewer/viewer_gl.py`](https://github.com/newton-physics/newton/blob/main/newton/_src/viewer/viewer_gl.py), [`viewer_usd.py`](https://github.com/newton-physics/newton/blob/main/viewer_usd.py), and [`viewer_rerun.py`](https://github.com/newton-physics/newton/blob/main/viewer_rerun.py) handle backend-specific I/O.

## Setting Up the OpenGL Viewer (ViewerGL)

`ViewerGL` provides a real-time interactive window using GLFW and a custom `RendererGL`. This backend is ideal for local debugging and interactive parameter tuning.

```python
import newton as nt
from newton.viewer import ViewerGL

# Create a simple pendulum model

model = nt.examples.basic.example_basic_pendulum.create_model()

# Instantiate the GL viewer with specific window dimensions

viewer = ViewerGL(width=1600, height=900, vsync=True)

# Attach the model to the viewer

viewer.set_model(model)

# Run the simulation loop

for step in range(200):
    time = step * model.timestep
    state = model.state_at_time(time)
    
    viewer.begin_frame(time)
    viewer.log_state(state)
    viewer.end_frame()
    
    if not viewer.is_running():
        break

viewer.close()

```

In [`newton/_src/viewer/viewer_gl.py`](https://github.com/newton-physics/newton/blob/main/newton/_src/viewer/viewer_gl.py), the constructor initializes the GLFW window and `RendererGL` (lines 23-35). The `log_state` method packs shape transforms into GPU buffers for high-throughput rendering (around lines 510-580), while PBO read-back enables efficient screenshot capture.

## Exporting to USD with ViewerUSD

`ViewerUSD` writes geometry to a Pixar USD stage, creating a file that can be opened in Maya, Houdini, Blender, or `usdview`. This backend is optimized for archival and post-processing workflows.

```python
import newton as nt
from newton.viewer import ViewerUSD

# Create a soft body simulation model

model = nt.examples.softbody.example_softbody_hanging.create_model()

# Initialize the USD viewer with output path and frame rate

viewer = ViewerUSD(output_path="out/softbody_sim.usd", fps=30, scaling=1.0)

# Attach model

viewer.set_model(model)

# Simulate and export frames

for i in range(120):
    t = i * model.timestep
    state = model.state_at_time(t)
    
    viewer.begin_frame(t)
    viewer.log_state(state)
    viewer.end_frame()

viewer.close()  # Finalizes the USD layer

```

The implementation in [`newton/_src/viewer/viewer_usd.py`](https://github.com/newton-physics/newton/blob/main/newton/_src/viewer/viewer_usd.py) converts Warp arrays to NumPy and writes them to `UsdGeom.Mesh` prototypes (lines 230-270). For instanced geometry, it supports both per-instance prims and USD PointInstancers via `log_instances_point_instancer`, enabling efficient representation of particle systems or rigid body aggregates.

## Real-Time Visualization with ViewerRerun

`ViewerRerun` streams geometry to the Rerun SDK, supporting both real-time collaborative viewing and recorded `.rrd` files for later playback. This backend excels in notebook environments and remote debugging scenarios.

```python
import newton as nt
from newton.viewer import ViewerRerun

# Create a cloth simulation model

model = nt.examples.cloth.example_cloth_h1.create_model()

# Initialize Rerun viewer with application ID

viewer = ViewerRerun(app_id="newton-cloth", keep_historical_data=False)

# Attach model

viewer.set_model(model)

# Stream simulation frames

for i in range(100):
    t = i * model.timestep
    state = model.state_at_time(t)
    
    viewer.begin_frame(t)
    viewer.log_state(state)
    viewer.end_frame()

viewer.close()

```

Rerun support requires the optional dependency `rerun-sdk` (`pip install rerun-sdk`). In [`newton/_src/viewer/viewer_rerun.py`](https://github.com/newton-physics/newton/blob/main/newton/_src/viewer/viewer_rerun.py), the constructor configures the Rerun recording stream (lines 40-73), while `_get_blueprint` (lines 8-22) defines a minimal UI layout with a 3D view and optional time-series panels for scalar data.

## Switching Between Backends Without Code Changes

The abstract `ViewerBase` API enables zero-friction backend swapping. By passing the viewer instance as a parameter, the same simulation logic runs unchanged across GL, USD, and Rerun:

```python
def run_simulation(viewer):
    model = nt.examples.basic.example_basic_joints.create_model()
    viewer.set_model(model)
    
    for i in range(150):
        t = i * model.timestep
        state = model.state_at_time(t)
        
        viewer.begin_frame(t)
        viewer.log_state(state)
        viewer.end_frame()
    
    viewer.close()

# Execute with any backend

run_simulation(ViewerGL())
run_simulation(ViewerUSD("sim.usd"))
run_simulation(ViewerRerun(app_id="demo"))

```

This pattern is supported by the consistent implementation of `log_mesh`, `log_instances`, and `log_state` across [`newton/_src/viewer/viewer_gl.py`](https://github.com/newton-physics/newton/blob/main/newton/_src/viewer/viewer_gl.py), [`viewer_usd.py`](https://github.com/newton-physics/newton/blob/main/viewer_usd.py), and [`viewer_rerun.py`](https://github.com/newton-physics/newton/blob/main/viewer_rerun.py).

## Summary

- **Newton's viewer system** in `newton-physics/newton` provides a unified abstraction via `ViewerBase` in [`newton/_src/viewer/viewer.py`](https://github.com/newton-physics/newton/blob/main/newton/_src/viewer/viewer.py).
- **Three primary backends** support different workflows: `ViewerGL` for real-time OpenGL windows, `ViewerUSD` for Pixar USD archival, and `ViewerRerun` for collaborative streaming.
- **Identical API** across all implementations means you can swap `ViewerGL()`, `ViewerUSD("output.usd")`, and `ViewerRerun(app_id="sim")` without changing simulation logic.
- **Dependencies vary by backend**: OpenGL requires `glfw` and `PyOpenGL`, USD requires `usd-core`, and Rerun requires `rerun-sdk`.

## Frequently Asked Questions

### How do I choose between ViewerGL, ViewerUSD, and ViewerRerun?

**Use `ViewerGL`** when you need an interactive OpenGL window for local debugging with camera navigation and picking support. **Choose `ViewerUSD`** when you must archive simulation results for post-processing in DCC tools like Maya, Houdini, or Blender. **Select `ViewerRerun`** for real-time collaborative visualization, Jupyter notebook integration, or when you need to share interactive recordings via `.rrd` files.

### Can I use multiple viewers simultaneously in the same simulation?

Yes, because each viewer maintains independent state, you can instantiate `ViewerGL`, `ViewerUSD`, and `ViewerRerun` concurrently within the same script. Call `begin_frame()`, `log_state()`, and `end_frame()` on each viewer inside your simulation loop to output to multiple formats simultaneously.

### What dependencies are required for each backend?

`ViewerGL` requires `glfw`, `PyOpenGL`, and `numpy`. `ViewerUSD` depends on `usd-core` (Pixar's USD Python bindings). `ViewerRerun` requires the optional `rerun-sdk` package. The `ViewerNull` and `ViewerFile` backends have no external dependencies beyond Newton's core requirements.

### How do I configure the USD output settings?

When constructing `ViewerUSD`, pass the `output_path` parameter to specify the `.usd` file location. You can also set `fps` to control the time-code sampling rate and `scaling` to adjust the world units. The viewer automatically manages mesh prototypes and point instancers based on the model geometry type.