hussain004/bagel: ROS Bag Visualizer. Contribute to Hussain004/BAGEL development by creating an account on GitHub.

github.com · 2026-10-02 · by Long Nguyen ·github
ROS Bag Visualizer. Contribute to Hussain004/BAGEL development by creating an account on GitHub.

hussain004/bagel

ROS Bag Visualizer. Contribute to Hussain004/BAGEL development by creating an account on GitHub.

49 stars · 5 forks

README

BAGEL BAG ExpLoration: ROS Bag File Web Visualizer Explore ROS1 & ROS2 bag files in your browser. No installation required.

→ Live Demo · Report Bug · Request Feature

What is BAGEL? 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. Why BAGEL?

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)

Demo Quick tour with the bundled sample bag

BAGEL-15s-showcase-2K-h264.mp4

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.

Features A condensed feature list is below. Detailed version-by-version release notes (with the design rationale behind every feature) live in FEATURES.md. File formats and live sources

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.

Visualization panels

TimeSeriesPlot: chart any numeric leaf field (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) ImageViewer: 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) ThreeDScene (Three.js): 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. Spatial topic layers (v1.7.1): Open any 3D panel, then use 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. SplatViewer (v1.7.0): dedicated 3D Gaussian Splat renderer for splat-flavored .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. TrajectoryPlot: Odometry / Pose / PoseWithCovariance / TransformStamped / NavSatFix as a 2D polyline, with an opt-in OpenStreetMap tile underlay for GPS traces. TFTree: /tf + /tf_static hierarchy with current transforms at the playhead time. DiagnosticArray: swimlane timeline + at-playhead inspector for diagnostic_msgs/DiagnosticArray. (v1.0) Log (rosout): virtualised list for rcl_interfaces/Log and rosgraph_msgs/Log with severity, node-name, and full-text filters. (v1.0) RawMessageInspector: collapsible JSON tree of the deserialized message at the playhead.

All panels resolve header.frame_id through /tf + /tf_static against a user-selected world frame. Multi-bag overlay

Drop multiple bags into the same session; each gets a colour tint that flows through every panel. Three time-alignment modes: 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). Per-bag parser Web Worker so parsing bag B doesn't queue behind bag A's decode.

Layout, sharing, and export

Drag-to-dock VSCode-style panel layout. Per-panel state (3D display settings, plot zoom, TF selection) survives docking. Sharable URL hashes encode layout + playhead + bag URL + per-bag anchors. v0.5 / v0.7 / v0.9 hash forms still parse, so old links keep working. Per-topic CSV / NDJSON export from every panel header. Bag editing / MCAP clip export: trim the time range, drop topics you don't need, download a fresh indexed .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) Paste-your-own .msg schema flow for ROS2 .db3 topics whose types aren't in the bundled registry. Persisted across sessions in localStorage. Clip export: Export button in the Toolbar renders any open panel (Image, Plot, Trajectory, or 3D Scene) frame-by-frame to a PNG zip or MP4 video (WebM fallback on browsers that cannot record MP4). Uses a frame-sync protocol (seek playhead, 2x rAF + 250 ms settle, canvas.toBlob()) so every panel type captures correctly. PNG frames are zipped with fflate at level 0 (no re-compression of already-deflated PNGs); video uses a two-phase MediaRecorder + captureStream(0) + requestFrame() approach so video playback speed is always correct. (v1.4.2) Timeline bookmarks: drop named markers at any timestamp on the scrubber (double-click the bar, click the + 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)

Robot model (URDF) overlay (v1.3.0)

