Visualization#

Isaac Lab provides 5 visualizers for real-time simulation inspection and debugging. Where renderers produce sensor data for training, visualizers give fast, lightweight feedback for human monitoring and recording. Visualizers can also stream that same sensor data through the streaming camera panel.

This page covers:

5 visualizers running Isaac-Velocity-Flat-AnymalD with a circular velocity command
Green arrow: commanded velocity. Blue arrow: current velocity.

Newton GL
Viser
Newton RTX
Rerun
Kit

Note: Newton RTX has no velocity arrows, since it doesn't yet support visualization markers.

Quick Start#

Pass --viz to any train command to launch a visualizer. --visualizer is an equivalent alias.

# Newton GL: lightweight OpenGL viewport
uv run isaaclab train --rl_library rsl_rl --task Isaac-Cartpole --viz newton_gl

# Viser: browser-based viewer
uv run isaaclab train --rl_library rsl_rl --task Isaac-Cartpole --viz viser

# Kit viewport
uv run isaaclab train --rl_library rsl_rl --task Isaac-Cartpole --viz kit

# Multiple visualizers simultaneously (comma-separated, no spaces)
uv run isaaclab train --rl_library rsl_rl --task Isaac-Cartpole --viz rerun,newton_rtx
./isaaclab.sh train --rl_library rsl_rl --task Isaac-Cartpole --viz newton_gl
./isaaclab.sh train --rl_library rsl_rl --task Isaac-Cartpole --viz viser
./isaaclab.sh train --rl_library rsl_rl --task Isaac-Cartpole --viz kit
./isaaclab.sh train --rl_library rsl_rl --task Isaac-Cartpole --viz rerun,newton_rtx

Note

Most tasks default to a PhysX backend, which requires Isaac Sim. If it isn’t installed yet, add --extra isaacsim to the uv run commands above; see Optional extras for details.

For combining visualizers, running headless, and other common use cases, see Usage below.

Visualizer Overview#

Visualizer

Description

Newton GL

Lightweight, strong feature support; Recommended

Viser

Web-based, supports recording and replay; Recommended

Newton RTX

High-quality RTX rendering; experimental, missing some features

Rerun

Web-based, supports recording and replay; limited UI toggles

Kit

High-quality RTX rendering, rich Isaac Sim tooling; longer start-up time

The Newton GL visualizer is a lightweight OpenGL window with minimal startup overhead.

Isaac-Reorient-Cube-Allegro in Newton GL Visualizer
Each Allegro hand reorients its cube to match the target shown by the marker above it

Visualizer-specific features:

  • Pause Rendering, Pause Simulation, and Reset Episode ImGui controls

  • Rigid-body dragging via right-click, see right (Newton solvers only)

  • Adjustable render update frequency

newton_viewer_dominoes demo
Right-click dragging the first domino triggers the cascade across an NVIDIA-logo domino layout

Core configuration:

from isaaclab_visualizers.newton import NewtonGLVisualizerCfg

visualizer_cfg = NewtonGLVisualizerCfg(
    eye=(8.0, 8.0, 3.0),
    lookat=(0.0, 0.0, 0.0),
    window_width=1920,
    window_height=1080,
    show_joints=False,
    show_contacts=False,
    enable_live_plots=True,
)

For the full config reference, see the config classes below.

NewtonGLVisualizerCfg source
@configclass
class NewtonGLVisualizerCfg(NewtonVisualizerCfg):
    """Configuration for the Newton OpenGL rasterizer visualizer.

    Selects Newton's OpenGL backend — fast local window with the full Isaac Lab
    feature set: streaming camera panel, particle color override, and live scalar/array plots.

    The streaming camera panel is enabled by default (``streaming_view=True``) but starts
    hidden — no camera rendering work is performed until the user opens the panel via the
    sidebar combo, keeping per-step overhead zero when the panel is closed.
    """

    class_type: type[NewtonGLVisualizer] | str = "{DIR}.newton_visualizer:NewtonGLVisualizer"
    """Visualizer implementation class."""

    visualizer_type: str = "newton_gl"
    """Visualizer selector identifier. Do not change."""

    streaming_view: bool = True
    """Enable the tiled streaming camera panel.

    Overrides the base-class default of ``False``.  The panel starts **hidden** so there
    is no per-step camera rendering cost; the user can open it at any time via the
    *Streaming View* combo in the Newton sidebar.
    """
NewtonVisualizerCfg source (shared Newton base class)
@configclass
class NewtonVisualizerCfg(VisualizerCfg):
    """Shared configuration base for Newton visualizer backends.

    .. deprecated::
        :class:`NewtonVisualizerCfg` is deprecated. Use :class:`NewtonGLVisualizerCfg` for the
        OpenGL rasterizer or :class:`NewtonRTXVisualizerCfg` for the OVRTX path tracer.
    """

    class_type: type[NewtonGLVisualizer] | str = "{DIR}.newton_visualizer:NewtonGLVisualizer"
    """Deprecated alias for the Newton GL visualizer implementation."""

    # Deprecated alias: "newton" routes to the GL backend via simulation_context._VISUALIZER_ALIASES.
    visualizer_type: str = "newton_gl"

    def __post_init__(self):
        if type(self) is NewtonVisualizerCfg:
            warnings.warn(
                "NewtonVisualizerCfg is deprecated and will be removed in a future release. "
                "Use NewtonGLVisualizerCfg (OpenGL rasterizer) or NewtonRTXVisualizerCfg (OVRTX path tracer) instead.",
                DeprecationWarning,
                stacklevel=3,
            )

    window_width: int = 1920
    """Window width in pixels."""

    window_height: int = 1080
    """Window height in pixels."""

    headless: bool = False
    """Run the Newton viewer without requiring a display server."""

    update_frequency: int = 1
    """Visualizer update frequency (renders every N simulation frames)."""

    world_spacing: tuple[float, float, float] = (0.0, 0.0, 0.0)
    """Visual spacing between simulation worlds along each axis [m].

    Non-zero axes arrange visible worlds in a compact grid without changing their simulated poses.
    """

    show_joints: bool = False
    """Show joint visualization."""

    show_contacts: bool = False
    """Show contact visualization."""

    show_collision: bool = False
    """Show collision visualization."""

    show_springs: bool = False
    """Show spring visualization."""

    show_inertia_boxes: bool = False
    """Show inertia box visualization."""

    show_com: bool = False
    """Show center of mass visualization."""

    show_particles: bool = False
    """Show particle visualization."""

    particle_color: tuple[float, float, float] | None = None
    """Optional particle color RGB [0, 1]. Uses Newton viewer defaults when ``None``."""

    enable_picking: bool = True
    """Enable right-click dragging with Newton rigid-body solvers.

    Supported coupled solvers may expose dragging through a rigid-body entry.
    Disabled automatically for headless viewers, standalone MPM, and non-Newton
    physics. MPM particles are not pickable.
    """

    enable_shadows: bool = True
    """Enable shadow rendering."""

    enable_sky: bool = True
    """Enable sky rendering."""

    enable_wireframe: bool = False
    """Enable wireframe rendering."""

    sky_upper_color: tuple[float, float, float] = (0.2, 0.4, 0.6)
    """Sky upper color RGB [0, 1]."""

    sky_lower_color: tuple[float, float, float] = (0.5, 0.6, 0.7)
    """Sky lower color RGB [0, 1]."""

    light_color: tuple[float, float, float] = (1.0, 1.0, 1.0)
    """Light color RGB [0, 1]."""
VisualizerCfg source (shared base class)
@configclass
class VisualizerCfg:
    """Base configuration for all visualizer backends.

    Note:
        This is an abstract base class and should not be instantiated directly.
        Use specific configs from isaaclab_visualizers: KitVisualizerCfg, NewtonGLVisualizerCfg,
        RerunVisualizerCfg, or ViserVisualizerCfg (from isaaclab_visualizers.kit/.newton/.rerun/.viser).
    """

    class_type: type[BaseVisualizer] | str | None = None
    """Visualizer implementation class. Concrete configs must set this field."""

    # Primary interactive camera settings
    eye: tuple[float, float, float] = (4.0, -4.0, 3.0)
    """Interactive visualizer camera eye position in world coordinates."""

    lookat: tuple[float, float, float] = (0.0, 0.0, 0.0)
    """Interactive visualizer camera look-at target in world coordinates."""

    focal_length: float = 12.0
    """Camera focal length in millimeters for visualizer camera views."""

    # ── Streaming view ────────────────────────────────────────────────────────
    # Captures pixels from a camera sensor (existing or auto-created), tiles them
    # across envs and GT types, and shows the result as an image panel in interactive
    # visualizers (Newton GL, Kit) or pushes it per-step to sink-based ones (Rerun, Viser).

    streaming_view: bool = False
    """Enable the streaming camera image view (opt-in, disabled by default)."""

    # Source — existing sensor (takes priority when set)
    streaming_sensor_prim_path: str | None = None
    """Prim path of an existing TiledCamera sensor to stream from.

    When set, all ``streaming_cam_*`` fields are ignored.  Should point to an
    existing camera sensor, e.g. ``"/World/envs/*/Camera"``.
    """

    # Source — auto-created camera (used when streaming_sensor_prim_path is None)
    streaming_cam_target_prim_path: str | None = None
    """Target prim for the auto-created streaming camera (ignored when
    :attr:`streaming_sensor_prim_path` is set).

    When ``None`` (the default), the visualizer adopts the first scene camera
    sensor it discovers dynamically at initialization time.  If no scene camera
    exists the streaming panel remains empty.  Set this explicitly (e.g.
    ``"/World/envs/*/Robot"``) only when you need an auto-created follow-camera
    and no suitable scene camera is present.
    """

    streaming_cam_eye: tuple[float, float, float] = (4.0, -4.0, 3.0)
    """Eye offset [m] for the auto-created streaming camera relative to the target prim."""

    streaming_cam_renderer: str | None = None
    """Renderer for the auto-created streaming camera.

    One of ``"newton_warp"``, ``"ovrtx"``, or ``None`` (let each backend
    choose its own default).  Defaults to ``None`` so each backend selects
    an appropriate renderer automatically.  Ignored when
    :attr:`streaming_sensor_prim_path` is set.
    """

    # Shared settings
    streaming_envs: int | list[int] = 32
    """Environments to capture.

    * ``int`` — sample this many envs once at initialization (from all visible envs).
    * ``list[int]`` — capture exactly these env indices.
    """

    streaming_gt_types: tuple[str, ...] = ("rgb",)
    """GT data types displayed left-to-right per environment row.

    Valid values: ``"rgb"``, ``"depth"``, ``"segmentation"``, ``"normals"``.
    Validated against :data:`~isaaclab.envs.utils.camera_colorizer.SUPPORTED_GT_TYPES`
    at initialization time (only when :attr:`streaming_view` is ``True``).
    """

    streaming_depth_min: float = 0.1
    """Near-clip for the turbo depth colormap [m].  Used when ``"depth"`` is in
    :attr:`streaming_gt_types`."""

    streaming_depth_max: float = 10.0
    """Far-clip for the turbo depth colormap [m].  Used when ``"depth"`` is in
    :attr:`streaming_gt_types`."""

    # Partial visualization settings
    max_visible_envs: int | None = None
    """Upper bound on how many envs are shown.

    * If visible_env_indices is not None, then this field will apply also
      to the explicit env indices set to the visible_env_indices.
    """

    visible_env_indices: list[int] | None = None
    """env indices to visualize in order (out-of-range indices are dropped)."""

    randomly_sample_visible_envs: bool = True
    """If ``max_visible_envs`` is provided, when enabled, selected visible envs are randomly sampled.
       If disabled, the first ``max_visible_envs`` envs are selected.

    * Note: ``visible_env_indices`` overrides this field.
    """

    # Visualization Markers
    enable_markers: bool = True
    """Enable visualization markers (debug drawing)."""

    # Live Plots
    enable_live_plots: bool = True
    """Stream per-step scalar data (manager terms, episode reward, episode length) into the visualizer.

    Plot windows start hidden or collapsed by default and can be toggled open at runtime.
    Set to ``False`` to disable live plots entirely and avoid any collection overhead.
    """

    live_plots_update_interval: int = 5
    """Collect and push live plot data every ``N`` simulation steps (default: every 5 steps)."""

    # Internal
    visualizer_type: str | None = None
    """Type identifier (e.g., 'newton', 'rerun', 'viser', 'kit'). Must be overridden by subclasses."""

    # Deprecated aliases kept for one-release compatibility. Remove in the next major release.
    tiled_cam_view: bool | None = None
    """Deprecated. Use :attr:`streaming_view` instead."""

    tiled_cam_num: int | None = None
    """Deprecated. Use :attr:`streaming_envs` (int) instead."""

    tiled_cam_env_indices: list[int] | None = None
    """Deprecated. Use :attr:`streaming_envs` (list[int]) instead."""

    tiled_cam_prim_path: str | None = None
    """Deprecated. Use :attr:`streaming_sensor_prim_path` instead."""

    tiled_cam_eye: tuple[float, float, float] | None = None
    """Deprecated. Use :attr:`streaming_cam_eye` instead."""

    tiled_cam_target_prim_path: str | None = None
    """Deprecated. Use :attr:`streaming_cam_target_prim_path` instead."""

    tiled_cam_renderer: str | None = None
    """Deprecated. Use :attr:`streaming_cam_renderer` instead."""

    def __post_init__(self) -> None:
        import warnings

        _simple = [
            ("tiled_cam_view", "streaming_view"),
            ("tiled_cam_prim_path", "streaming_sensor_prim_path"),
            ("tiled_cam_eye", "streaming_cam_eye"),
            ("tiled_cam_target_prim_path", "streaming_cam_target_prim_path"),
            ("tiled_cam_renderer", "streaming_cam_renderer"),
        ]
        for old, new in _simple:
            val = getattr(self, old)
            if val is not None:
                warnings.warn(f"{old!r} is deprecated; use {new!r} instead.", DeprecationWarning, stacklevel=3)
                setattr(self, new, val)
                setattr(self, old, None)
        # tiled_cam_env_indices takes priority over tiled_cam_num
        env_indices = getattr(self, "tiled_cam_env_indices")
        if env_indices is not None:
            warnings.warn(
                "'tiled_cam_env_indices' is deprecated; use 'streaming_envs' instead.",
                DeprecationWarning,
                stacklevel=3,
            )
            self.streaming_envs = env_indices
            self.tiled_cam_env_indices = None
            self.tiled_cam_num = None
        else:
            num = getattr(self, "tiled_cam_num")
            if num is not None:
                warnings.warn(
                    "'tiled_cam_num' is deprecated; use 'streaming_envs' instead.",
                    DeprecationWarning,
                    stacklevel=3,
                )
                self.streaming_envs = num
                self.tiled_cam_num = None

Viser streams the Newton Warp renderer to a local web server, at http://localhost:8080 by default, with optional public share URLs via share=True and .viser recording files for replay.

Isaac-Lift-KukaAllegro in Viser Visualizer
Each Kuka arm and Allegro hand lifts its cube off the table
Table color shows success: red until the cube reaches its target pose, green once it does.

Visualizer-specific features:

  • Public share URL for remote access, set share=True

  • Pause Rendering, Pause Simulation, and Reset Episode sidebar controls

Important

A URL is printed before training begins. Set open_browser=True to open it automatically. For remote access, set display_address to the machine hostname or IP and ensure the configured port is reachable from the browser.

╭────── viser (listening *:8080) ───────╮
│   URL  │  http://localhost:8080        │
╰───────────────────────────────────────╯

Core configuration:

from isaaclab_visualizers.viser import ViserVisualizerCfg

visualizer_cfg = ViserVisualizerCfg(
    port=8080,
    bind_address="0.0.0.0",       # use 0.0.0.0 for remote access
    display_address="localhost",   # hostname shown in the printed URL
    open_browser=False,
    share=False,                   # request a public share URL
    record_to_viser=None,          # set a path to save a .viser recording
)

For the full config reference, see the config classes below.

ViserVisualizerCfg source
@configclass
class ViserVisualizerCfg(VisualizerCfg):
    """Configuration for Viser visualizer (web-based visualization)."""

    class_type: type[ViserVisualizer] | str = "{DIR}.viser_visualizer:ViserVisualizer"
    """Visualizer implementation class."""

    visualizer_type: str = "viser"
    """Type identifier for Viser visualizer."""

    port: int = 8080
    """Port of the local viser web server."""

    bind_address: str = "0.0.0.0"
    """Host/interface for the Viser server to bind.

    Use ``"0.0.0.0"`` to listen on all interfaces for remote access.
    """

    display_address: str = "localhost"
    """Host name or IP address shown in the printed browser URL.

    For remote access, set this to the hostname/IP reachable from your browser.
    """

    open_browser: bool = False
    """Whether to attempt opening the viser web viewer URL in a browser.

    The viewer URL is always logged during initialization. Set this to ``True`` to auto-launch it.
    """

    verbose: bool = True
    """Whether to print viewer server startup information."""

    share: bool = False
    """Whether to request a public share URL from viser."""

    record_to_viser: str | None = None
    """Path to save a .viser recording file. None = no recording."""

    show_particles: bool = True
    """Whether to render particle systems (MPM, VBD) in the Viser viewer.

    Defaults to ``True`` so particle simulations (granular, cloth, soft-body) are
    visible on startup.  Can also be toggled at runtime via the Visualization Markers
    panel in the Viser sidebar.
    """
