npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ros2-web2d

v1.7.3

Published

Web-based 2D visualization library for ROS 2 (rosbridge + EaselJS).

Downloads

1,795

Readme

ros2-web2d

A web-based 2D visualization library for ROS 2. Renders occupancy grids, markers, paths, poses, odometry, and laser scans onto an EaselJS stage, driven by roslibjs over rosbridge.

ROS 2 only. ROS 1 (roscpp/rospy) installations are not supported — the library targets rosbridge v2 topic types (e.g. nav_msgs/msg/OccupancyGrid, geometry_msgs/msg/PoseStamped). Use the original ros2djs if you are on ROS 1.

import { Viewer, OccupancyGridClient } from 'ros2-web2d';
import ROSLIB from 'roslib';

const ros    = new ROSLIB.Ros({ url: 'ws://localhost:9090' });
const viewer = new Viewer({ divID: 'map', width: 800, height: 600 });

new OccupancyGridClient({ ros, rootObject: viewer.scene });

Features

  • TF-aware rendering — every client accepts an optional tfClient and wraps its output in a SceneNode that subscribes to the message's header.frame_id. Multi-robot deployments with mixed frames (e.g. /robot_0/map, /robot_1/odom) render correctly side-by-side. Without tfClient, clients behave exactly as in 1.2.x.
  • Map renderingOccupancyGridClient for live nav_msgs/OccupancyGrid streams, ImageMapClient for map_server-style map.yaml + .pgm / .png / .svg assets (the YAML and PGM loaders run entirely in the browser).
  • Navigation overlaysPathClient, PoseStampedClient, OdometryClient, PoseArrayClient, all share the same TF path and compose cleanly on one viewer.
  • MarkersMarkerArrayClient honors the ADD / MODIFY / DELETE / DELETEALL actions, per-marker lifetimes, and the ten marker primitives that project meaningfully into 2D.
  • SensorsLaserScanClient renders sensor_msgs/LaserScan as 2D points, with optional sampling and range filters.
  • Mouse controls — a drop-in enableViewerMouseControls helper in the example studio wires left-drag pan, right-drag rotate, and wheel zoom to any Viewer.
  • Modern build — ES modules, Rollup bundles (CJS / ESM / IIFE), TypeScript declarations, and a vitest suite with 156 tests at the time of writing.

Install

npm install ros2-web2d

Peer-installed alongside roslib ^2.x and createjs / easeljs.

ESM

import { Viewer, OccupancyGridClient } from 'ros2-web2d';

CommonJS

const { Viewer, OccupancyGridClient } = require('ros2-web2d');

Browser IIFE

<script src="https://cdn.jsdelivr.net/npm/easeljs@1/lib/easeljs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/roslib@2"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/build/ros2d.min.js"></script>
<script>
  const viewer = new ROS2D.Viewer({ divID: 'map', width: 640, height: 480 });
</script>

The browser global is ROS2D, kept for historical compatibility with the original ros2djs API surface. ESM / CJS imports use the new ros2-web2d specifier but the class names are unchanged, so ROS2D.Viewer and import { Viewer } from 'ros2-web2d' refer to the same constructor. A namespace rename (e.g. ROS2WEB2D) is deferred to a future major release with a deprecation window.

TF-aware rendering

Every client that used to ignore header.frame_id now accepts an optional tfClient. On first message for a given frame_id, the client creates a ROS2D.SceneNode that subscribes to TF, stays hidden until the first transform arrives, and then composes the message's pose into the configured fixedFrame.

import { PoseStampedClient, OdometryClient, PathClient } from 'ros2-web2d';

const tfClient = new ROSLIB.ROS2TFClient({
  ros,
  fixedFrame: 'map',
  angularThres: 0.01,
  transThres: 0.01,
  rate: 10.0,
});

new PoseStampedClient({ ros, topic: '/goal_pose',  rootObject: viewer.scene, tfClient });
new OdometryClient   ({ ros, topic: '/odom',       rootObject: viewer.scene, tfClient });
new PathClient       ({ ros, topic: '/plan',       rootObject: viewer.scene, tfClient });

