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/newtonprovides a unified abstraction viaViewerBaseinnewton/_src/viewer/viewer.py. - Three primary backends support different workflows:
ViewerGLfor real-time OpenGL windows,ViewerUSDfor Pixar USD archival, andViewerRerunfor collaborative streaming. - Identical API across all implementations means you can swap
ViewerGL(),ViewerUSD("output.usd"), andViewerRerun(app_id="sim")without changing simulation logic. - Dependencies vary by backend: OpenGL requires
glfwandPyOpenGL, USD requiresusd-core, and Rerun requiresrerun-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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →