Native Foxglove Visualization in LeRobot | Foxglove
Skip to main content
Download Examples
Sign in
Get started for free
The developer conference for people who build robots · Aug 18–19, SF
Get tickets
tl;dr LeRobot 0.6.0 ships native Foxglove support. Add --display_mode=foxglove to lerobot-teleoperate, lerobot-record, or lerobot-rollout to stream camera feeds and joint/action plots live over WebSocket. Replay any recorded dataset as a seekable timeline with lerobot-dataset-viz --display-mode foxglove. Connect the Foxglove app to ws://localhost:8765 and you’re done — no custom integration code required.
With PR #3902, Foxglove becomes a first-class visualization backend for LeRobot — selectable at runtime with a single flag. We are excited about this development and can’t wait to see how the community embraces simpler workflows.
Why Foxglove for LeRobot
LeRobot’s control loop produces a rich mix of data every timestep: per-motor joint positions, action targets, one or more camera images, and (optionally) depth maps. Foxglove’s multi-panel layouts let you see all of this side by side — camera feeds in Image panels, joint traces in Plot panels, all synced to the same timeline.
Three things make this integration useful for imitation learning workflows:
Live feedback during teleop and recording. Spot a dead camera, a stuck joint, or a misaligned gripper before you’ve recorded fifty episodes. The Foxglove app updates in real time as you move the leader arm.
Seekable dataset playback. Recorded episodes are served as a scrubbable timeline using Foxglove’s PlaybackControl capability. Play, pause, seek, and change speed — frames are read from the on-disk dataset on demand and stamped at their original timestamps.
Consistent topic layout across live and replay. Whether you’re streaming live or replaying a dataset, data appears on the same topics: /observation/state, /action/state, and /observation/images/. Build a layout once and reuse it everywhere.
Under the hood, LeRobot uses the Foxglove SDK (foxglove-sdk>=0.25.1) to start a WebSocket server. Scalar features are published as typed JSON messages on a static lerobot.Scalars schema — a flat {label, value} array so every joint and action dimension is automatically named in Plot panels. Images are published as RawImage (or JPEG CompressedImage when compression is enabled).
Getting started
LeRobot requires Python 3.12+. We run it using uv, but conda is also supported.
uv venv --python 3.12<br>source .venv/bin/activate<br>uv pip install 'lerobot[feetech,dataset_viz]'<br>Refer to LeRobot Installation documentation for more information.
Connect Foxglove
In the Foxglove app, select Open connection from the dashboard or left-hand menu.
Choose Foxglove WebSocket and enter ws://localhost:8765 (the default bind address).
Start any LeRobot command with --display_mode=foxglove (live) or --display-mode foxglove (dataset replay) — data appears as soon as the server starts.
Download our layout for LeRobot or create your own. We show you how to open a layout in Foxglove in this video.
Live teleoperation
The fastest way to see Foxglove in action is during teleoperation. Pass --display_data=true --display_mode=foxglove to lerobot-teleoperate and move the leader arm — camera feeds and joint plots update in the Foxglove app in real time.
uv run lerobot-teleoperate \<br>--robot.type=so101_follower \<br>--robot.port=/dev/tty.usbmodemFOLLOWER \<br>--robot.id=my_follower \<br>--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \<br>--teleop.type=so101_leader \<br>--teleop.port=/dev/tty.usbmodemLEADER \<br>--teleop.id=my_leader \<br>--display_data=true \<br>--display_mode=foxglove<br>Replace the USB port paths with your actual device paths (find them with lerobot-find-port). Each motor’s position appears as a named series on /observation/state, and the teleoperator’s targets appear on /action/state. Camera frames stream to /observation/images/front.
Recording with live visualization
When collecting demonstration data, live visualization is invaluable for catching problems early. Add the same flags to lerobot-record, and you’ll see exactly what the dataset will contain as each episode is captured.
uv run lerobot-record \<br>--robot.type=so101_follower \<br>--robot.port=/dev/tty.usbmodemFOLLOWER \<br>--robot.id=my_follower \<br>--robot.cameras="{ front: {type: opencv, index_or_path: 0, width: 640, height: 480, fps: 30}}" \<br>--teleop.type=so101_leader \<br>--teleop.port=/dev/tty.usbmodemLEADER \<br>--teleop.id=my_leader \<br>--dataset.repo_id=${HF_USER}/foxglove-demo \<br>--dataset.num_episodes=5 \<br>--dataset.single_task="Pick up the cube" \<br>--display_data=true \<br>--display_mode=foxglove<br>The live Foxglove panel gives you instant feedback on camera framing, joint ranges, and action tracking — much easier than discovering a bad episode after recording dozens of them.
Seekable dataset playback
The most powerful part of this integration is dataset replay. lerobot-dataset-viz can serve any...