Drop a .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. Geometry support: box / cylinder / sphere primitives + .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. Bundled sample robot URDF (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.

Live robot data (v1.5.0 - v1.5.6)

Foxglove WebSocket client: connect to a live robot by pasting a 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. Per-topic ring buffer: the last 10,000 messages per topic are held in memory. All existing panels (Image, Plot, 3D Scene, Trajectory, TF Tree, Log) display live data without any panel-level changes - the hooks detect a live entry and read from the ring buffer instead of the parser worker. Follow / Pause mode: the timeline's Follow button (also in the Toolbar chip) keeps the playhead at the live edge. Press Pause to scrub back through buffered history; press Follow to snap forward again. Auto-reconnect: exponential backoff on disconnect: 1s, 2s, 4s, 8s, 16s, 30s. Connection status shown as a pulsing dot (emerald = connected, amber = reconnecting, rose = error) on the toolbar chip. CDR and JSON decoding in the main thread. ROS2 CDR via @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) Live MCAP recording (v1.5.2): 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. Recording size limit + topic filter (v1.5.5): 500 MB hard cap auto-stops the recording and downloads immediately. A filter icon lets you select a subset of topics before recording starts. Byte count turns amber above 400 MB as a warning. Sim clock support (v1.5.4): when /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 simulation sessions. A SIM badge appears on the toolbar chip. Cross-bag health comparison (v1.5.6): the Health panel shows a chip strip at the top when multiple bags are loaded. Click any chip to switch the stats view to that bag without opening a new panel. Active chip is highlighted; live bags are excluded.

Analysis tools (v1.4)

Bag Health dashboard (v1.4.0, extended v1.5.6): a per-topic analytics panel showing measured Hz, jitter (standard deviation of inter-message gaps), gap events (pauses longer than 3x the expected period), and bandwidth (bytes/s). Opens from a Health button in the Toolbar. Data is computed once per bag in a background scan and cached. (v1.5.6) When multiple bags are loaded, a chip strip at the top lets you switch the view between bags without opening additional panels. Math expressions in plots (v1.4.1): type arithmetic expressions (vel_x * 2 + offset, sqrt(x*x + y*y)) as derived series directly in the TimeSeriesPlot panel. References other numeric fields from the same topic; evaluated in a sandboxed expression engine (no eval). Clip export (v1.4.2): render any panel to an animated PNG zip or MP4 video (WebM fallback) via a frame-sync protocol. Toolbar Export button opens the modal. Timeline bookmarks (v1.4.3): named markers on the scrubber, persisted per bag and shareable via the bm= URL hash segment.

UX and quality

Light + dark themes (toggle in the toolbar; persisted per browser). (v1.0) Keyboard shortcuts: Space to play, ← / → to step, L to loop, M to bookmark (v1.4.3), Esc to close panels, T to focus topic search, O to open a bag, ? for the cheat-sheet. Loop playback: a Timeline toolbar toggle wraps the playhead back to start at end-of-bag instead of pausing, persisted across reloads. (v1.3.3) Saved Display defaults: per-data-type defaults for the 3D panel's Display card (colour mode, accumulator, point size, range filter, up axis, camera-frustum master toggle), persisted across sessions. Manageable from the About modal. (v1.3.3 / v1.3.4) Accessibility pass: ARIA roles + focus management on every modal, prefers-reduced-motion respected, focus-visible rings throughout. Bundled tour.mcap sample bag exercises every panel type. Drop in zero seconds with the "Try a sample bag" button. 600+-test Vitest suite + GitHub Actions CI runs tsc -b + pnpm test on every PR. (v1.0, expanded each release) Bags well over 2 GB work in the browser: range reads + lazy decoding throughout the parser stack.

Looking for the long version with implementation notes and design tradeoffs for each release? See FEATURES.md.

Earlier version highlights at a glance

v1.7.0: Gaussian Splat viewer. Splat-flavored .ply (detected by header, distinguishing it from a plain colored point cloud), .splat, and .ksplat open in a dedicated panel built on @mkkellogg/gaussian-splats-3d. Camera auto-fit samples splat centers directly and uses a coordinate-wise median center + median-distance radius rather than a naive bounding box, so the handful of stray "floater" splats real captures commonly have don't wreck the framing. Shift+click sets a custom orbit pivot (via a camera-facing plane, since the library's own splat raycaster isn't part of its public API). Keyboard fly-through and orbit (W/S forward-back, A/D strafe, Q/E turn, R/F up-down, Z/C orbit) active while hovering the panel, plus a scale-appropriate ground grid and axes so movement direction is easy to judge. B/N/I/K/U/J spin the splat itself around the pivot on all three axes instead of moving the camera, reusing the same dynamicScene transform mechanism. V cycles up-axis orientation presets - the viewer is constructed with dynamicScene: true so the shader actually re-reads the transform each cycle, after an earlier fix that only updated the camera-fit math and silently never rotated the render. Performance: sharedMemoryForWorkers turns on automatically when the page is cross-origin isolated (moves the sort worker off a copy-based data path), and a splat's on-screen size is capped at 256px (down from the library's 1024px default) so getting close to one doesn't balloon its fill-rate cost to cover most of the panel. 13 new tests; 526 total. v1.6.3: Image zoom and pan in the ImageViewer panel. Scroll to zoom (centered on the cursor, newPanX = panX ratio + mouseX (1 - ratio)), drag to pan (pointer capture keeps tracking out-of-bounds), double-click to reset. Zoom percentage shown in the footer when not at 100%. View resets on topic or bag change. No new tests (pure UI state). v1.6.2: WebCodecs H264/H265 video decoding for foxglove.CompressedVideo topics. The parser worker builds a per-topic keyframe index (scanning only the first 24 base64 chars per message for speed), returns all frames from the last keyframe to the target time, and transfers them zero-copy via ArrayBuffer transfer. The main thread runs the browser's VideoDecoder API to accumulate reference frames and produce the correct ImageBitmap. isH264Keyframe / isH265Keyframe helpers detect IDR/SPS/VPS NAL types in Annex B streams. 19 new tests (+3 CompressedVideo in foxgloveSchemas.test.ts, 16 in tests/parsers/video.test.ts); 491 total. v1.6.1: Foxglove Studio MCAP schema support. Foxglove exports channels with schemaEncoding: "jsonschema" and encoding: "json"; BAGEL now decodes these with JSON.parse + a schema translator (foxgloveSchemas.ts) that maps Foxglove field names to ROS equivalents. Supported: foxglove.CompressedImage, foxglove.RawImage, foxglove.PointCloud (including NumericType remapping and base64 binary fields), foxglove.LaserScan (start/end angle to angle_min/max), foxglove.FrameTransform. All existing rendering panels (ImageViewer, ThreeDScene, LaserScan) work unchanged. 19 new tests in tests/parsers/foxgloveSchemas.test.ts; 472 total. v1.6.0: Standalone .pcd / .ply viewer. Drop any PCD or PLY point cloud directly into BAGEL - no ROS bag required. PCD supports all three encodings (ascii, binary, binary_compressed with LZF). PLY supports ASCII and both binary byte orders. Both produce a synthetic sensor_msgs/PointCloud2 that feeds the existing ThreeDScene pipeline unchanged - all color modes, range filters, and multi-file overlays work out of the box. 28 new tests in tests/parsers/pcd.test.ts and tests/parsers/ply.test.ts; 453 total. v1.5.6: Cross-bag health comparison. The Health panel now shows a chip strip at the top when multiple non-live bags are loaded. Click a chip to switch the panel's stats view to that bag. No new tests (React-only panel logic, covered by manual verification). 425 tests total. v1.5.5: Recording size limit + topic filter. 500 MB hard cap auto-stops recording and triggers download. Filter icon selects a per-topic subset before recording starts. Amber size warning above 400 MB. isFull and topicFilter added to RecordingStats. 9 new tests in liveRecorder.test.ts; 425 total. v1.5.4: Sim clock (/clock) support. LiveConnection tracks the /clock channel; messages with logTimeNs = 0 fall back to simClockNs instead of Date.now(). Purple SIM badge on the toolbar chip. extractClockNs() helper handles both ROS1 (nsec) and ROS2 (nanosec) clock schemas. 16 new tests in tests/live/simClock.test.ts; 425 total. v1.5.3: ROS1 live connection. Added encoding: "ros1" (ROS1 CDR, no RTPS header) to the live decoder alongside the existing cdr (ROS2) and json paths. @foxglove/rosmsg-serialization was already bundled; no new dependencies. Separate reader caches for ROS1 and ROS2 (wire formats are not interchangeable). 16 new tests in tests/live/liveDecoder.test.ts; 425 total. v1.5.2: Live MCAP recording. A Record button appears in the Toolbar while connected

source

github.com · 6 images · 1 videos · 124 links

Comments (0)

No comments yet