All four overlays converge into the same fixed frame without the caller touching coordinate math. SceneNode owns the single Y-negate on the TF path, so child display objects keep using ROS coordinates.

Multi-robot arrays work the same way — MarkerArrayClient picks up each marker's own header.frame_id, so a single MarkerArray mixing /robot_0/odom and /robot_1/odom frames renders each robot at its own TF position.

Costmap overlays

OccupancyGridClient accepts a colorizer option that controls how each cell is painted. The 'map' preset is the default grayscale renderer (free/occupied/unknown). The 'costmap' preset implements an rviz-style inflation gradient — free and unknown cells are transparent, inflation goes blue → cyan → yellow, the inscribed band renders pink, and lethal (100) is red — with per-cell alpha scaling so a /local_costmap/costmap layer overlays cleanly on a base /map rendered in a lower rootObject layer.

// Base /map, grayscale
new OccupancyGridClient({ ros, topic: '/map', rootObject: viewer.scene });
// nav2 local costmap on top of it
new OccupancyGridClient({
  ros,
  topic: '/local_costmap/costmap',
  colorizer: 'costmap',
  rootObject: viewer.scene,
});

Pass a function for full control: colorizer: (value) => [r, g, b, a] receives the raw cell value (-1 for unknown, 0..100 for cost) and returns a 0..255 RGBA tuple.

Client reference

| Client | Topic type | Notes | |--------|------------|-------| | OccupancyGridClient | nav_msgs/OccupancyGrid | Continuous or one-shot; tfClient wraps the grid in a SceneNode; colorizer: 'costmap' renders nav2 inflation gradients over a base map | | ImageMapClient | (none) | Loads map.yaml + image asset directly; supports .png / .svg / .pgm | | MarkerArrayClient | visualization_msgs/MarkerArray | Supports ADD / MODIFY / DELETE / DELETEALL and lifetimes | | PathClient | nav_msgs/Path | Draws the path through PathShape | | PoseStampedClient | geometry_msgs/PoseStamped | Default arrow via NavigationArrow; pass shape to override | | OdometryClient | nav_msgs/Odometry | Same arrow surface as PoseStampedClient; extracts pose.pose | | PoseArrayClient | geometry_msgs/PoseArray | Rebuilds every message; useful for AMCL particle clouds | | LaserScanClient | sensor_msgs/LaserScan | 2D hit points with optional sampleStep / maxRange | | PolygonStampedClient | geometry_msgs/PolygonStamped | Closed outline via PolygonShape; default topic /local_costmap/published_footprint for active nav2 footprints; optional tfClient follows header.frame_id when frame conversion is needed |

Shared options on ROS-driven clients: ros, topic, rootObject, tfClient. Every client also forwards the standard ROSLIB.Topic subscribe options when supplied. Only options that the rosbridge subscribe op actually carries (plus the connection-level reconnect_on_close) are forwarded; advertise-only options like queue_size and latch are intentionally omitted because every client in this library is a subscriber.

| Option | Type | Notes | |--------|------|-------| | throttle_rate | number (ms) | Minimum interval between delivered messages | | queue_length | number | Bridge-side subscriber queue length | | compression | 'none' / 'cbor' / 'cbor-raw' / 'png' | rosbridge compression scheme | | reconnect_on_close | boolean | Auto-resubscribe after disconnect |

new MarkerArrayClient({
  ros, topic: '/markers', rootObject: viewer.scene,
  throttle_rate: 100,    // 10 Hz cap
  queue_length: 1,
  compression: 'cbor',
});

Footprint and polygon overlays

PolygonStampedClient subscribes to geometry_msgs/PolygonStamped and renders each message through PolygonShape, a thin stroked (optionally filled) closed polygon. The default topic is /local_costmap/published_footprint because the most common use is visualizing the active nav2 robot footprint over the costmap. Nav2's published footprint is already oriented in the message's header.frame_id (often odom or map); pass tfClient only when the viewer's fixed frame differs from that message frame.