VisualizerCfg source (shared base class)
@configclass
class VisualizerCfg:
    """Base configuration for all visualizer backends.

    Note:
        This is an abstract base class and should not be instantiated directly.
        Use specific configs from isaaclab_visualizers: KitVisualizerCfg, NewtonGLVisualizerCfg,
        RerunVisualizerCfg, or ViserVisualizerCfg (from isaaclab_visualizers.kit/.newton/.rerun/.viser).
    """

    class_type: type[BaseVisualizer] | str | None = None
    """Visualizer implementation class. Concrete configs must set this field."""

    # Primary interactive camera settings
    eye: tuple[float, float, float] = (4.0, -4.0, 3.0)
    """Interactive visualizer camera eye position in world coordinates."""

    lookat: tuple[float, float, float] = (0.0, 0.0, 0.0)
    """Interactive visualizer camera look-at target in world coordinates."""

    focal_length: float = 12.0
    """Camera focal length in millimeters for visualizer camera views."""

    # ── Streaming view ────────────────────────────────────────────────────────
    # Captures pixels from a camera sensor (existing or auto-created), tiles them
    # across envs and GT types, and shows the result as an image panel in interactive
    # visualizers (Newton GL, Kit) or pushes it per-step to sink-based ones (Rerun, Viser).

    streaming_view: bool = False
    """Enable the streaming camera image view (opt-in, disabled by default)."""

    # Source — existing sensor (takes priority when set)
    streaming_sensor_prim_path: str | None = None
    """Prim path of an existing TiledCamera sensor to stream from.

    When set, all ``streaming_cam_*`` fields are ignored.  Should point to an
    existing camera sensor, e.g. ``"/World/envs/*/Camera"``.
    """

    # Source — auto-created camera (used when streaming_sensor_prim_path is None)
    streaming_cam_target_prim_path: str | None = None
    """Target prim for the auto-created streaming camera (ignored when
    :attr:`streaming_sensor_prim_path` is set).

    When ``None`` (the default), the visualizer adopts the first scene camera
    sensor it discovers dynamically at initialization time.  If no scene camera
    exists the streaming panel remains empty.  Set this explicitly (e.g.
    ``"/World/envs/*/Robot"``) only when you need an auto-created follow-camera
    and no suitable scene camera is present.
    """

    streaming_cam_eye: tuple[float, float, float] = (4.0, -4.0, 3.0)
    """Eye offset [m] for the auto-created streaming camera relative to the target prim."""

    streaming_cam_renderer: str | None = None
    """Renderer for the auto-created streaming camera.

    One of ``"newton_warp"``, ``"ovrtx"``, or ``None`` (let each backend
    choose its own default).  Defaults to ``None`` so each backend selects
    an appropriate renderer automatically.  Ignored when
    :attr:`streaming_sensor_prim_path` is set.
    """

    # Shared settings
    streaming_envs: int | list[int] = 32
    """Environments to capture.

    * ``int`` — sample this many envs once at initialization (from all visible envs).
    * ``list[int]`` — capture exactly these env indices.
    """

    streaming_gt_types: tuple[str, ...] = ("rgb",)
    """GT data types displayed left-to-right per environment row.

    Valid values: ``"rgb"``, ``"depth"``, ``"segmentation"``, ``"normals"``.
    Validated against :data:`~isaaclab.envs.utils.camera_colorizer.SUPPORTED_GT_TYPES`
    at initialization time (only when :attr:`streaming_view` is ``True``).
    """

    streaming_depth_min: float = 0.1
    """Near-clip for the turbo depth colormap [m].  Used when ``"depth"`` is in
    :attr:`streaming_gt_types`."""

    streaming_depth_max: float = 10.0
    """Far-clip for the turbo depth colormap [m].  Used when ``"depth"`` is in
    :attr:`streaming_gt_types`."""

    # Partial visualization settings
    max_visible_envs: int | None = None
    """Upper bound on how many envs are shown.

    * If visible_env_indices is not None, then this field will apply also
      to the explicit env indices set to the visible_env_indices.
    """

    visible_env_indices: list[int] | None = None
    """env indices to visualize in order (out-of-range indices are dropped)."""

    randomly_sample_visible_envs: bool = True
    """If ``max_visible_envs`` is provided, when enabled, selected visible envs are randomly sampled.
       If disabled, the first ``max_visible_envs`` envs are selected.

    * Note: ``visible_env_indices`` overrides this field.
    """

    # Visualization Markers
    enable_markers: bool = True
    """Enable visualization markers (debug drawing)."""

    # Live Plots
    enable_live_plots: bool = True
    """Stream per-step scalar data (manager terms, episode reward, episode length) into the visualizer.

    Plot windows start hidden or collapsed by default and can be toggled open at runtime.
    Set to ``False`` to disable live plots entirely and avoid any collection overhead.
    """

    live_plots_update_interval: int = 5
    """Collect and push live plot data every ``N`` simulation steps (default: every 5 steps)."""

    # Internal
    visualizer_type: str | None = None
    """Type identifier (e.g., 'newton', 'rerun', 'viser', 'kit'). Must be overridden by subclasses."""

    # Deprecated aliases kept for one-release compatibility. Remove in the next major release.
    tiled_cam_view: bool | None = None
    """Deprecated. Use :attr:`streaming_view` instead."""

    tiled_cam_num: int | None = None
    """Deprecated. Use :attr:`streaming_envs` (int) instead."""

    tiled_cam_env_indices: list[int] | None = None
    """Deprecated. Use :attr:`streaming_envs` (list[int]) instead."""

    tiled_cam_prim_path: str | None = None
    """Deprecated. Use :attr:`streaming_sensor_prim_path` instead."""

    tiled_cam_eye: tuple[float, float, float] | None = None
    """Deprecated. Use :attr:`streaming_cam_eye` instead."""

    tiled_cam_target_prim_path: str | None = None
    """Deprecated. Use :attr:`streaming_cam_target_prim_path` instead."""

    tiled_cam_renderer: str | None = None
    """Deprecated. Use :attr:`streaming_cam_renderer` instead."""

    def __post_init__(self) -> None:
        import warnings

        _simple = [
            ("tiled_cam_view", "streaming_view"),
            ("tiled_cam_prim_path", "streaming_sensor_prim_path"),
            ("tiled_cam_eye", "streaming_cam_eye"),
            ("tiled_cam_target_prim_path", "streaming_cam_target_prim_path"),
            ("tiled_cam_renderer", "streaming_cam_renderer"),
        ]
        for old, new in _simple:
            val = getattr(self, old)
            if val is not None:
                warnings.warn(f"{old!r} is deprecated; use {new!r} instead.", DeprecationWarning, stacklevel=3)
                setattr(self, new, val)
                setattr(self, old, None)
        # tiled_cam_env_indices takes priority over tiled_cam_num
        env_indices = getattr(self, "tiled_cam_env_indices")
        if env_indices is not None:
            warnings.warn(
                "'tiled_cam_env_indices' is deprecated; use 'streaming_envs' instead.",
                DeprecationWarning,
                stacklevel=3,
            )
            self.streaming_envs = env_indices
            self.tiled_cam_env_indices = None
            self.tiled_cam_num = None
        else:
            num = getattr(self, "tiled_cam_num")
            if num is not None:
                warnings.warn(
                    "'tiled_cam_num' is deprecated; use 'streaming_envs' instead.",
                    DeprecationWarning,
                    stacklevel=3,
                )
                self.streaming_envs = num
                self.tiled_cam_num = None

The experimental Newton RTX visualizer, launched with --viz newton_rtx, uses the OVRTX path-tracer for photorealistic rendering without a Kit process. It is slightly slower than the Newton GL visualizer due to the cost of high-quality RTX rendering.

Isaac-Velocity-Rough-H1 in Newton RTX Visualizer
Each H1 humanoid climbs a staircase sub-terrain

Visualizer-specific features:

  • OVRTX path-traced rendering for photorealistic, physically-based lighting

Note

The following features are not yet supported and will be added in a future release: visualization markers, live plots, and pause rendering. All are available in the other visualizers. The streaming camera panel’s live on-screen preview is also unavailable, but headless streaming capture (e.g. for VideoRecorderCfg) works.

Core configuration:

from isaaclab_visualizers.newton import NewtonRTXVisualizerCfg

visualizer_cfg = NewtonRTXVisualizerCfg(
    eye=(8.0, 8.0, 3.0),
    lookat=(0.0, 0.0, 0.0),
)

For the full config reference, see the config classes below.

