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

@blueyerobotics/multibeam

v0.1.0

Published

Reusable multibeam sonar viewer components for Blueye drones.

Readme

@blueyerobotics/multibeam

React components and utilities for rendering and playing back Blueye multibeam sonar (.mbez) files in the browser via WebGL.

Installation

npm install @blueyerobotics/multibeam @blueyerobotics/blueye-ts

Peer dependencies: react >=18, @blueyerobotics/blueye-ts >=3


Palette image

All rendering functions require a palette image that maps sonar intensity values to colours. The image must be exactly 256 pixels wide. Each palette occupies a 10-pixel-high row — the image height determines the number of available palettes (e.g. the bundled Palettes.png is 256 × 90 px, providing 9 palettes).

The palette image is not bundled with the package — it must be supplied by the application. The Blunux web app ships one at frontend/public/Palettes.png. You can use that file directly, or provide your own image following the same format.

Call loadPaletteFromUrl once at app startup before rendering any frames:

import { loadPaletteFromUrl } from "@blueyerobotics/multibeam";

// The image must be served from your app — it is not bundled with the package.
// The Blunux app's Palettes.png can be found at frontend/public/Palettes.png.
loadPaletteFromUrl("/Palettes.png");

The palette is loaded into a shared singleton WebGL context so it only needs to be called once.


Components

MultibeamPlayer

High-level component that handles file loading, decompression, parsing, and timing-accurate playback. It renders the canvas and exposes playback state to your UI via a render prop.

import { MultibeamPlayer } from "@blueyerobotics/multibeam";

<MultibeamPlayer
  file={blob} // Blob — the .mbez file
  paletteIndex={0} // number — which palette row to use (default: 0)
  overlayOptions={{
    // optional range/sector overlay
    rangeLines: true,
    sectors: true,
    labels: true,
  }}
  fadeFarEdge={false} // fade the far edge of the fan (default: false)
  className="w-full h-full" // optional — outer wrapper class
  rendererClassName="drop-shadow-xl" // optional — passed to inner MultibeamRenderer
  onDiscovery={(d) => {
    // fired if a MultibeamDiscoveryTel message exists
    console.log("sonar model:", d.modelName);
  }}
  onReady={() => {
    // fired after parsing — use to hide loading spinners
    setLoading(false);
  }}
>
  {(state) => (
    // state: PlayerState — see type below
    <MyControls state={state} />
  )}
</MultibeamPlayer>;

PlayerState

| Field | Type | Description | | ------------- | --------------------- | ----------------------------------------- | | messages | Message[] | Decoded ping frames | | index | number | Currently displayed frame index | | playing | boolean | Whether playback is running | | loading | boolean | Whether the file is still loading/parsing | | error | string \| null | Parse or fetch error message | | play() | () => void | Start playback | | pause() | () => void | Pause playback | | toggle() | () => void | Toggle play/pause | | seek(index) | (n: number) => void | Jump to a specific frame | | reset() | () => void | Return to frame 0 and pause |


MultibeamRenderer

Low-level component that renders a single frame to a canvas. Each instance owns its own WebGL context and renders directly — no shared state or blit overhead. Overlays are drawn on a separate 2D canvas layered on top. Palette URL is sourced automatically from MultibeamPaletteProvider (or an optional paletteUrl prop override).

import { MultibeamRenderer } from "@blueyerobotics/multibeam";

<MultibeamRenderer
  frame={message} // Message | null — a decoded ping frame
  paletteIndex={0}
  overlayOptions={{ rangeLines: true, sectors: true, labels: true }}
  fadeFarEdge={false}
  paletteUrl="/Palettes.png" // optional — overrides MultibeamPaletteProvider
  className="w-full h-full"
/>;

QueuedMultibeamRenderer

Thumbnail renderer that uses a shared off-screen WebGL context with a serialised render queue. Uses IntersectionObserver to lazy-load thumbnails when they scroll into view. Ideal for media grids with many sonar thumbnails.

import { QueuedMultibeamRenderer } from "@blueyerobotics/multibeam";

<QueuedMultibeamRenderer
  url="https://device/webdav/dive_001_thumbnail.mbez"
  paletteIndex={0}
  width={600} // optional — canvas width in px (default: 600)
  height={400} // optional — canvas height in px (default: 400)
  fallback={<span>No preview</span>}
  className="w-full h-full"
/>;

MultibeamPaletteProvider

Context provider that loads a palette image and makes the URL available to all MultibeamRenderer and QueuedMultibeamRenderer instances in the tree. Also loads the palette into the shared WebGL context for thumbnail rendering. Place near the app root.

import { MultibeamPaletteProvider } from "@blueyerobotics/multibeam";

<MultibeamPaletteProvider url="/Palettes.png">
  <App />
</MultibeamPaletteProvider>;

Use the useMultibeamPaletteUrl() hook to read the current palette URL from context.


Utilities

enqueueThumbnailLoad(url: string): Promise<Message>

