Browse by type
Explore ROS1 & ROS2 bag files in your browser. No installation required.
→ Live Demo · Report Bug · Request Feature
BAGEL is a fully static web application that lets you explore ROS bag files (.mcap, .db3, .bag), standalone point cloud files (.pcd, .ply), and 3D Gaussian Splat scenes (.ply, .splat, .ksplat) entirely in your browser needing no server, no installation, no account. Just drag and drop!
Robotics engineers and researchers frequently generate bag files during experiments, SLAM runs, and sensor calibration. Inspecting these files currently requires a full ROS1 or ROS2 installation, Foxglove Studio (increasingly commercial), or writing custom Python scripts for every inspection task.
BAGEL eliminates this friction.
| Problem | BAGEL Solution |
|---|---|
| Need ROS1/ROS2 installed to inspect bag files | Works in any modern browser |
| Foxglove Studio going commercial | 100% open source, MIT licensed |
ros2 bag info gives text-only output |
Rich visual interface with search & filtering |
| Can't share bag contents easily | Zero-install, you can send anyone the URL |
| Students struggle with ROS tooling | No setup required, just drag and drop |
| Legacy ROS1 bags require old toolchains | Drag the .bag straight in (no conversion needed) |
Data featured in this demo is from the excellent open-access M2DGR dataset provided by the SJTU-ViSYS team, which was instrumental in stress-testing this visualizer's spatial rendering capabilities.
A condensed feature list is below. Detailed version-by-version release notes (with the design rationale behind every feature) live in FEATURES.md.
| Format | Notes |
|---|---|
ROS2 .mcap |
Including zstd-compressed chunks (the new ROS2 default). Foxglove Studio JSON-encoded channels (schemaEncoding: "jsonschema", encoding: "json") supported from v1.6.1. |
ROS2 .db3 |
SQLite via sql.js / WASM |
ROS1 .bag |
Including bz2 and lz4 compressed chunks |
| Remote URLs | HTTP Range requests, so only the chunks you scrub through hit the network. .mcap / .bag stream lazily; .db3 eager-fetches (sql.js needs it in memory). |
| Foxglove WebSocket (v1.5.0) | Paste a ws:// or wss:// URL to connect to a live robot running foxglove_bridge or rosbridge_suite. All panels update in real time. Per-topic ring buffer holds the last 10,000 messages per topic; Follow/Pause button lets you scrub back into history without disconnecting. Auto-reconnect with exponential backoff. |
.pcd point clouds (v1.6.0) |
All three PCD 0.7 encodings: ascii, binary, binary_compressed (LZF). All color modes (height, intensity, rgb, single). Feeds directly into the ThreeDScene panel via a synthetic sensor_msgs/PointCloud2 message. |
.ply point clouds (v1.6.0) |
ASCII + binary_little_endian + binary_big_endian. RGB from red/green/blue uchar properties or packed rgb float. Drop a .ply and the cloud appears instantly in the 3D panel with all existing color, range-filter, and accumulator settings. |
| Gaussian Splats (v1.7.0) | Splat-flavored .ply (detected by header, not extension - a plain colored-point-cloud .ply still opens in the regular 3D panel), plus .splat and .ksplat. Opens in a dedicated Splat panel with outlier-robust camera auto-fit, shift+click custom orbit pivot, keyboard fly-through and orbit controls (W/S/A/D/Q/E/R/F/Z/C), B/N/I/K/U/J shortcuts to spin the splat itself on all three axes instead of moving the camera, and a V shortcut to cycle up-axis orientation presets. |
linear.x, orientation.w, etc.) on uPlot. Math expressions as derived series: type field_a * 2 + field_b in the series editor to plot any arithmetic combination of fields from the same topic without writing code. (v1.4.1)sensor_msgs/Image (rgb8 / bgr8 / rgba8 / mono8 / mono16) and CompressedImage (jpeg / png) with lazy single-message reads. Foxglove equivalents (foxglove.RawImage, foxglove.CompressedImage) supported via JSON schema translation. (v1.6.1) H264/H265 video via foxglove.CompressedVideo using the browser's WebCodecs VideoDecoder with a fast keyframe index for efficient seeking. (v1.6.2) Scroll to zoom (cursor-centered), drag to pan, double-click to reset; zoom percentage shown in the footer. (v1.6.3) Optional sensor_msgs/CameraInfo overlay (principal-point reticle + focal-length badge + calibration-likely-unfilled chip) toggles from the panel header. (v1.3.2) undistort button applies per-frame plumb-bob (Brown-Conrady) undistortion using the paired CameraInfo's D coefficients. (v1.3.4)PointCloud2, LaserScan, MarkerArray (all twelve primitives: CUBE / SPHERE / CYLINDER / ARROW / LINE_STRIP / LINE_LIST / CUBE_LIST / SPHERE_LIST / POINTS / TEXT_VIEW_FACING / MESH_RESOURCE / TRIANGLE_LIST from v1.3.1), OccupancyGrid, pose markers, camera frustums for every sensor_msgs/CameraInfo topic with per-camera hide checkboxes on multi-camera rigs (v1.3.2 / v1.3.4). Custom orbit pivot, range filter, point accumulation, configurable up-axis.Display > Overlays > scene topics to add maps, point clouds, laser scans, and odometry or pose topics to the same TF-aligned scene. This supports workflows such as an OccupancyGrid base map with live LiDAR points and the robot odometry pose on top. Per-overlay point size and flat-color overrides keep stacked layers visually distinct. A top-down orthographic 2D projection mode (with a zoom slider synced to scroll/pinch) reads an overlaid scene like a floor plan instead of a 3D orbit..ply / .splat / .ksplat files, built on @mkkellogg/gaussian-splats-3d. Outlier-robust camera auto-fit (ignores stray "floater" splats a naive bounding-box fit would get wrecked by), shift+click custom orbit pivot, and keyboard fly-through: W/S forward-back, A/D strafe, Q/E turn, R/F up-down, Z/C orbit around the pivot, active while hovering the panel. B/N, I/K, U/J spin the splat itself around the pivot on the Z/X/Y axes respectively, instead of moving the camera - useful for viewing it from a new angle or correcting a multi-axis tilt without the camera's perspective changing. V cycles up-axis orientation presets, since gaussian-splatting exports don't follow one universal up-axis convention./tf + /tf_static hierarchy with current transforms at the playhead time.diagnostic_msgs/DiagnosticArray. (v1.0)rcl_interfaces/Log and rosgraph_msgs/Log with severity, node-name, and full-text filters. (v1.0)All panels resolve header.frame_id through /tf + /tf_static against a user-selected world frame.
wall-clock, bag-start, and anchor (with a "Set anchor" picker UI in v1.0 so you can lock runs to a physical sync event)..mcap. Replaces the mcap filter CLI workflow for the common cuts. v1.2 extends the editor to ROS1 .bag and ROS2 .db3 inputs alongside MCAP - output is always MCAP regardless of input format. .db3 topics whose type isn't in BAGEL's bundled registry are flagged in the modal and excluded by default; opt them in to include them with a schema-less channel. (v1.1 / v1.2).msg schema flow for ROS2 .db3 topics whose types aren't in the bundled registry. Persisted across sessions in localStorage.canvas.toBlob()) so every panel type captures correctly. PNG frames are zipped with fflate at level 0 (no re-compression of already-deflated PNGs); WebM uses a two-phase MediaRecorder + captureStream(0) + requestFrame() approach so video playback speed is always correct. (v1.4.2)+ button, or press M), click a tick to seek, hover to see the label. Bookmarks persist to localStorage per bag and are encoded in the URL hash (bm=) for sharing. (v1.4.3).urdf and the robot appears in every 3D panel, anchored to its root link in world space via the bag's /tf stream. Joints animate from sensor_msgs/JointState (auto-detected) when the bag publishes it; static URDFs render at their rest pose. A toolbar "Robot" button opens the load modal; the Display card in each 3D panel grows a per-panel robot model hide toggle..stl / .dae / .obj meshes. Loaders are lazy-imported on first use of each file type so primitives-only URDFs don't pay the Collada loader's bundle cost.package:// resolver: paste a URL prefix or drag-drop a folder per referenced package; URL bindings persist across sessions in localStorage under bagel:package-roots:v1. No auto-fetch from ROS distros - BAGEL only loads meshes from where you point it.public/sample-bags/sample-robot.urdf) pairs with tour.mcap so the "Try a sample robot" button in the modal demonstrates the full flow on a fresh checkout.ws://host:8765 URL into the new "Connect" input. Works with foxglove_bridge (ROS2) and rosbridge_suite (ROS1/ROS2). Implements foxglove.websocket.v1 - binary MESSAGE_DATA frames for message payloads, JSON frames for topic advertisement.@foxglove/rosmsg2-serialization, ROS1 CDR via @foxglove/rosmsg-serialization - both already bundled as bag-parsing dependencies, no new packages. (v1.5.3 adds encoding: "ros1" for ROS1 bridges)Record button in the Toolbar (visible when a live connection is active) buffers every incoming message. Click Stop to serialize all captured data to a fully-indexed MCAP file and download it instantly. The output opens back in BAGEL or any mcap-compatible tool without conversion./clock is advertised, messages with logTimeNs = 0 (common in Gazebo/Isaac Sim) use the simulation clock value instead of wall-clock time, keeping plots readable in simulatibrowse all types & interfaces →
$ claude mcp add BAGEL \
-- python -m otcore.mcp_server <graph>