NewtonRTXVisualizerCfg source
@configclass
class NewtonRTXVisualizerCfg(NewtonVisualizerCfg):
    """Configuration for the Newton OVRTX path-tracer visualizer.

    Selects Newton's OVRTX backend — photorealistic rendering using the same
    ``begin_frame / log_state / end_frame`` step interface as the GL backend.

    .. note::
        RTX render quality settings (fps, lighting environment, denoiser, etc.)
        are not yet exposed here; ``ViewerRTX`` defaults are used. These will be
        surfaced in a future revision in a way that is consistent across all
        RTX-capable renderers.

    ``render_rgb_array()`` captures the path-traced LDR framebuffer at
    :attr:`window_width` by :attr:`window_height`. The tiled camera panel remains
    unsupported because ``ViewerRTX.log_image`` has no display sink.
    """

    class_type: type[NewtonRTXVisualizer] | str = "{DIR}.newton_visualizer:NewtonRTXVisualizer"
    """Visualizer implementation class."""

    visualizer_type: str = "newton_rtx"
    """Visualizer selector identifier. Do not change."""

    rtx_environment: str = "default"
    """OVRTX lighting environment.  One of ``"default"`` (dome + distant light),
    ``"studio"`` (three-point rig for cleaner highlights), or ``"none"``."""

    render_settings: dict[str, Any] = dict()
    """RTX attributes to author on the OVRTX render product, as ``{name: (usd_type_name, value)}``.

    ``usd_type_name`` names an ``Sdf.ValueTypeNames`` member, as a string so the config stays
    copyable. For example, ``{"omni:rtx:quality": ("Int", 100)}`` re-enables the path tracer's
    quality convergence loop, which ``ViewerRTX`` otherwise disables to keep interactive latency
    down."""
NewtonVisualizerCfg source (shared Newton base class)
@configclass
class NewtonVisualizerCfg(VisualizerCfg):
    """Shared configuration base for Newton visualizer backends.

    .. deprecated::
        :class:`NewtonVisualizerCfg` is deprecated. Use :class:`NewtonGLVisualizerCfg` for the
        OpenGL rasterizer or :class:`NewtonRTXVisualizerCfg` for the OVRTX path tracer.
    """

    class_type: type[NewtonGLVisualizer] | str = "{DIR}.newton_visualizer:NewtonGLVisualizer"
    """Deprecated alias for the Newton GL visualizer implementation."""

    # Deprecated alias: "newton" routes to the GL backend via simulation_context._VISUALIZER_ALIASES.
    visualizer_type: str = "newton_gl"

    def __post_init__(self):
        if type(self) is NewtonVisualizerCfg:
            warnings.warn(
                "NewtonVisualizerCfg is deprecated and will be removed in a future release. "
                "Use NewtonGLVisualizerCfg (OpenGL rasterizer) or NewtonRTXVisualizerCfg (OVRTX path tracer) instead.",
                DeprecationWarning,
                stacklevel=3,
            )

    window_width: int = 1920
    """Window width in pixels."""

    window_height: int = 1080
    """Window height in pixels."""

    headless: bool = False
    """Run the Newton viewer without requiring a display server."""

    update_frequency: int = 1
    """Visualizer update frequency (renders every N simulation frames)."""

    world_spacing: tuple[float, float, float] = (0.0, 0.0, 0.0)
    """Visual spacing between simulation worlds along each axis [m].

    Non-zero axes arrange visible worlds in a compact grid without changing their simulated poses.
    """

    show_joints: bool = False
    """Show joint visualization."""

    show_contacts: bool = False
    """Show contact visualization."""

    show_collision: bool = False
    """Show collision visualization."""

    show_springs: bool = False
    """Show spring visualization."""

    show_inertia_boxes: bool = False
    """Show inertia box visualization."""

    show_com: bool = False
    """Show center of mass visualization."""

    show_particles: bool = False
    """Show particle visualization."""

    particle_color: tuple[float, float, float] | None = None
    """Optional particle color RGB [0, 1]. Uses Newton viewer defaults when ``None``."""

    enable_picking: bool = True
    """Enable right-click dragging with Newton rigid-body solvers.

    Supported coupled solvers may expose dragging through a rigid-body entry.
    Disabled automatically for headless viewers, standalone MPM, and non-Newton
    physics. MPM particles are not pickable.
    """

    enable_shadows: bool = True
    """Enable shadow rendering."""

    enable_sky: bool = True
    """Enable sky rendering."""

    enable_wireframe: bool = False
    """Enable wireframe rendering."""

    sky_upper_color: tuple[float, float, float] = (0.2, 0.4, 0.6)
    """Sky upper color RGB [0, 1]."""

    sky_lower_color: tuple[float, float, float] = (0.5, 0.6, 0.7)
    """Sky lower color RGB [0, 1]."""

    light_color: tuple[float, float, float] = (1.0, 1.0, 1.0)
    """Light color RGB [0, 1]."""
VisualizerCfg source (shared base class)
@configclass
class VisualizerCfg:
    """Base configuration for all visualizer backends.

    Note:
        This is an abstract base class and should not be instantiated directly.
        Use specific configs from isaaclab_visualizers: KitVisualizerCfg, NewtonGLVisualizerCfg,
        RerunVisualizerCfg, or ViserVisualizerCfg (from isaaclab_visualizers.kit/.newton/.rerun/.viser).
    """

    class_type: type[BaseVisualizer] | str | None = None
    """Visualizer implementation class. Concrete configs must set this field."""

    # Primary interactive camera settings
    eye: tuple[float, float, float] = (4.0, -4.0, 3.0)
    """Interactive visualizer camera eye position in world coordinates."""

    lookat: tuple[float, float, float] = (0.0, 0.0, 0.0)
    """Interactive visualizer camera look-at target in world coordinates."""

    focal_length: float = 12.0
    """Camera focal length in millimeters for visualizer camera views."""

    # ── Streaming view ────────────────────────────────────────────────────────
    # Captures pixels from a camera sensor (existing or auto-created), tiles them
    # across envs and GT types, and shows the result as an image panel in interactive
    # visualizers (Newton GL, Kit) or pushes it per-step to sink-based ones (Rerun, Viser).

    streaming_view: bool = False
    """Enable the streaming camera image view (opt-in, disabled by default)."""

    # Source — existing sensor (takes priority when set)
    streaming_sensor_prim_path: str | None = None
    """Prim path of an existing TiledCamera sensor to stream from.

    When set, all ``streaming_cam_*`` fields are ignored.  Should point to an
    existing camera sensor, e.g. ``"/World/envs/*/Camera"``.
    """

    # Source — auto-created camera (used when streaming_sensor_prim_path is None)
    streaming_cam_target_prim_path: str | None = None
    """Target prim for the auto-created streaming camera (ignored when
    :attr:`streaming_sensor_prim_path` is set).

    When ``None`` (the default), the visualizer adopts the first scene camera
    sensor it discovers dynamically at initialization time.  If no scene camera
    exists the streaming panel remains empty.  Set this explicitly (e.g.
    ``"/World/envs/*/Robot"``) only when you need an auto-created follow-camera
    and no suitable scene camera is present.
    """

    streaming_cam_eye: tuple[float, float, float] = (4.0, -4.0, 3.0)
    """Eye offset [m] for the auto-created streaming camera relative to the target prim."""

    streaming_cam_renderer: str | None = None
    """Renderer for the auto-created streaming camera.

    One of ``"newton_warp"``, ``"ovrtx"``, or ``None`` (let each backend
    choose its own default).  Defaults to ``None`` so each backend selects
    an appropriate renderer automatically.  Ignored when
    :attr:`streaming_sensor_prim_path` is set.
    """

    # Shared settings
    streaming_envs: int | list[int] = 32
    """Environments to capture.

    * ``int`` — sample this many envs once at initialization (from all visible envs).
    * ``list[int]`` — capture exactly these env indices.
    """

    streaming_gt_types: tuple[str, ...] = ("rgb",)
    """GT data types displayed left-to-right per environment row.

    Valid values: ``"rgb"``, ``"depth"``, ``"segmentation"``, ``"normals"``.
    Validated against :data:`~isaaclab.envs.utils.camera_colorizer.SUPPORTED_GT_TYPES`
    at initialization time (only when :attr:`streaming_view` is ``True``).
    """

    streaming_depth_min: float = 0.1
    """Near-clip for the turbo depth colormap [m].  Used when ``"depth"`` is in
    :attr:`streaming_gt_types`."""

    streaming_depth_max: float = 10.0
    """Far-clip for the turbo depth colormap [m].  Used when ``"depth"`` is in
    :attr:`streaming_gt_types`."""

    # Partial visualization settings
    max_visible_envs: int | None = None
    """Upper bound on how many envs are shown.

    * If visible_env_indices is not None, then this field will apply also
      to the explicit env indices set to the visible_env_indices.
    """

    visible_env_indices: list[int] | None = None
    """env indices to visualize in order (out-of-range indices are dropped)."""

    randomly_sample_visible_envs: bool = True
    """If ``max_visible_envs`` is provided, when enabled, selected visible envs are randomly sampled.
       If disabled, the first ``max_visible_envs`` envs are selected.

    * Note: ``visible_env_indices`` overrides this field.
    """

    # Visualization Markers
    enable_markers: bool = True
    """Enable visualization markers (debug drawing)."""

    # Live Plots
    enable_live_plots: bool = True
    """Stream per-step scalar data (manager terms, episode reward, episode length) into the visualizer.

    Plot windows start hidden or collapsed by default and can be toggled open at runtime.
    Set to ``False`` to disable live plots entirely and avoid any collection overhead.
    """

    live_plots_update_interval: int = 5
    """Collect and push live plot data every ``N`` simulation steps (default: every 5 steps)."""

    # Internal
    visualizer_type: str | None = None
    """Type identifier (e.g., 'newton', 'rerun', 'viser', 'kit'). Must be overridden by subclasses."""

    # Deprecated aliases kept for one-release compatibility. Remove in the next major release.
    tiled_cam_view: bool | None = None
    """Deprecated. Use :attr:`streaming_view` instead."""

    tiled_cam_num: int | None = None
    """Deprecated. Use :attr:`streaming_envs` (int) instead."""

    tiled_cam_env_indices: list[int] | None = None
    """Deprecated. Use :attr:`streaming_envs` (list[int]) instead."""

    tiled_cam_prim_path: str | None = None
    """Deprecated. Use :attr:`streaming_sensor_prim_path` instead."""

    tiled_cam_eye: tuple[float, float, float] | None = None
    """Deprecated. Use :attr:`streaming_cam_eye` instead."""

    tiled_cam_target_prim_path: str | None = None
    """Deprecated. Use :attr:`streaming_cam_target_prim_path` instead."""

    tiled_cam_renderer: str | None = None
    """Deprecated. Use :attr:`streaming_cam_renderer` instead."""

    def __post_init__(self) -> None:
        import warnings

        _simple = [
            ("tiled_cam_view", "streaming_view"),
            ("tiled_cam_prim_path", "streaming_sensor_prim_path"),
            ("tiled_cam_eye", "streaming_cam_eye"),
            ("tiled_cam_target_prim_path", "streaming_cam_target_prim_path"),
            ("tiled_cam_renderer", "streaming_cam_renderer"),
        ]
        for old, new in _simple:
            val = getattr(self, old)
            if val is not None:
                warnings.warn(f"{old!r} is deprecated; use {new!r} instead.", DeprecationWarning, stacklevel=3)
                setattr(self, new, val)
                setattr(self, old, None)
        # tiled_cam_env_indices takes priority over tiled_cam_num
        env_indices = getattr(self, "tiled_cam_env_indices")
        if env_indices is not None:
            warnings.warn(
                "'tiled_cam_env_indices' is deprecated; use 'streaming_envs' instead.",
                DeprecationWarning,
                stacklevel=3,
            )
            self.streaming_envs = env_indices
            self.tiled_cam_env_indices = None
            self.tiled_cam_num = None
        else:
            num = getattr(self, "tiled_cam_num")
            if num is not None:
                warnings.warn(
                    "'tiled_cam_num' is deprecated; use 'streaming_envs' instead.",
                    DeprecationWarning,
                    stacklevel=3,
                )
                self.streaming_envs = num
                self.tiled_cam_num = None

Warning

Newton RTX (OVRTX) is a kitless renderer and cannot be used in the same process as Kit (presets=isaacsim_physx). presets=ovphysx is itself kitless and works fine with Newton RTX. Use presets=newton_mjwarp,ovrtx or presets=ovphysx,ovrtx with --viz newton_rtx, or switch to --viz newton_gl, --viz viser, --viz rerun, or --viz kit with a Kit-compatible physics backend.

Like Viser, Rerun streams simulation state to a local web server, for remote monitoring, timeline playback, and recording to .rrd files.

Isaac-Reorient-Cube-Shadow-Direct in Rerun Visualizer
Each Shadow Hand reorients its cube to match the target pose

Visualizer-specific features:

  • Timeline scrubbing and playback of .rrd recordings

  • Pause Rendering and Reset Episode controls via the ImGui sidebar

Note

The native Play/Pause timeline controls in the Rerun visualizer UI do not work while visualizing a live simulation or training run. They are hidden by default, but Rerun’s dock panel UI can still be used to reveal them; when revealed, clicking them has no effect. Use Isaac Lab’s own Pause Rendering / Reset Episode controls instead. The timeline controls are only meaningful when replaying a saved .rrd recording.

Important

A highlighted URL is printed to the terminal before training begins. Ctrl-click it to open the viewer, or set open_browser=True to open it automatically.

╭─────────────────────────── rerun (listening *:9090) ───────────────────────────╮
│   URL  │  http://127.0.0.1:9090/?url=rerun%2Bhttp://127.0.0.1:9876/proxy       │
╰────────────────────────────────────────────────────────────────────────────────╯

Core configuration:

from isaaclab_visualizers.rerun import RerunVisualizerCfg

visualizer_cfg = RerunVisualizerCfg(
    eye=(8.0, 8.0, 3.0),
    lookat=(0.0, 0.0, 0.0),
    keep_historical_data=False,   # enable for time scrubbing
    keep_scalar_history=False,    # enable for scalar time-series
    record_to_rrd=None,           # set a path to save a .rrd recording
    open_browser=False,
)

For the full config reference, see the config classes below.

RerunVisualizerCfg source
@configclass
class RerunVisualizerCfg(VisualizerCfg):
    """Configuration for Rerun visualizer (web-based visualization)."""

    class_type: type[RerunVisualizer] | str = "{DIR}.rerun_visualizer:RerunVisualizer"
    """Visualizer implementation class."""

    visualizer_type: str = "rerun"
    """Type identifier for Rerun visualizer."""

    app_id: str = "isaaclab-simulation"
    """Application identifier shown in viewer title."""

    web_port: int = 9090
    """Port of the local rerun web viewer whose URL is logged during initialization."""

    grpc_port: int = 9876
    """Port of the rerun gRPC server (used when serving web viewer externally)."""

    bind_address: str | None = "0.0.0.0"
    """Host used for endpoint formatting and reuse checks.

    Notes:
    - If an existing rerun server is reachable on ``grpc_port``, it is reused.
    - New server startup is managed by ``newton.viewer.ViewerRerun`` via the rerun Python SDK.
    - Local browser links normalize common loopback/wildcard hosts to ``127.0.0.1``.
    """

    open_browser: bool = False
    """Whether to attempt opening the rerun web viewer URL in a browser.

    The viewer URL is always logged during initialization. Set this to ``True`` to auto-launch it.
    """

    keep_historical_data: bool = False
    """Keep transform history for time scrubbing (False = constant memory for training)."""

    keep_scalar_history: bool = False
    """Accumulate scalars as a time-series in the Rerun timeline (True = live plot history, False = constant memory).

    When :attr:`~isaaclab.visualizers.VisualizerCfg.enable_live_plots` is ``True`` (the default),
    this is automatically forced to ``True`` so that scalar values accumulate as a time series in
    the Rerun viewer.  Set to ``False`` explicitly to reduce memory usage when scalar history is
    not needed, but note this will disable live plot curves.
    """

    show_particles: bool = True
    """Whether to show model particles.

    Disable this option to reduce streaming overhead for large particle clouds.
    """

    record_to_rrd: str | None = None
    """Path to save .rrd recording file. None = no recording."""
VisualizerCfg source (shared base class)
@configclass
class VisualizerCfg:
    """Base configuration for all visualizer backends.

    Note:
        This is an abstract base class and should not be instantiated directly.
        Use specific configs from isaaclab_visualizers: KitVisualizerCfg, NewtonGLVisualizerCfg,
        RerunVisualizerCfg, or ViserVisualizerCfg (from isaaclab_visualizers.kit/.newton/.rerun/.viser).
    """

    class_type: type[BaseVisualizer] | str | None = None
    """Visualizer implementation class. Concrete configs must set this field."""

    # Primary interactive camera settings
    eye: tuple[float, float, float] = (4.0, -4.0, 3.0)
    """Interactive visualizer camera eye position in world coordinates."""

    lookat: tuple[float, float, float] = (0.0, 0.0, 0.0)
    """Interactive visualizer camera look-at target in world coordinates."""

    focal_length: float = 12.0
    """Camera focal length in millimeters for visualizer camera views."""

    # ── Streaming view ────────────────────────────────────────────────────────
    # Captures pixels from a camera sensor (existing or auto-created), tiles them
    # across envs and GT types, and shows the result as an image panel in interactive
    # visualizers (Newton GL, Kit) or pushes it per-step to sink-based ones (Rerun, Viser).

    streaming_view: bool = False
    """Enable the streaming camera image view (opt-in, disabled by default)."""

    # Source — existing sensor (takes priority when set)
    streaming_sensor_prim_path: str | None = None
    """Prim path of an existing TiledCamera sensor to stream from.

    When set, all ``streaming_cam_*`` fields are ignored.  Should point to an
    existing camera sensor, e.g. ``"/World/envs/*/Camera"``.
    """

    # Source — auto-created camera (used when streaming_sensor_prim_path is None)
    streaming_cam_target_prim_path: str | None = None
    """Target prim for the auto-created streaming camera (ignored when
    :attr:`streaming_sensor_prim_path` is set).

    When ``None`` (the default), the visualizer adopts the first scene camera
    sensor it discovers dynamically at initialization time.  If no scene camera
    exists the streaming panel remains empty.  Set this explicitly (e.g.
    ``"/World/envs/*/Robot"``) only when you need an auto-created follow-camera
    and no suitable scene camera is present.
    """

    streaming_cam_eye: tuple[float, float, float] = (4.0, -4.0, 3.0)
    """Eye offset [m] for the auto-created streaming camera relative to the target prim."""

    streaming_cam_renderer: str | None = None
    """Renderer for the auto-created streaming camera.

    One of ``"newton_warp"``, ``"ovrtx"``, or ``None`` (let each backend
    choose its own default).  Defaults to ``None`` so each backend selects
    an appropriate renderer automatically.  Ignored when
    :attr:`streaming_sensor_prim_path` is set.
    """

    # Shared settings
    streaming_envs: int | list[int] = 32
    """Environments to capture.

    * ``int`` — sample this many envs once at initialization (from all visible envs).
    * ``list[int]`` — capture exactly these env indices.
    """

    streaming_gt_types: tuple[str, ...] = ("rgb",)
    """GT data types displayed left-to-right per environment row.

    Valid values: ``"rgb"``, ``"depth"``, ``"segmentation"``, ``"normals"``.
    Validated against :data:`~isaaclab.envs.utils.camera_colorizer.SUPPORTED_GT_TYPES`
    at initialization time (only when :attr:`streaming_view` is ``True``).
    """

    streaming_depth_min: float = 0.1
    """Near-clip for the turbo depth colormap [m].  Used when ``"depth"`` is in
    :attr:`streaming_gt_types`."""

    streaming_depth_max: float = 10.0
    """Far-clip for the turbo depth colormap [m].  Used when ``"depth"`` is in
    :attr:`streaming_gt_types`."""

    # Partial visualization settings
    max_visible_envs: int | None = None
    """Upper bound on how many envs are shown.

    * If visible_env_indices is not None, then this field will apply also
      to the explicit env indices set to the visible_env_indices.
    """

    visible_env_indices: list[int] | None = None
    """env indices to visualize in order (out-of-range indices are dropped)."""

    randomly_sample_visible_envs: bool = True
    """If ``max_visible_envs`` is provided, when enabled, selected visible envs are randomly sampled.
       If disabled, the first ``max_visible_envs`` envs are selected.

    * Note: ``visible_env_indices`` overrides this field.
    """

    # Visualization Markers
    enable_markers: bool = True
    """Enable visualization markers (debug drawing)."""

    # Live Plots
    enable_live_plots: bool = True
    """Stream per-step scalar data (manager terms, episode reward, episode length) into the visualizer.

    Plot windows start hidden or collapsed by default and can be toggled open at runtime.
    Set to ``False`` to disable live plots entirely and avoid any collection overhead.
    """

    live_plots_update_interval: int = 5
    """Collect and push live plot data every ``N`` simulation steps (default: every 5 steps)."""

    # Internal
    visualizer_type: str | None = None
    """Type identifier (e.g., 'newton', 'rerun', 'viser', 'kit'). Must be overridden by subclasses."""

    # Deprecated aliases kept for one-release compatibility. Remove in the next major release.
    tiled_cam_view: bool | None = None
    """Deprecated. Use :attr:`streaming_view` instead."""

    tiled_cam_num: int | None = None
    """Deprecated. Use :attr:`streaming_envs` (int) instead."""

    tiled_cam_env_indices: list[int] | None = None
    """Deprecated. Use :attr:`streaming_envs` (list[int]) instead."""

    tiled_cam_prim_path: str | None = None
    """Deprecated. Use :attr:`streaming_sensor_prim_path` instead."""

    tiled_cam_eye: tuple[float, float, float] | None = None
    """Deprecated. Use :attr:`streaming_cam_eye` instead."""

    tiled_cam_target_prim_path: str | None = None
    """Deprecated. Use :attr:`streaming_cam_target_prim_path` instead."""

    tiled_cam_renderer: str | None = None
    """Deprecated. Use :attr:`streaming_cam_renderer` instead."""

    def __post_init__(self) -> None:
        import warnings

        _simple = [
            ("tiled_cam_view", "streaming_view"),
            ("tiled_cam_prim_path", "streaming_sensor_prim_path"),
            ("tiled_cam_eye", "streaming_cam_eye"),
            ("tiled_cam_target_prim_path", "streaming_cam_target_prim_path"),
            ("tiled_cam_renderer", "streaming_cam_renderer"),
        ]
        for old, new in _simple:
            val = getattr(self, old)
            if val is not None:
                warnings.warn(f"{old!r} is deprecated; use {new!r} instead.", DeprecationWarning, stacklevel=3)
                setattr(self, new, val)
                setattr(self, old, None)
        # tiled_cam_env_indices takes priority over tiled_cam_num
        env_indices = getattr(self, "tiled_cam_env_indices")
        if env_indices is not None:
            warnings.warn(
                "'tiled_cam_env_indices' is deprecated; use 'streaming_envs' instead.",
                DeprecationWarning,
                stacklevel=3,
            )
            self.streaming_envs = env_indices
            self.tiled_cam_env_indices = None
            self.tiled_cam_num = None
        else:
            num = getattr(self, "tiled_cam_num")
            if num is not None:
                warnings.warn(
                    "'tiled_cam_num' is deprecated; use 'streaming_envs' instead.",
                    DeprecationWarning,
                    stacklevel=3,
                )
                self.streaming_envs = num
                self.tiled_cam_num = None

The Kit visualizer embeds Isaac Lab inside a Kit process, providing access to the full Isaac Sim USD stage, RTX renderer, and GUI tooling. It has the highest startup and runtime overhead of the five visualizers.

Isaac-Reach-Franka in Kit Visualizer
Each Franka arm reaches its end-effector toward a randomized target pose

Visualizer-specific features:

  • Direct access to the Isaac Sim USD stage for inspecting and editing prims at runtime

  • Full Isaac Sim GUI tooling (Property, Layers, and Stage panels)

Core configuration:

from isaaclab_visualizers.kit import KitVisualizerCfg

visualizer_cfg = KitVisualizerCfg(
    eye=(8.0, 8.0, 3.0),
    lookat=(0.0, 0.0, 0.0),
    window_width=1280,
    window_height=720,
    enable_markers=True,
    enable_live_plots=True,
)

For the full config reference, see the config classes below.

KitVisualizerCfg source
@configclass
class KitVisualizerCfg(VisualizerCfg):
    """Configuration for Kit visualizer using Isaac Sim viewport.

    .. note::
        The streaming camera panel (``streaming_view=True``) requires the
        ``--enable_cameras`` CLI flag.  Without it, the streaming view is silently
        skipped and no image panel is created.  Set ``dock_position="RIGHT"`` so
        the panel appears side-by-side with the Viewport instead of as a hidden tab.
    """

    class_type: type[KitVisualizer] | str = "{DIR}.kit_visualizer:KitVisualizer"
    """Visualizer implementation class."""

    visualizer_type: str = "kit"
    """Type identifier for Kit visualizer."""

    viewport_name: str | None = None
    """Name for a new viewport window when :attr:`create_viewport` is ``True``.

    If ``None``, a default name (``"Visualizer Viewport"``) is used.
    """

    create_viewport: bool = False
    """If ``True``, create a new viewport window; if ``False``, use the active viewport window."""

    headless: bool = False
    """Run without creating viewport windows when supported by the app."""

    dock_position: str = "SAME"
    """Dock position for the streaming image panel and any new viewport window.

    Options: ``'LEFT'``, ``'RIGHT'``, ``'BOTTOM'``, ``'SAME'``.

    .. note::
        ``'SAME'`` (the default) places the streaming panel as a hidden tab in the
        same dock group as the main Viewport — you must click the panel's tab to see it.
        Use ``'RIGHT'`` to keep both the Viewport and the streaming panel visible
        side-by-side.
    """

    window_width: int = 1280
    """Viewport width in pixels (when :attr:`create_viewport` is ``True``)."""

    window_height: int = 720
    """Viewport height in pixels (when :attr:`create_viewport` is ``True``)."""

    origin_type: str = "world"
    """Frame in which :attr:`~isaaclab.visualizers.VisualizerCfg.eye` and
    :attr:`~isaaclab.visualizers.VisualizerCfg.lookat` are interpreted.

    Options:

    * ``"world"``: global origin.
    * ``"env"``: origin of the environment at :attr:`origin_env_index`.
    * ``"asset"``: a scene asset (or body) specified by :attr:`origin_track_path`.
    """

    origin_env_index: int = 0
    """Index of the environment used as the viewport camera origin.

    Only meaningful when :attr:`origin_type` is ``"env"`` or ``"asset"``.
    """

    origin_track_path: str | None = None
    """Asset tracking path for the viewport camera origin.

    Format: ``"<asset_name>"`` to track the asset root, or ``"<asset_name>/<body_name>"``
    to track a specific body on the asset.  Required when :attr:`origin_type` is ``"asset"``.

    Examples::

        origin_track_path = "robot"             # track robot root
        origin_track_path = "robot/panda_hand"  # track panda_hand body on robot
    """