Downloads a _thumbnail.mbez file and returns the single decoded frame. Requests are serialised through an internal queue to avoid flooding the browser with concurrent fetches — safe to call for every item in a large media grid.

import { enqueueThumbnailLoad } from "@blueyerobotics/multibeam";

const frame = await enqueueThumbnailLoad("https://device/webdav/dive_001_thumbnail.mbez");

loadPaletteFromUrl(url: string): void

Loads a palette image into the shared WebGL context. Must be called before any frame is rendered. See the Palette image section above.

renderFrameToCanvas(message, canvas, paletteIndex, overlayOptions?, fadeFarEdge?): void

Renders a decoded frame directly onto a provided HTMLCanvasElement. Lower-level escape hatch for non-React usage or custom canvas management.

import { renderFrameToCanvas } from "@blueyerobotics/multibeam";

renderFrameToCanvas(message, myCanvas, 0, { rangeLines: true });

renderOverlays(ctx, message, options?): void

Draws range arcs, sector lines, and distance labels onto an existing CanvasRenderingContext2D. Useful if you want to composite the overlay on a separate canvas layer.

createDirectRenderer(canvas): DirectRenderer

Creates a renderer that owns its own WebGL context on the given canvas. Returns an object with draw(frame, paletteIndex?, fadeFarEdge?), loadPalette(url, onLoaded?), and destroy() methods. The optional onLoaded callback fires after palette textures are created — use it to trigger a redraw so the first frame doesn't render with a fallback palette. Used internally by MultibeamRenderer but available for advanced use.

getPaletteSwatchStyle(imageUrl, index, count, swatchHeight): PaletteSwatchStyle

Returns CSS background properties (backgroundImage, backgroundSize, backgroundPosition, backgroundRepeat) needed to display a single palette swatch from the palette sprite image. Useful for building palette selector UIs.

paletteCount

The number of palettes available in the loaded palette sprite image.


Device utilities

SONAR_DEVICE_INFO: Record<number, SonarDeviceInfo>

Device capability data for all supported multibeam sonar models, keyed by device ID. Includes name, frequency ranges, valid beam counts, and feature support flags.

MULTIBEAM_DEVICE_IDS: Set<number>

Set of all known multibeam device IDs. Use for quick membership checks (e.g. to determine whether a guest-port device is a multibeam sonar).

MULTIBEAM_DEVICE_NAMES: Record<number, string>

Map from device ID to a human-readable model name (e.g. 16 → "Gemini 720im").

DEFAULT_MULTIBEAM_CONFIG

Sensible default values for a MultibeamConfig protobuf message. Useful as a starting point when the drone hasn't reported its current configuration yet.

getRangeInfo(deviceId, frequencyMode): SonarRangeInfo

Returns the min/max range for a given device and frequency mode.

getValidBeams(deviceId): number[]

Returns the valid beam count enum values for a given device.

getConnectedMultibeam(droneInfo): ConnectedMultibeam | null

Extracts the connected multibeam device from a DroneInfoTel telemetry message.


OverlayOptions

type OverlayOptions = {
  rangeLines?: boolean; // draw concentric range arcs (default: true)
  sectors?: boolean; // draw sector boundary lines (default: true)
  labels?: boolean; // draw distance labels on range arcs (default: true)
  formatRange?: (meters: number) => string; // custom label formatter
  labelFontSize?: number; // override auto-scaled label font size in pixels
  labelColor?: string; // label text fill colour (default: "rgba(255,255,255,0.8)")
  labelStrokeColor?: string | null; // label stroke colour, or null to disable (default: "rgba(0,0,0,0.6)")
};

Examples

Ready-to-use example components are provided in src/examples/:

ThumbnailExample

Lazy-loading thumbnail using QueuedMultibeamRenderer. Only fetches the _thumbnail.mbez when the element scrolls into view. Requires a MultibeamPaletteProvider ancestor.

import ThumbnailExample from "@blueyerobotics/multibeam/examples/ThumbnailExample";

<div style={{ width: 320, height: 213 }}>
  <ThumbnailExample
    url="https://device/webdav/dive_001_thumbnail.mbez"
    paletteIndex={0}
    fallback={<span>No preview</span>}
  />
</div>;

PlayerExample

Minimal playback UI with scrubber, play/pause, reset, overlay lines, and far-edge fade. Use as a starting point for a custom player.

import PlayerExample from "@blueyerobotics/multibeam/examples/PlayerExample";

// file is a Blob — e.g. from a fetch or <input type="file">
<PlayerExample file={blob} paletteIndex={0} onDiscovery={(d) => console.log(d)} />;

Publishing

The package is published automatically from CI when a tag matching multibeam/vX.Y.Z is pushed:

git tag multibeam/v0.2.0
git push origin multibeam/v0.2.0

The publishConfig.exports field in package.json remaps the entry point from src/index.ts (used in the workspace) to dist/multibeam.js (used by npm consumers).