The Viser 3D viewport

What the 3D viewport shows, how to drive it, and how to read the blue ghost robot.

The 3D viewport is a Viser client embedded in an iframe. The execution container runs the Viser server; the app points an iframe at it. It is where you watch a run, check a plan before you commit to it, and confirm the arm is where you think it is.

Where the viewport appears

There is no separate screen for running. Workflows run inside the Workflow Editor, with the viewport in the right-hand panel.

ScreenViser serverWhat it shows
Workflow Editorport 8080 (sim) or 8081 (real)The scene your workflow runs against
Skills Editorport 8080 or 8081The same scene, while you iterate on one skill
World Managementport 8080 or 8081The world you are building or loading
Object Databaseport 8082A single object and its anchors
Hardware Setupport 8083Hardware layout during setup

The first three share one long-lived Viser client. Moving between them repositions the same iframe instead of destroying it, because a fresh client makes the server replay the whole scene, roughly 16 seconds on a cloud sim viewport. The viewport also stays mounted when the editor window loses focus.

🖥️

If you see Live viewport not configured or Viewport unavailable instead of a scene, the app has no URL for that Viser server. Real viewport unavailable specifically means port 8081 never bound. Real hardware did not initialise on that backend, which the backend treats as non-fatal and carries on sim-only.

Simulation and real hardware are separate viewports

The dropdown above the viewport selects Simulation or Real Hardware. These are two different Viser servers backed by two different worlds: world_sim and world_real. They are not overlaid on each other, and switching between them swaps which of the two already-live clients you are looking at.

Two consequences worth knowing:

  • Switching worlds cancels a running execution. The dropdown is disabled while a workflow is running or stepping; the tooltip reads Cannot change execution world during active execution.
  • Sync is one-way. Switching from real to sim copies object poses from world_real into world_sim. Switching from sim to real copies nothing. world_real keeps its state from localization, and sim-side test changes are discarded. Real is the source of truth.

In the cloud, real hardware is unavailable and the dropdown is locked to Simulation.

The two robots in every viewport

This is the part that most often gets misread. Each viewport renders the arms twice, and both are always present in both sim and real mode.

NodeWhat it isHow it updates
/world_robotThe robot at its current measured joint statePolled at 10 Hz
/fk_previewA blue ghost at a planned or IK-solved poseOnly when something plans, previews, or solves IK

The ghost is not "the simulation" and the solid robot is not "the real arm". Both refer to the same machine in whichever mode you selected. The ghost is the future: where the arm intends to go. The solid robot is the present: where it is now.

Read the pair like this:

  • Ghost sitting on top of the live robot. Nothing planned, or the arm has finished executing its plan.
  • Ghost ahead of the live robot. Normal. That is the plan the arm is about to execute, or is midway through executing.
  • Ghost somewhere implausible. The planner or IK solver produced a pose you did not intend. Check the target before executing.

Both the left and right arm controls drive the same /fk_preview node, so the ghost shows whichever arm planned most recently.

🔵

The ghost is opaque blue. Source comments in arm_control_viz.py still describe it as "semi-transparent gray", but that comment is stale and the code sets blue at full opacity.

Previewing a trajectory

There is no trajectory path drawn in the scene: no line, spline, or ribbon geometry exists. A plan is shown by animating it on the ghost robot instead.

After planning, a Preview Trajectory button appears in the arm's control panel tab. It steps /fk_preview through every waypoint of the planned trajectory at the trajectory's own timestep, and loops until you press it again. The live robot does not move during a preview. Execute Plan is what actually runs it.

Controls

No camera bindings are configured anywhere in this product, so the viewport uses the stock camera-controls defaults. Note that middle and right are not the common convention in other 3D tools.

InputAction
Left-dragOrbit
Middle-dragDolly (zoom)
Right-dragPan
Scroll wheelZoom

Keyboard controls are also bound, and work while the viewport has focus:

KeyAction
W / SMove forward / back
A / DPan left / right
Q / EMove down / up
Arrow keysRotate the camera

Resetting the view

When a client connects, the camera is placed at [1.2, -1.2, 0.8] looking at the table centre, with +Z up. The Reset View button in Viser's control panel returns the camera to exactly that pose.

⚠️

Viser's control panel is auto-collapsed on load. Reset View, and every other control described below, is hidden until you click the panel header to expand it. Double-clicking the scene does nothing.

The control panel tabs

Expand the control panel and you get one collapsible folder per module. The three you will use most are World, Left Arm, and Right Arm, but the run viewport also registers a Camera: <name> folder per camera, plus the scene editor's own folders (Object Library, Transform Controls, Object Attachment, Waypoint Recording, Dev Tools). Every folder starts collapsed.

The World folder holds the scene-level controls:

ControlEffect
InfoLive readout, e.g. FK updating @ 10.0 Hz
Voxel Size (m)Slider, 0.005–0.05 m, default 0.01
Voxel DisplayBlox Only or Blox + Objects
Show MeshesToggles object mesh visibility
Refresh VoxelsPulls voxels from the world and redraws them
Clear VoxelsRemoves the voxel cloud
Show SpherizationToggles the collision-sphere overlay the planner uses
Export WorldWrites world state and joint poses to a timestamped export directory

Voxels are real, but not live

If the world has scanned Blox geometry, you can display it. Two things to understand about it:

  • It is drawn as a point cloud, not as cubes. One point per occupied voxel, sized to the voxel size and coloured by height.
  • It never refreshes on its own. The 10 Hz tick that updates the robot does not touch the voxels. The cloud is only rebuilt when you press Refresh Voxels, move the Voxel Size (m) slider, or change Voxel Display. What you are looking at is a snapshot from the last refresh, not the planner's current collision state.

What the viewport does not show

  • Anchor markers. Anchor frames and their text labels are only rendered by the annotation editor on port 8082, reached through the Object Database screen. You do not see anchors in the run viewport. See Adding an Object.
  • A planned path as geometry. Use Preview Trajectory, described above.
  • Skill internals. The viewport shows poses, not the reasoning behind them. For that, read the skill's log output.
  • Run history. The viewport is always the current moment. To see a past run again, run it again against the same world.

A world on disk

The scene you see comes from your project's worlds/<name>/ directory. A saved world is more than one file:

FileContentsAlways present
world_state.jsonObject list, placements, planner configYes
blox_voxels.npzESDF voxel grid used for collision queriesWhen Blox is enabled
nvblox_map.binReconstruction sidecarOn scene-scanned worlds
blox_mesh.plyTSDF reconstruction meshOnly when saved with meshes

blox_mesh.ply is optional and is absent from every world in the example projects, so do not rely on it existing.

For the field-by-field breakdown, see worlds/<id>/world_state.json.

Where to go next


Did this page help you?