VisualizerCfg source (shared base class)
@configclass
class VisualizerCfg:
    """Base configuration for all visualizer backends.

    Note:
        This is an abstract base class and should not be instantiated directly.
        Use specific configs from isaaclab_visualizers: KitVisualizerCfg, NewtonGLVisualizerCfg,
        RerunVisualizerCfg, or ViserVisualizerCfg (from isaaclab_visualizers.kit/.newton/.rerun/.viser).
    """

    class_type: type[BaseVisualizer] | str | None = None
    """Visualizer implementation class. Concrete configs must set this field."""

    # Primary interactive camera settings
    eye: tuple[float, float, float] = (4.0, -4.0, 3.0)
    """Interactive visualizer camera eye position in world coordinates."""

    lookat: tuple[float, float, float] = (0.0, 0.0, 0.0)
    """Interactive visualizer camera look-at target in world coordinates."""

    focal_length: float = 12.0
    """Camera focal length in millimeters for visualizer camera views."""

    # ── Streaming view ────────────────────────────────────────────────────────
    # Captures pixels from a camera sensor (existing or auto-created), tiles them
    # across envs and GT types, and shows the result as an image panel in interactive
    # visualizers (Newton GL, Kit) or pushes it per-step to sink-based ones (Rerun, Viser).

    streaming_view: bool = False
    """Enable the streaming camera image view (opt-in, disabled by default)."""

    # Source — existing sensor (takes priority when set)
    streaming_sensor_prim_path: str | None = None
    """Prim path of an existing TiledCamera sensor to stream from.

    When set, all ``streaming_cam_*`` fields are ignored.  Should point to an
    existing camera sensor, e.g. ``"/World/envs/*/Camera"``.
    """

    # Source — auto-created camera (used when streaming_sensor_prim_path is None)
    streaming_cam_target_prim_path: str | None = None
    """Target prim for the auto-created streaming camera (ignored when
    :attr:`streaming_sensor_prim_path` is set).

    When ``None`` (the default), the visualizer adopts the first scene camera
    sensor it discovers dynamically at initialization time.  If no scene camera
    exists the streaming panel remains empty.  Set this explicitly (e.g.
    ``"/World/envs/*/Robot"``) only when you need an auto-created follow-camera
    and no suitable scene camera is present.
    """

    streaming_cam_eye: tuple[float, float, float] = (4.0, -4.0, 3.0)
    """Eye offset [m] for the auto-created streaming camera relative to the target prim."""

    streaming_cam_renderer: str | None = None
    """Renderer for the auto-created streaming camera.

    One of ``"newton_warp"``, ``"ovrtx"``, or ``None`` (let each backend
    choose its own default).  Defaults to ``None`` so each backend selects
    an appropriate renderer automatically.  Ignored when
    :attr:`streaming_sensor_prim_path` is set.
    """

    # Shared settings
    streaming_envs: int | list[int] = 32
    """Environments to capture.

    * ``int`` — sample this many envs once at initialization (from all visible envs).
    * ``list[int]`` — capture exactly these env indices.
    """

    streaming_gt_types: tuple[str, ...] = ("rgb",)
    """GT data types displayed left-to-right per environment row.

    Valid values: ``"rgb"``, ``"depth"``, ``"segmentation"``, ``"normals"``.
    Validated against :data:`~isaaclab.envs.utils.camera_colorizer.SUPPORTED_GT_TYPES`
    at initialization time (only when :attr:`streaming_view` is ``True``).
    """

    streaming_depth_min: float = 0.1
    """Near-clip for the turbo depth colormap [m].  Used when ``"depth"`` is in
    :attr:`streaming_gt_types`."""

    streaming_depth_max: float = 10.0
    """Far-clip for the turbo depth colormap [m].  Used when ``"depth"`` is in
    :attr:`streaming_gt_types`."""

    # Partial visualization settings
    max_visible_envs: int | None = None
    """Upper bound on how many envs are shown.

    * If visible_env_indices is not None, then this field will apply also
      to the explicit env indices set to the visible_env_indices.
    """

    visible_env_indices: list[int] | None = None
    """env indices to visualize in order (out-of-range indices are dropped)."""

    randomly_sample_visible_envs: bool = True
    """If ``max_visible_envs`` is provided, when enabled, selected visible envs are randomly sampled.
       If disabled, the first ``max_visible_envs`` envs are selected.

    * Note: ``visible_env_indices`` overrides this field.
    """

    # Visualization Markers
    enable_markers: bool = True
    """Enable visualization markers (debug drawing)."""

    # Live Plots
    enable_live_plots: bool = True
    """Stream per-step scalar data (manager terms, episode reward, episode length) into the visualizer.

    Plot windows start hidden or collapsed by default and can be toggled open at runtime.
    Set to ``False`` to disable live plots entirely and avoid any collection overhead.
    """

    live_plots_update_interval: int = 5
    """Collect and push live plot data every ``N`` simulation steps (default: every 5 steps)."""

    # Internal
    visualizer_type: str | None = None
    """Type identifier (e.g., 'newton', 'rerun', 'viser', 'kit'). Must be overridden by subclasses."""

    # Deprecated aliases kept for one-release compatibility. Remove in the next major release.
    tiled_cam_view: bool | None = None
    """Deprecated. Use :attr:`streaming_view` instead."""

    tiled_cam_num: int | None = None
    """Deprecated. Use :attr:`streaming_envs` (int) instead."""

    tiled_cam_env_indices: list[int] | None = None
    """Deprecated. Use :attr:`streaming_envs` (list[int]) instead."""

    tiled_cam_prim_path: str | None = None
    """Deprecated. Use :attr:`streaming_sensor_prim_path` instead."""

    tiled_cam_eye: tuple[float, float, float] | None = None
    """Deprecated. Use :attr:`streaming_cam_eye` instead."""

    tiled_cam_target_prim_path: str | None = None
    """Deprecated. Use :attr:`streaming_cam_target_prim_path` instead."""

    tiled_cam_renderer: str | None = None
    """Deprecated. Use :attr:`streaming_cam_renderer` instead."""

    def __post_init__(self) -> None:
        import warnings

        _simple = [
            ("tiled_cam_view", "streaming_view"),
            ("tiled_cam_prim_path", "streaming_sensor_prim_path"),
            ("tiled_cam_eye", "streaming_cam_eye"),
            ("tiled_cam_target_prim_path", "streaming_cam_target_prim_path"),
            ("tiled_cam_renderer", "streaming_cam_renderer"),
        ]
        for old, new in _simple:
            val = getattr(self, old)
            if val is not None:
                warnings.warn(f"{old!r} is deprecated; use {new!r} instead.", DeprecationWarning, stacklevel=3)
                setattr(self, new, val)
                setattr(self, old, None)
        # tiled_cam_env_indices takes priority over tiled_cam_num
        env_indices = getattr(self, "tiled_cam_env_indices")
        if env_indices is not None:
            warnings.warn(
                "'tiled_cam_env_indices' is deprecated; use 'streaming_envs' instead.",
                DeprecationWarning,
                stacklevel=3,
            )
            self.streaming_envs = env_indices
            self.tiled_cam_env_indices = None
            self.tiled_cam_num = None
        else:
            num = getattr(self, "tiled_cam_num")
            if num is not None:
                warnings.warn(
                    "'tiled_cam_num' is deprecated; use 'streaming_envs' instead.",
                    DeprecationWarning,
                    stacklevel=3,
                )
                self.streaming_envs = num
                self.tiled_cam_num = None

Shared Features#

Streaming Camera View#

The streaming camera view composites per-environment sensor data into a tiled panel that updates every step.

Interactive view of the Galbot cube stacking environment

Streaming view showing tiled Galbot wrist-camera feeds

The streaming panel supports RGB, depth, segmentation, and surface normals, with a configurable number of environments shown.

Streams can come from auto-created cameras that track and follow robot bodies, or from existing scene camera sensors, letting you toggle between different views, such as the Galbot task’s wrist-mounted and ego cameras. Supported on Kit, Newton GL, Rerun, and Viser; not yet supported on Newton RTX (experimental).

See Visualizer Streaming Camera View for the full guide and tutorial.

Visualization Markers#

Visualization markers draw debug geometry over the scene via VisualizationMarkers.

Velocity arrow marker on an AnymalD robot

Large green/blue arrow markers showing target and base velocity for an AnymalD robot

Joint arrow markers on a Franka arm and contact sensor markers on a cube

Arrow markers on the Franka arm's joints, with contact sensor markers on the cube

See Creating Visualization Markers for creating and configuring custom markers.

There are 2 types of visualization markers:

