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

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 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:

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 re-exports the concrete implementations:

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, viewer_usd.py, and 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.

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, 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.

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 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.

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, 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:

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, viewer_usd.py, and viewer_rerun.py.

Summary

  • Newton's viewer system in newton-physics/newton provides a unified abstraction via ViewerBase in 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →