A Rust-based ROS2 visualizer built on wgpu + egui. RViz alternative.
Each tagged release publishes Ubuntu 22.04 / ROS Humble and Ubuntu 24.04 / ROS Jazzy artifacts on the Releases page — both as a tarball and as a .deb.
The .deb declares all runtime dependencies, including the ROS message packages and Vulkan loader, so apt pulls them in for you. Pick the file that matches your distro:
| OS | Asset |
|---|---|
| Ubuntu 22.04 / Humble | fastviz-humble_<version>-1_amd64.deb |
| Ubuntu 24.04 / Jazzy | fastviz-jazzy_<version>-1_amd64.deb |
curl -LO "https://github.com/OWNER/REPO/releases/latest/download/fastviz-jazzy_<version>-1_amd64.deb"
sudo apt install ./fastviz-jazzy_<version>-1_amd64.deb
fastviz --config /usr/share/fastviz/configs/default.tomlThe installed binary has the ROS lib directory baked into its rpath, so you do not need to source /opt/ros/<distro>/setup.bash before launching it.
tag=$(curl -s https://api.github.com/repos/OWNER/REPO/releases/latest | grep -oE '"tag_name": *"[^"]+"' | cut -d'"' -f4)
# pick `noble-jazzy` or `jammy-humble`
asset="app-${tag}-x86_64-unknown-linux-gnu-noble-jazzy.tar.gz"
curl -L -o fastviz.tar.gz "https://github.com/OWNER/REPO/releases/download/${tag}/${asset}"
tar -xzf fastviz.tar.gz
sudo install -m 0755 "${asset%.tar.gz}" /usr/local/bin/fastvizReplace OWNER/REPO with the repo slug (or just download the asset from the Releases page in a browser).
The release binary is dynamically linked. On the host you'll need:
- ROS2 Humble (Ubuntu 22.04) or ROS2 Jazzy (Ubuntu 24.04) — pick the artifact that matches. Other distros are untested.
- ROS2 message packages used by the subscribers (substitute
humbleforjazzyon 22.04):sudo apt-get install -y \ ros-jazzy-tf2-msgs ros-jazzy-nav-msgs ros-jazzy-sensor-msgs \ ros-jazzy-geometry-msgs ros-jazzy-visualization-msgs
- Vulkan loader + an ICD for
wgpu:On NVIDIA hosts, the proprietary driver provides its own ICD — installsudo apt-get install -y libvulkan1 vulkan-tools mesa-vulkan-drivers
nvidia-driver-*from the Ubuntu archive (or the upstream.runinstaller). Verify withvulkaninfo --summary. - Windowing/input libraries (usually already present on a desktop install):
libxkbcommon0,libwayland-client0,libxcb1.
sudo apt-get install -y build-essential pkg-config clang libclang-dev \
libx11-dev libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev \
libxkbcommon-dev libwayland-dev libssl-dev \
libvulkan1 vulkan-tools mesa-vulkan-drivers
export LIBCLANG_PATH=/usr/lib/x86_64-linux-gnu # r2r's bindgen step needs this
source /opt/ros/jazzy/setup.bash
cargo build --releaseThe binary lands at target/release/app. Toolchain version is pinned in rust-toolchain.toml; rustup picks it up automatically.
If your host doesn't have ROS2 Jazzy (e.g. you're on Fedora, Arch, or a non-24.04 Ubuntu), use the bundled dev container — it ships Ubuntu 24.04 + Jazzy + the r2r build deps + the Vulkan loader, all pre-installed.
# VS Code / Cursor:
# 1. Install the "Dev Containers" extension.
# 2. Open the repo folder, then "Reopen in Container".NVIDIA GPU passthrough requires nvidia-container-toolkit on the host. The container ships an NVIDIA Vulkan ICD manifest that activates automatically when the toolkit bind-mounts the driver libs; on non-NVIDIA hosts it falls back to Mesa.
See .devcontainer/Dockerfile for the exact image — it's also a good reference if you want to build your own container.
The .deb installs a desktop entry + icon system-wide, so a packaged install shows the fastviz icon automatically. A dev build (cargo run, including from the dev container) does not — and on Wayland the icon is resolved by the host compositor from a matching .desktop file, so installing it inside the container has no effect. Run this once on the host to get the same icon:
./icons/install-desktop.shIt drops the same fastviz.desktop + icon into ~/.local/share, matching the window's app_id (fastviz). On an X11/XWayland session the icon already works without this, since it's sent over the wire by the app itself.
Source ROS2 first, then launch with a config file:
source /opt/ros/jazzy/setup.bash
fastviz --config configs/default.tomlCommon invocations:
# TurtleBot 4 Gazebo sim — picks the URDF up off /robot_description
fastviz --config configs/turtlebot4.toml
# Override the reference frame from the CLI (wins over the config file)
fastviz --config configs/default.toml --ref-frame odom
# Load a URDF from a file directly (skips /robot_description)
fastviz --config configs/default.toml --urdf /path/to/robot.urdf| Topic kind | Message | Scene primitive |
|---|---|---|
[map] |
nav_msgs/OccupancyGrid |
Grid |
[poses] |
geometry_msgs/PoseStamped |
Arrow |
[pose_arrays] |
geometry_msgs/PoseArray |
Arrows |
[paths] |
nav_msgs/Path |
Polyline |
[scans] |
sensor_msgs/LaserScan |
Points |
[points] |
sensor_msgs/PointCloud2 |
Points |
[markers] |
visualization_msgs/Marker |
mixed primitives |
[marker_arrays] |
visualization_msgs/MarkerArray |
mixed primitives |
[tf] (/tf, /tf_static) |
tf2_msgs/TFMessage |
TF tree |
[urdf] (/robot_description) |
std_msgs/String + JointState |
meshes + FK |
Mesh files referenced from a URDF can be .stl, .obj, or .dae (Collada). package:// URIs are resolved through AMENT_PREFIX_PATH.
- Left mouse drag — orbit
- Right mouse drag — pan
- Scroll — zoom
F— reset viewT— top-down viewS— side viewEsc— quit
fastviz [--ref-frame FRAME] [--config PATH] [--urdf PATH] [--width N] [--height N]
--ref-frame and --urdf win over the matching config-file values when both are set.
configs/default.toml mirrors RosConfig::default() and is the source of truth. Other presets live alongside it (e.g. configs/turtlebot4.toml). Key features:
- Each per-message kind (
[map],[poses],[pose_arrays],[paths],[scans],[points],[markers],[marker_arrays]) takes atopics = [...]list. - A bare
"*"element enables polled discovery: anything in the ROS graph with the matching message type is auto-subscribed within ~1 s. Works for the per-message kinds above (not[map], which is single-topic). - Per-topic QoS overrides via
[<kind>.qos."<topic>"]:reliability,durability,depth. - Visual style per kind (
arrow,paths.style,scans.style,points.style). [tf]block lets you remap/tf//tf_statictopic names + override their QoS — handy for robots that publish under a namespace.[urdf]block: set eitherpath(URDF/xacro file on disk) ortopic(std_msgs/Stringtopic carrying the URDF XML, typically/robot_description).joint_states_topicdefaults to/joint_states.
Example with wildcard scans, a per-topic QoS override on /map, and a robot loaded off /robot_description:
reference_frame = "map"
[tf]
topic = "/tf"
static_topic = "/tf_static"
[urdf]
topic = "/robot_description"
joint_states_topic = "/joint_states"
[map]
topics = ["/map"]
[map.qos."/map"]
durability = "transient_local"
reliability = "reliable"
[scans]
topics = ["*"] # auto-discover every sensor_msgs/LaserScan
style = { size = 4.0, color = [1.0, 0.95, 0.20] }| crate | role |
|---|---|
app |
binary: window, event loop, egui shell, CLI |
renderer |
wgpu pipelines, render passes, camera |
scene |
scene graph, scene primitives, dirty tracking |
ros_node |
r2r executor on a dedicated thread; per-message subscribers, TF tree, URDF loader, polled topic discovery |
mock_injector |
dev-only test harness that populates the scene without ROS2 |
Milestone 0.5 is complete: TF, OccupancyGrid, PoseStamped, PoseArray, Path, LaserScan, PointCloud2, URDF + JointState with STL/OBJ/DAE meshes, visualization_msgs/Marker(Array), and TOML config with polled wildcard discovery + per-topic QoS. See MBABYSTEPS_1.md for the running plan.