Built-in marker types:

  • Sensor markers: contact hit/no-contact spheres, ray-caster hit points, deformable-object kinematic targets, visuo-tactile contact points

  • Frame markers: coordinate frame with a connecting line

  • Colored arrows (red, blue, green) for direction and velocity indicators

  • Goal shapes: cuboid, sphere, and multi-state position-goal markers

  • Center of mass markers

In the Newton GL visualizer’s HUD, built-in marker types that aren’t currently active are shown greyed out.

Custom Markers:

  • Defined by individual tasks and MDP terms that instantiate their own VisualizationMarkersCfg

  • Example: the AnymalD velocity command draws its green/blue velocity arrows this way

  • Example: the Isaac-Reorient-Cube-Allegro task defines its own goal marker (a DexCube asset) to show the target cube orientation

Note

Visualization markers are supported on Kit, Newton GL, Rerun, and Viser; not yet supported on Newton RTX (experimental).

Live Plots#

Live plots stream per-step scalar data into the visualizer.

Newton GL visualizer, with 3 plots opened

Viser visualizer, with 1 plot opened

Live Plot windows start hidden or collapsed, except for Rerun, where the Live Plots can be collapsed by selecting the “eye” icon. Supported on Kit, Newton GL, Rerun, and Viser; not yet supported on Newton RTX (experimental).

What’s plotted: per-episode total reward and episode length, plus any active manager term for manager-based environments (e.g. joint_pos).

Video Recording#

Video Recording supports capturing feed from any visualizer view, both interactive and streaming views, and renderer-based sensor data capture.

Kit visualizer tiled streaming

Recorded clip with ground-truth sensor views

Pass --video on the command line for a quick recording from the default visualizer, or define multiple VideoRecorderCfg entries to record multiple sources at once. Kit, Newton GL, and Newton RTX visualizers can be recorded while in headless mode, to reduce overhead or in case no display is available.

Not currently supported by the web-based visualizers Viser and Rerun; add a headless visualizer as a capture source alongside them to record video.

See Recording Video for the full guide and tutorial.

Usage#

Common Recipes#

Headless training with video recording

Run without a window and record clips from a Newton GL or Kit visualizer kept alive as the capture source:

uv run isaaclab train --rl_library rsl_rl --task Isaac-Cartpole \
    --viz newton_gl --headless --video
./isaaclab.sh train --rl_library rsl_rl --task Isaac-Cartpole \
    --viz newton_gl --headless --video

See Recording Video for clip length, interval, and multi-source options.

Combining an interactive view with a headless recording source

Watch training live in Kit while recording from a separate headless Newton GL angle:

from isaaclab_visualizers.kit import KitVisualizerCfg
from isaaclab_visualizers.newton import NewtonGLVisualizerCfg

sim_cfg.visualizer_cfgs = [
    KitVisualizerCfg(eye=(4.0, 4.0, 2.0)),
    NewtonGLVisualizerCfg(eye=(12.0, 0.0, 6.0), headless=True),
]

See the “Recording from an independent camera angle” section of Recording Video for the full example.

Following a moving robot (Kit)

Lock the Kit camera to a moving asset instead of updating eye/lookat yourself every step:

from isaaclab_visualizers.kit import KitVisualizerCfg

sim_cfg.visualizer_cfgs = [
    KitVisualizerCfg(
        origin_type="asset",
        origin_track_path="robot",  # or "robot/panda_hand" to track a specific body
        eye=(4.0, 4.0, 2.0),  # offset from the tracked asset
    )
]

Sharing a live view with a remote teammate

Viser can request a public share URL for the running session, useful for remote pairing without screen-sharing:

from isaaclab_visualizers.viser import ViserVisualizerCfg

sim_cfg.visualizer_cfgs = [ViserVisualizerCfg(share=True, open_browser=True)]

The share URL is logged on startup. Rerun has no equivalent config field, but exposes its own share button in the native UI.

Resolution Rules#

Visualizers are resolved from --viz (comma-separated, e.g. --viz kit,newton_gl) or SimulationCfg.visualizer_cfgs in code. If --viz is omitted, the config value is used; --viz none always disables all visualizers, regardless of config.

Add --headless alongside --viz kit or --viz newton_gl to keep that visualizer running without an on-screen window, e.g. as a --video recording source on a machine without a display.

To configure visualizer settings in code, pass VisualizerCfg instances to SimulationCfg:

from isaaclab.sim import SimulationCfg
from isaaclab_visualizers.kit import KitVisualizerCfg
from isaaclab_visualizers.newton import NewtonGLVisualizerCfg

sim_cfg = SimulationCfg(
    visualizer_cfgs=[
        KitVisualizerCfg(eye=(0.0, 0.0, 20.0)),
        NewtonGLVisualizerCfg(eye=(5.0, 5.0, 5.0), show_joints=True),
    ]
)
Common modes#

CLI args

visualizer_cfgs

Effective behavior

no --viz

[]

No visualizer launches; no window, no capture source.

--viz kit,newton_gl

[]

Launch default Kit and Newton GL visualizers.

--viz newton_gl --headless

[]

Launch Newton GL without a window, e.g. as a --video recording source.

--viz kit,newton_gl

[NewtonGLVisualizerCfg(...), RerunVisualizerCfg(...)]

Launch default Kit and custom Newton GL; Rerun is not launched.

no --viz

[NewtonGLVisualizerCfg(...), RerunVisualizerCfg(...)]

Launch custom Newton GL and Rerun from config.

--viz none

[NewtonGLVisualizerCfg(...), RerunVisualizerCfg(...)]

All visualizers disabled; no window, no capture source.

For migration context, see Migrating To 3.0.

Performance#

Visualizer

Tips

Newton GL

Lowest overhead; increase update_frequency to skip render calls.

Viser

Newton Warp renderer; use max_visible_envs to limit the number of rendered environments.

Newton RTX

Path-traced; highest per-frame cost, use --max_visible_envs to reduce load.

Rerun

Web viewer may slow down with many environments; use --num_envs to reduce load.

Kit

Highest overhead of the five visualizers; reduce window_width / window_height or use --max_visible_envs.

Limitations#

Backend feature support:

Feature

Newton GL

Newton RTX

Viser

Rerun

Kit

Visualization markers

Live plots

Streaming camera panel

Video recording, --video

Pause rendering

Headless mode

Lighting differences across visualizers

Each backend lights the scene differently, so the same environment can look noticeably different across visualizers. Kit renders the scene’s actual authored USD lights. Newton GL uses a fixed sky-gradient and single directional light color (sky_upper_color, sky_lower_color, light_color), independent of scene USD lights. Newton RTX supports only 3 lighting-environment presets (rtx_environment: "default", "studio", "none") and does not use any scene-authored USD lights. Viser uses a single ambient light with no directional key light, so scenes tend to look darker and flatter than the other backends. Rerun uses fixed built-in viewer shading with no scene-driven lighting.

Kit: incompatible with ovphysx / ovrtx presets

--viz kit cannot be used with presets=ovphysx or presets=ovrtx in the same process. Use --viz newton_gl, --viz rerun, or --viz viser with those presets, or omit --viz for headless execution.

Newton RTX: incompatible with Kit

--viz newton_rtx raises a RuntimeError at startup if the active physics backend is isaacsim_physx (i.e. presets=isaacsim_physx), since OVRTX is a kitless renderer and cannot share a process with Kit. presets=ovphysx is itself kitless and remains supported. Use presets=newton_mjwarp,ovrtx or presets=ovphysx,ovrtx with --viz newton_rtx, or switch to --viz newton_gl, --viz viser, --viz rerun, or --viz kit with a Kit-compatible physics backend.

Rerun: large environment performance

The Rerun web viewer may slow down or crash with many environments. Reduce load with --num_envs:

uv run isaaclab train --rl_library rsl_rl --task Isaac-Cartpole --viz rerun --num_envs 512
./isaaclab.sh train --rl_library rsl_rl --task Isaac-Cartpole --viz rerun --num_envs 512

Newton GL: CUDA/OpenGL interoperability warnings

In some configurations, the Newton GL visualizer emits warnings about CUDA/OpenGL interoperability:

Warning: Could not get MSAA config, falling back to non-AA.
Warp CUDA error 999: unknown error (in function wp_cuda_graphics_register_gl_buffer)
Warp UserWarning: Could not register GL buffer …

The visualizer still functions correctly but falls back to CPU copy operations, which reduces performance.

Newton GL: OpenGL context failures

If pyglet reports that glCreateShader is not exported or that OpenGL 2.0 is required, the process is running without a GPU-backed display context (for example, in a service session or a remote desktop without GPU acceleration). Run from a GPU-backed interactive display session, or omit --viz newton_gl for headless execution.

Newton GL: Spark + conda

Conda-installed X11 libraries may conflict with pyglet on Spark, producing:

pyglet.window.xlib.XlibException: Could not create UTF8 text property

Remove the conflicting conda packages to use the system libraries instead:

conda remove --force xorg-libx11 libxcb

See Also#