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

niimbot-web-bluetooth

v1.3.5

Published

Zero-dependency Web Bluetooth driver + reverse-engineered protocol docs to print Niimbot label printers straight from the browser — no app. Validated on B1, B1 Pro and M2-H.

Downloads

983

Readme

niimbot-web-bluetooth

Live demo Release npm License: MIT Dependencies: none

Web Bluetooth driver and protocol documentation for Niimbot label printers — print straight from the browser, with no intermediary app and no dependencies.

Reverse-engineered and validated on real hardware (Niimbot B1, B1 Pro and M2-H). Two print-task variants over the same frame cover the B1 Pro / B21 Pro / D11 line (300 dpi, v4) and the B1 / M2-H / B21 line (b1, mostly protocol 3) — chosen automatically per connected printer.

🖨 Try the live demo →

Open it in Chrome/Edge, click Connect & identify printer, and print a test label. (Web Bluetooth needs HTTPS — the live demo and localhost both qualify.)

Contents

| Path | What it is | |---|---| | src/niimbot.js | Generic driver, no dependencies/build. Exposes window.Niimbot. | | registry.json | Registry of printer models + label sizes. | | docs/protocol-v4.md | Protocol V4 documentation (opcodes, frame, flow, geometry). | | demo/index.html | Standalone demo: pair and print a test label. |

Supported printers

| Model | task | dpi | Model id | Status | |---|---|---|---|---| | Niimbot B1 Pro | v4 | 300 | 4097 | ✅ Validated on real hardware | | Niimbot B1 | b1 | 203 | 4096 | ✅ Validated on real hardware | | Niimbot M2-H | b1 | 300 | 4608 | ✅ Validated on real hardware |

These three are in registry.json and tested end-to-end. Other printers on the same two protocol families — v4: D11_H / B21 Pro / D110_M; b1: B21 / D11 / D110 / B21S — are likely compatible but untested. To try one, add a model entry to registry.json (copy an existing model, set its task/dpi/id); please report results.

The driver auto-detects the connected model (see Selecting your printer), so it picks the right task and flow control even though several models share a BLE name.

Selecting your printer

The app picks the printer by passing a model and size object (both from registry.json) into the print calls:

  • model chooses the protocol behaviour. The key field is task: "v4" (B1 Pro line, 300 dpi) or "b1" (B1 line, 203 dpi, protocol 3). It also carries density (1–5), label_type, speed, and name_prefixes — the list of BLE advertised-name prefixes used to filter the browser's device chooser.
  • size is the label geometry in pixels: w_px (printhead axis) × h_px (feed axis), calibrated per dpi. A 50×30 mm label is a different pixel size on the B1 (384×240 @ 203 dpi) than on the B1 Pro (584×354 @ 300 dpi) — always pair a size with a model of the same dpi.

Auto-identification. The B1 and B1 Pro advertise the same BLE name (B1…), but the driver does identify which is which: on connect it asks the printer for its model id (PrinterInfo 0x40[08]) and protocol version (PrinterStatusData 0xA5) — exactly how niim.blue tells them apart — and exposes it as Niimbot.printer ({ modelId, protocolVersion, label, task, dpi }). Validated ids: B1 = 4096, B1 Pro = 4097. Two safeguards follow:

  • Niimbot.identify(model) connects and returns that info without printing, so the app can auto-select the right model/size (the demo does this — match model.id in registry.json to Niimbot.printer.modelId).
  • If you call printImage/printBatch with a model/size whose task or dpi doesn't match the connected printer, the driver throws before printing (naming the detected model) instead of printing at the wrong resolution.

On the first connect the browser shows its Bluetooth chooser (filtered by name_prefixes); the user selects the physical printer and pairs once.

Quick start