new ROS2D.PolygonStampedClient({
  ros, rootObject: viewer.scene,
  topic: '/robot_0/local_costmap/published_footprint',
  tfClient: tfClient,             // optional frame conversion via header.frame_id
  strokeColor: '#ef4444',         // default red
  strokeSize: 0.03,               // ROS meters
  fillColor: 'rgba(239,68,68,0.1)', // optional translucent fill
});

In multi-robot deployments give each robot its own client (and topic) with a distinct strokeColor so the footprints stay visually attributable.

Interactive pose picking

ROS2D.PoseInteractionView is the web equivalent of rviz2's "2D Goal Pose" tool: the user clicks on the map, drags to indicate a heading, and on release receives { x, y, yaw } in the ROS world frame. The view owns its own NavigationArrow preview and handles the canvas Y-flip / rotation sign conventions internally.

var goalPicker = new ROS2D.PoseInteractionView({
  viewer: viewer,                 // ROS2D.Viewer instance
  arrowSize: 1.5,                 // ROS meters (default 1.5)
  arrowFillColor: '#ef4444',      // default red
  dragThresholdPx: 10,            // taps under this commit yaw=undefined
  onCommit: function(commit) {
    // commit: { x, y, yaw }
    // yaw is in radians (CCW from +X) or undefined for taps
    publishGoalPose(commit);
  },
});

// Toggle on/off without losing the preview shape:
goalPicker.disable();
goalPicker.enable();

// Permanent teardown (removes preview from the scene):
goalPicker.destroy();

Pair the view with setInteractionEnabled(false) (or your equivalent) on Pan/Rotate so the goal-placement drag does not also pan/rotate the map. The view ignores shift-modified clicks (reserved for pan) and any non-left mouse button.

Example studio

examples/ ships a Vite + React app that exercises every client end-to-end against a running rosbridge.

cd examples
npm install
npm run dev    # → http://localhost:5173

Demos included:

  • OccupancyGridClient — live /map with one-shot auto-fit
  • ImageMapClient — bundled sample_map.pgm + YAML, no ROS needed
  • MarkerArrayClient — TF-aware marker overlays
  • LaserScanClient/scan with a toggleable TF client
  • Navigation Overlayspath + pose + odom + particlecloud composed together

examples/src/lib/ros2dHelpers.js also exports reusable pieces — enableViewerMouseControls, createInitialMapViewFitter, fitMapView, addMetricBackdrop — that any app can drop into its own Viewer code.

Development

npm install
npm run build      # prebuild (transpile) + rollup + tsc
npm test           # vitest
npm run lint       # eslint via grunt

Source pipeline

src/                 single source of truth (ES5 global-namespace)
  ↓  grunt transpile
src-esm/             auto-generated ES modules (gitignored — do not edit)
  ↓  rollup
build/
  ros2d.cjs.js       CommonJS
  ros2d.esm.js       ES module
  ros2d.min.js       IIFE for browser <script>
  types/             TypeScript declarations (tsc)

Edit files in src/ only. The prebuild hook regenerates src-esm/ automatically, and npm run check:transpile acts as a CI guardrail against hand-edited src-esm/ commits.

Scripts

| Script | Description | |--------|-------------| | npm run build | Full pipeline: prebuild + rollup + tsc | | npm test | Run the vitest suite | | npm run lint | ESLint via grunt | | npm run transpile | Regenerate src-esm/ with debug output | | npm run check:transpile | Regenerate + assert no diff | | npm run doc | Rebuild JSDoc |

See CHANGELOG.md for per-release notes.

Origin

This project started as a fork of RobotWebTools/ros2djs and has since diverged into an independent, ROS 2-only library. The upstream project predates ROS 2 and has been unmaintained since 2022; ros2-web2d picks up the 2D-visualization role with a rebuilt TF integration, modern Rollup/ES module pipeline, a Vite + React example studio, and a test surface spanning 156 vitest cases plus a Playwright smoke suite. ROS 1 support is intentionally dropped.

License

BSD-3-Clause. Original work © Robert Bosch LLC, Willow Garage Inc., Worcester Polytechnic Institute, and Yujin Robot. See LICENSE for details.