Visualization Platform
Before you can build great robotics and AI systems, you need to see what they're doing. A text log that says "robot moved to position X" tells you almost nothing. But watching a live 3D point cloud of your robot's lidar scan, overlaid with its camera feed and battery level — that tells you everything.
That's what the CredVault Visualization Platform is for.
It lets you log any kind of data — 3D scenes, images, sensor readings, GPS tracks, model metrics, text events — and stream it to a visual viewer in real time. You can also save recordings to file and replay them later, frame by frame, to debug exactly what went wrong and when.
How It Works
The core idea is simple: you instrument your code with logging calls, and the viewer shows you everything as it happens.
Your code → rr.log(...) → CredVault Viewer
You decide what to log, when to log it, and how to organize it. The viewer handles the rest — rendering 3D scenes, plotting time-series, displaying images, and letting you scrub through time.
Data is organized into entities (like robot/camera, robot/lidar, sensor/temperature) and timelines (like frame, epoch, or wall-clock time). This means you can have dozens of data streams all synchronized on the same timeline, and scrub through them together.
Installation
Python
The Python SDK is the most common way to log data. Install it with pip:
pip install credvault-rerun-sdk
Requires Python 3.10 or higher. Once installed, import it as rerun:
import rerun as rr
JavaScript / Node.js
If you're building a web application or Node.js service:
npm install @credvault/rerun-viewer
React
If you want to embed the visualization viewer directly inside a React dashboard:
npm install @credvault/rerun-viewer-react
Your First Visualization
Let's start with the simplest possible example — logging a 3D point and opening the viewer.
import rerun as rr
# Initialize with a name for your recording
rr.init("my_first_visualization")
# Open the viewer (spawns a child process)
rr.spawn()
# Log a 3D point cloud
rr.log("scene/points", rr.Points3D(positions=[[0, 0, 0], [1, 0, 0], [0, 1, 0]]))
Run this and the CredVault viewer opens automatically, showing three points in 3D space.
Now let's make it more interesting by adding a timeline so you can scrub through frames:
import rerun as rr
import numpy as np
rr.init("timeline_example")
rr.spawn()
for frame in range(100):
# Tell the viewer which frame this data belongs to
rr.set_time("frame", sequence=frame)
# Log a point that moves over time
x = np.sin(frame * 0.1)
y = np.cos(frame * 0.1)
rr.log("scene/moving_point", rr.Points3D(positions=[[x, y, 0]]))
Now you can drag the timeline slider in the viewer and watch the point move.
Understanding Entities and Paths
Every piece of data you log goes to an entity path — a slash-separated string that organizes your data like a file system.
rr.log("robot/camera/rgb", ...) # camera image
rr.log("robot/camera/depth", ...) # depth image
rr.log("robot/lidar", ...) # lidar point cloud
rr.log("robot/transform", ...) # robot position
rr.log("metrics/loss", ...) # training loss
rr.log("metrics/accuracy", ...) # training accuracy
The viewer shows these as a tree on the left side. You can expand and collapse branches, toggle visibility, and inspect individual entities. Think of it like a scene graph — everything has a place in the hierarchy.
Good entity paths make your recordings much easier to navigate. Use descriptive names and group related data under the same parent path.
Understanding Timelines
A timeline is how you associate data with a point in time. You can have multiple timelines in the same recording.
# Frame-based timeline (for robotics, video)
rr.set_time("frame", sequence=42)
# Step-based timeline (for ML training)
rr.set_time("training_step", sequence=1000)
# Wall-clock time
import time
rr.set_time("wall_time", duration=time.time())
After calling set_time, every rr.log call until the next set_time will be associated with that timestamp. This is how the viewer knows where to place your data on the timeline.
You can also have static data — data that has no time and exists across all timelines. Use this for things that don't change, like camera calibration parameters or a 3D model of your environment:
# This camera calibration is valid for the entire recording
rr.log("robot/camera", rr.Pinhole(focal_length=500, width=640, height=480), static=True)
Logging 3D Data
Point Clouds
Point clouds are the most common 3D data type in robotics. They come from lidar sensors, depth cameras, and 3D reconstruction algorithms.
import numpy as np
# Generate a simple point cloud
positions = np.random.uniform(-1, 1, (1000, 3))
colors = np.random.uniform(0, 255, (1000, 3)).astype(np.uint8)
rr.log("scene/lidar", rr.Points3D(
positions=positions,
colors=colors,
radii=0.02 # size of each point
))
3D Bounding Boxes
Use bounding boxes to show detected objects in 3D space:
rr.log("scene/detections", rr.Boxes3D(
centers=[[1.0, 0.0, 0.5], [3.0, 1.0, 0.5]],
half_sizes=[[0.5, 0.3, 0.5], [0.4, 0.4, 0.5]],
labels=["robot_arm", "conveyor_belt"],
colors=[[0, 255, 0], [255, 165, 0]]
))
Transforms and Coordinate Frames
When you have a robot with multiple joints or sensors, you need to track how each part relates to the others. Use Transform3D to define the position and orientation of each frame:
# Robot base position in the world
rr.log("robot/base", rr.Transform3D(
translation=[x, y, 0],
rotation=rr.Quaternion(xyzw=[0, 0, np.sin(yaw/2), np.cos(yaw/2)])
))
# Camera mounted on the robot (relative to base)
rr.log("robot/base/camera", rr.Transform3D(
translation=[0.2, 0, 0.5], # 20cm forward, 50cm up
rotation=rr.Quaternion(xyzw=[0, 0, 0, 1]) # no rotation
))
The viewer understands the parent-child relationship between robot/base and robot/base/camera, so the camera moves with the robot automatically.
Arrows and Vectors
Useful for showing velocity, force, or direction:
rr.log("robot/velocity", rr.Arrows3D(
origins=[[0, 0, 0]],
vectors=[[vx, vy, vz]],
colors=[[0, 200, 255]]
))
3D Meshes
Load and display 3D models of your robots or environment:
rr.log("scene/robot_model", rr.Mesh3D(
vertex_positions=vertices, # Nx3 array
triangle_indices=faces, # Mx3 array of vertex indices
vertex_colors=colors # optional per-vertex colors
))
Logging Images and Video
RGB Images
import numpy as np
# From a numpy array (H x W x 3, uint8)
image = get_camera_frame() # your function that returns a numpy array
rr.log("robot/camera/rgb", rr.Image(image))
Depth Images
Depth images store distance values per pixel. The meter parameter tells the viewer what value represents 1 meter:
depth = get_depth_frame() # H x W array of uint16 values in millimeters
rr.log("robot/camera/depth", rr.DepthImage(depth, meter=1000.0))
Segmentation Masks
Segmentation images use integer labels per pixel. You can define what each label means using an annotation context:
# Define what each class label means
rr.log("annotations", rr.AnnotationContext([
(0, "background"),
(1, "robot"),
(2, "obstacle"),
(3, "target"),
]), static=True)
# Log the segmentation mask
rr.log("robot/camera/segmentation", rr.SegmentationImage(mask_array))
Camera Calibration
If you log a Pinhole to the same entity as your image, the viewer can correctly project 3D points onto the image plane:
# Log camera intrinsics once (static)
rr.log("robot/camera", rr.Pinhole(
focal_length=[fx, fy],
principal_point=[cx, cy],
width=640,
height=480
), static=True)
# Log images on the same entity
rr.log("robot/camera", rr.Image(frame))
Logging Scalar Metrics
Scalars are single numeric values over time. Perfect for training metrics, sensor readings, and system stats.
# Training metrics
rr.set_time("epoch", sequence=epoch)
rr.log("train/loss", rr.Scalars(loss))
rr.log("train/accuracy", rr.Scalars(accuracy))
rr.log("train/learning_rate", rr.Scalars(lr))
# Robot sensors
rr.set_time("frame", sequence=frame)
rr.log("robot/battery", rr.Scalars(battery_pct))
rr.log("robot/speed", rr.Scalars(speed_ms))
rr.log("robot/temperature", rr.Scalars(motor_temp))
The viewer plots these as line charts automatically. You can view multiple scalars on the same chart by selecting them together.
Logging Text and Events
Text Logs
Use text logs to record events, warnings, and errors alongside your data:
rr.log("logs/system", rr.TextLog("Connected to cluster", level=rr.TextLogLevel.INFO))
rr.log("logs/system", rr.TextLog("High memory usage: 87%", level=rr.TextLogLevel.WARN))
rr.log("logs/system", rr.TextLog("Motor fault on joint 3", level=rr.TextLogLevel.ERROR))
rr.log("logs/robot", rr.TextLog("Reached waypoint 5"))
Text logs appear in the viewer's log panel and are synchronized with the timeline, so you can see exactly what was happening when an event occurred.
Rich Text Documents
For longer content like reports or markdown:
rr.log("docs/summary", rr.TextDocument(
"## Run Summary\n\nAll systems nominal.\n\n- Battery: 87%\n- Speed: 1.2 m/s",
media_type="text/markdown"
))
Logging GPS and Geographic Data
If your robots operate outdoors, you can log GPS coordinates and see them on a map in the viewer.
# A single GPS position
rr.log("robot/gps", rr.GeoPoints(
lat_lon=[[-1.286389, 36.817223]], # latitude, longitude
radii=10.0
))
# A GPS track (path the robot has taken)
rr.log("robot/path", rr.GeoLineStrings(
lat_lon=[[
[-1.286, 36.817],
[-1.290, 36.820],
[-1.295, 36.825],
[-1.300, 36.830],
]]
))
Logging Graphs and Networks
Useful for visualizing pipeline dependencies, neural network architectures, or any graph-structured data:
rr.log("pipeline/graph", rr.GraphNodes(
node_ids=["input", "conv1", "relu1", "conv2", "relu2", "output"],
labels=["Input", "Conv 3x3", "ReLU", "Conv 3x3", "ReLU", "Output"]
))
rr.log("pipeline/graph", rr.GraphEdges(
edges=[
("input", "conv1"),
("conv1", "relu1"),
("relu1", "conv2"),
("conv2", "relu2"),
("relu2", "output"),
]
))
Embedding the Viewer in React
If you want to embed the visualization viewer directly in your CredVault dashboard or web app, use the React component:
import { WebViewer } from "@credvault/rerun-viewer-react"
export default function RobotDashboard() {
return (
<div style={{ width: "100%", height: "600px" }}>
<WebViewer
width="100%"
height="600px"
rrd="https://credvault-production.up.railway.app/api/rerun/recording/abc123"
/>
</div>
)
}
The rrd prop accepts a URL to a .rrd recording file. You can generate these from Python using rr.save("recording.rrd") and then serve them from your backend.
Complete Example: Robot Telemetry
Here's a complete example that logs everything you'd want from a mobile robot:
import rerun as rr
import numpy as np
import time
rr.init("robot_telemetry")
rr.spawn()
# Log static camera calibration once
rr.log("robot/camera", rr.Pinhole(
focal_length=500, width=640, height=480
), static=True)
frame = 0
while True:
rr.set_time("frame", sequence=frame)
# Robot position and orientation
x = frame * 0.01
y = np.sin(frame * 0.05) * 2
yaw = frame * 0.02
rr.log("robot/transform", rr.Transform3D(
translation=[x, y, 0],
rotation=rr.Quaternion(xyzw=[0, 0, np.sin(yaw/2), np.cos(yaw/2)])
))
# Camera image
rr.log("robot/camera", rr.Image(get_camera_frame()))
# Lidar scan
lidar_points = get_lidar_scan()
rr.log("robot/lidar", rr.Points3D(positions=lidar_points, radii=0.02))
# Sensor readings
rr.log("robot/battery", rr.Scalars(get_battery_level()))
rr.log("robot/speed", rr.Scalars(get_speed()))
rr.log("robot/motor_temp", rr.Scalars(get_motor_temperature()))
# Status log
rr.log("robot/logs", rr.TextLog(f"Frame {frame}: position ({x:.2f}, {y:.2f})"))
frame += 1
time.sleep(0.05) # 20 fps
Complete Example: ML Training
Log your training run and watch metrics in real time:
import rerun as rr
rr.init("training_run_001")
rr.spawn()
model = build_model()
optimizer = build_optimizer()
for epoch in range(200):
train_loss, train_acc = train_one_epoch(model, optimizer)
val_loss, val_acc = validate(model)
rr.set_time("epoch", sequence=epoch)
# Training metrics
rr.log("metrics/train/loss", rr.Scalars(train_loss))
rr.log("metrics/train/accuracy", rr.Scalars(train_acc))
# Validation metrics
rr.log("metrics/val/loss", rr.Scalars(val_loss))
rr.log("metrics/val/accuracy", rr.Scalars(val_acc))
# Log a sample prediction every 10 epochs
if epoch % 10 == 0:
sample_input, sample_pred = get_sample_prediction(model)
rr.log("predictions/input", rr.Image(sample_input))
rr.log("predictions/output", rr.Image(sample_pred))
rr.log("training/events", rr.TextLog(f"Epoch {epoch}: val_loss={val_loss:.4f}"))
Saving and Replaying Recordings
Instead of streaming to a live viewer, you can save everything to a .rrd file and replay it later:
rr.init("my_recording")
rr.save("robot_run_2026_05_11.rrd") # all logs go to this file
# ... your logging code ...
To replay it:
rerun robot_run_2026_05_11.rrd
Or load it in the React viewer:
<WebViewer rrd="/recordings/robot_run_2026_05_11.rrd" width="100%" height="600px" />
Using in Jupyter Notebooks
The viewer embeds directly in Jupyter notebooks — no separate window needed:
import rerun as rr
rr.init("notebook_demo")
rr.notebook_show(width=900, height=500)
# Everything you log appears inline in the notebook
rr.log("points", rr.Points3D([[1, 2, 3], [4, 5, 6], [7, 8, 9]]))
rr.log("metrics/value", rr.Scalars(42.0))
Output Options Summary
| What you want | How |
|---|---|
| Open viewer on your machine | rr.spawn() |
| Save to file for later | rr.save("recording.rrd") |
| Stream to a remote viewer | rr.connect_grpc("ip:port") |
| Embed in a web app | @credvault/rerun-viewer-react |
| Use in a Jupyter notebook | rr.notebook_show() |
| Get raw bytes (for custom transport) | rr.memory_recording() |
Support
Questions or issues with the visualization platform? File a support ticket from your dashboard or email credvault.net/support.