<script src="src/niimbot.js"></script>
<script>
  // Pull these from registry.json — shown inline here for clarity.
  // B1 (203 dpi):   task "b1",  size 384×240
  // B1 Pro (300dpi): task "v4", size 584×354
  const model = { name_prefixes: ["B1"], task: "b1", density: 3, label_type: 1, speed: 1 };
  const size  = { w_px: 384, h_px: 240, offset_y_px: 4 };   // T50×30 on the B1

  if (Niimbot.isSupported()) {
    // One label:
    await Niimbot.printImage("/path/to/label.png", {
      model, size, onProgress: (s) => console.log(s),
    });

    // N identical labels — image uploaded ONCE, printer repeats it (fast):
    await Niimbot.printImage("/path/to/label.png", { model, size, copies: 5 });

    // N distinct labels — one continuous job, streamed back-to-back:
    await Niimbot.printBatch([url1, url2, url3], { model, size });
  }
</script>

The image must be exactly w_px × h_px. The driver thresholds it to 1-bit (luminance < 128 = black) and sends it over BLE.

API

  • Niimbot.printImage(url, { model, size, copies, offsetY, onProgress }) — print one image. copies (default 1) prints N identical labels from a single upload (the printer repeats the image internally) — far faster than re-sending it. offsetY overrides size.offset_y_px to nudge the print down (px, feed axis).
  • Niimbot.printBatch([url1, url2, …], { model, size, onProgress }) — N distinct labels in one continuous job (one upload each, streamed back-to-back, no retract).
  • Niimbot.identify(model) → connect and return Niimbot.printer without printing.
  • Niimbot.printer → detected { modelId, protocolVersion, label, task, dpi } (or null before connecting). Used to tell a B1 from a B1 Pro (same BLE name).
  • Niimbot.isSupported()false on Firefox/Safari (no Web Bluetooth).
  • Niimbot.DEBUG = true — log BLE packets + a per-batch timing trace to the console.
  • Niimbot.BUNDLE_MAX — bytes per BLE write for frame bundling (default 240; 0 disables). Bundling cuts the paced-write count so dense pages stream without stalls.
  • Niimbot.PACE_MS — gap (ms) between unacked writes (default 10). macOS drops unacked write bursts, so there the driver paces every model; lower this only if your printer tolerates a smaller gap.

Requirements

Chrome/Edge (Chromium) over HTTPS or localhost. Web Bluetooth does not exist on Firefox/Safari.

Demo

Serve the repo over localhost and open the demo (Web Bluetooth needs HTTPS or localhost). A dependency-free Node server is included:

node demo/serve.mjs          # then open http://localhost:8080/demo/

The demo has a Model dropdown (B1 / B1 Pro) and a Label dropdown that only offers sizes matching the selected model's dpi — mirroring the selection rules above. Buttons cover a single label, 3 identical copies (one upload), a 3-label batch (distinct), and dense stress tests.

Real-world use

The driver running inside a real application, printing actual labels over Web Bluetooth — not just the test pattern:

Troubleshooting

| Symptom | Cause / fix | |---|---| | macOS: print comes out blank but progress hits 100% | macOS CoreBluetooth drops unacked write bursts. The driver already paces writes on macOS; if it still happens, raise the gap: Niimbot.PACE_MS = 16 (or higher). | | Error "Connected printer is X … select Y" | The selected model doesn't match the connected printer. Pick the model the driver detected (Niimbot.printer), or use Connect & identify in the demo. | | Dense / image-heavy labels are slow or stall between labels | This is BLE throughput on worst-case content. Tune Niimbot.BUNDLE_MAX (frames per write) and Niimbot.PACE_MS (gap). Real labels (text/codes, mostly white) stream fine; for N identical labels use copies (one upload). | | Printer never starts / PageEnd never acks (B1, 203 dpi) | An unacked burst dropped rows. Keep Niimbot.PACE_MS ≥ 10 for the B1. | | Niimbot.isSupported() is false | You're on Firefox/Safari, or not on HTTPS/localhost. Use Chrome/Edge over HTTPS. | | Nothing prints, no error | Open the console and set Niimbot.DEBUG = true to see the BLE packets + per-batch timing trace, then check where it stalls. |

Credits

Protocol reverse-engineered and validated on the B1 Pro. External community reference: niim.blue / niimbluelib.

License

MIT — see LICENSE.