ROS2 PointCloud2 web viewer — a C++ node that serves a real-time 3D point cloud visualization in the browser using Boost.Beast (HTTP/WebSocket) and WebGL.
The pointcloud_web_viewer node subscribes to a sensor_msgs/msg/PointCloud2
topic, converts the point cloud into a compact binary format, and streams it to
connected browsers via WebSocket. The board itself serves both the web page and
the binary stream — no external server or dependencies required on the client
side beyond a browser with WebGL support.
- C++ server: Boost.Beast HTTP + WebSocket
- Frontend: Single self-contained HTML file, raw WebGL 1.0 (no Three.js)
- Binary protocol: 24-byte header + 20-byte-per-point structs
- Thread-safe: Mutex-protected shared frame between ROS2 callback and WebSocket broadcast thread
| Dependency | Purpose |
|---|---|
| ROS2 Humble (or compatible) | Core framework |
rclcpp |
ROS2 C++ client library |
sensor_msgs |
PointCloud2 message type |
| Boost (system) | Asio networking |
| Boost.Beast (header-only) | HTTP/WebSocket server |
| C++17 compiler | Build |
| Modern browser with WebGL | Client |
cd ~/ros2_ws
colcon build --packages-select pointcloud_web_viewer
source install/setup.bashbash script/run_pointcloud_web_viewer.shThe script (script/run_pointcloud_web_viewer.sh) sources the TROS Humble
setup and launches the node with:
ros2 launch pointcloud_web_viewer \
pointcloud_web_viewer.launch.py \
pointcloud_topic:=/StereoNetNode/stereonet_pointcloud2 \
server_port:=9999 \
send_fps:=10.0 \
downsample_step:=1 \
max_points:=100000Open http://<board-ip>:<port> (e.g. http://192.168.1.100:8080) in a browser.
All parameters can be set via the YAML config file (config/viewer.yaml),
launch arguments, or command-line overrides.
| Parameter | Type | Default | Description |
|---|---|---|---|
pointcloud_topic |
string | /stereonet_pointcloud |
PointCloud2 topic to subscribe to |
server_host |
string | 0.0.0.0 |
HTTP/WebSocket bind address |
server_port |
int | 8080 |
HTTP/WebSocket listen port (1–65535) |
send_fps |
double | 10.0 |
Maximum frame rate for WebSocket streaming (min 0.1) |
downsample_step |
int | 2 |
Process every Nth point — higher values reduce CPU load (min 1) |
max_points |
int | 100000 |
Maximum number of points per frame (min 1) |
min_distance |
double | 0.1 |
Minimum distance filter in meters (min 0.0) |
max_distance |
double | 30.0 |
Maximum distance filter in meters (≥ min_distance) |
intensity_field |
string | intensity |
Name of the intensity field in the PointCloud2 message |
qos_depth |
int | 1 |
QoS keep_last depth for the subscription (min 1) |
The default config file config/viewer.yaml provides preset values:
pointcloud_web_viewer:
ros__parameters:
pointcloud_topic: /StereoNetNode/stereonet_pointcloud2
server_host: 0.0.0.0
server_port: 8080
send_fps: 10.0
downsample_step: 2
max_points: 100000
min_distance: 0.1
max_distance: 30.0
intensity_field: intensity
qos_depth: 1| Argument | Default | Description |
|---|---|---|
config_file |
<share>/config/viewer.yaml |
Path to YAML parameter file |
pointcloud_topic |
/StereoNetNode/stereonet_pointcloud2 |
PointCloud2 topic |
server_port |
8080 |
HTTP/WebSocket port |
Note: The launch file exposes only
pointcloud_topicandserver_portas CLI overrides. To change other parameters, editconfig/viewer.yamlor pass them via the launch file'sparametersdict.
The web viewer provides a real-time 3D point cloud display with the following features:
| Control | Action |
|---|---|
| Left-drag | Rotate (orbit) |
| Right-drag or Shift+Left-drag | Pan |
| Scroll | Zoom in/out |
- Connection status: Green/red indicator showing WebSocket connection state
- Point count: Number of points in the current frame
- Receive FPS: Actual frame rate from the WebSocket stream
- RGB status: Whether color data is available in the point cloud
- Point size: Slider (1–10) to adjust rendered point size
- Rotation sliders: Roll, Pitch, Yaw (−180° to 180°)
- Reset View Rotation: Resets rotation to default orientation
- Color mode: Select from:
- RGB — Native point cloud colors
- Forward Distance (X) — Heatmap based on X-axis distance
- Height (Z) — Heatmap based on Z-axis elevation
- Intensity — Heatmap based on the intensity field
- White — Solid white rendering
- XY Grid toggle: Reference grid at Z=0 with 1 m step, 5 m thick lines, color-coded ROS axes (red = X, green = Y)
- Legend: ROS coordinate axes reference and mouse control hints
ROS coordinates are mapped to the WebGL view as follows:
- ROS X (forward) → WebGL −Z
- ROS Y (left) → WebGL −X
- ROS Z (up) → WebGL Y
This ensures Y is always up in the browser view.
| Field | Required | Description |
|---|---|---|
x, y, z |
Yes | Point coordinates (any numeric type) |
intensity |
No | Configurable field name; any numeric type |
rgb / rgba |
No | Packed color as FLOAT32 or UINT32 |
r, g, b |
No | Separate color channels (any numeric type, clamped to 0–255) |
Both little-endian and big-endian messages are supported.
The WebSocket stream sends binary frames with the following layout:
PointCloudFrameHeader (24 bytes)
├── magic: uint32 = 0x31444350 ("PCD1")
├── version: uint16 = 2
├── point_format: uint16 = 1 (XYZI) or 2 (XYZI+RGB)
├── point_count: uint32
├── point_stride: uint32 = 20
└── timestamp_ns: uint64 (ROS timestamp)
WebPoint[] (point_count × 20 bytes)
├── x, y, z: float32
├── intensity: float32 (normalized to [0, 1])
└── r, g, b: uint8 × 3 + 1 reserved byte
| Endpoint | Method | Description |
|---|---|---|
/ or /index.html |
GET | Serves the WebGL viewer page |
/health |
GET | JSON health check: {"ok":true,"frame_seq":<N>,"has_cloud":true/false} |
/ws |
GET | WebSocket upgrade for binary point cloud streaming |
/favicon.ico |
GET | Returns 204 No Content |
pointcloud_web_viewer/
├── CMakeLists.txt
├── package.xml
├── config/
│ └── viewer.yaml # Default parameter values
├── doc/
│ └── pointcloud_web_viewer.gif # Demo animation
├── include/pointcloud_web_viewer/
│ ├── pointcloud_web_viewer.hpp # ROS2 node header
│ ├── protocol.hpp # Binary protocol + SharedFrame
│ └── web_server.hpp # HTTP/WebSocket server header
├── launch/
│ └── pointcloud_web_viewer.launch.py
├── script/
│ └── run_pointcloud_web_viewer.sh # Convenience startup script
├── src/
│ ├── main.cpp # Entry point
│ ├── pointcloud_web_viewer.cpp # ROS2 node implementation
│ └── web_server.cpp # Boost.Beast server implementation
└── web/
└── index.html # WebGL viewer frontend
