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

ipixel-remote

v1.1.0

Published

A reusable Web Bluetooth client library for controlling single or multiple iPixel 32x32 LED displays as a single coordinate grid display.

Readme

ipixel-remote

npm version npm downloads license

A client-side Web Bluetooth library for controlling one or more iPixel 32x32 LED matrix displays as a unified grid display. Supports time/brightness configuration, real-time canvas slicing, concurrent BLE transfers, and pixel-perfect font rendering.

🌐 Live Demo — Try it in Chrome with a real iPixel device.

Note: Web Bluetooth requires Chrome, Edge, or Opera on a desktop or Android device. HTTPS is required (except on localhost).


Installation

npm

npm install ipixel-remote
import { IPixelDevice, VirtualDisplay, PixelTextEngine, IPixelPreview } from 'ipixel-remote';

Direct HTML Script Tag

Include directly into a page to expose the global window.iPixel namespace:

<script src="https://eraldonio.github.io/ipixel_remote_lib/index.js" type="module"></script>
<script>
  const { Device, VirtualDisplay, TextEngine, Preview } = window.iPixel;
</script>

Note: Always include type="module" on the script tag.


Core API Reference

1. IPixelDevice

Represents a connection session to a single physical iPixel display.

| Method | Description | |---|---| | new IPixelDevice(options?) | Creates a new BLE controller. Accepts { chunkSize } (default: 244). | | async connect(options?) | Opens a browser device picker filtered to LED_BLE_ devices. Syncs time and sets brightness to 70 on success. | | static async autoConnect(options?) | Silently reconnects to all previously paired iPixel displays without showing a picker. Uses navigator.bluetooth.getDevices(). Works across tab closes and browser restarts. Accepts { onConnect: (dev) => {} } for per-device progressive UI updates. Returns an array of connected IPixelDevice instances. Requires Chrome and a prior manual pairing on this origin. | | disconnect() | Manually closes the GATT connection. | | async setBrightness(level) | Sets display brightness. level must be a number between 0 and 100. | | async syncTime() | Synchronizes the display clock with the current system time. | | async sendImage(pngBytes) | Transmits a 32x32 PNG Uint8Array to the display. | | onDisconnectCallback | Optional callback (device) => void fired on disconnect. |


2. VirtualDisplay

Grid layout coordinator that treats multiple displays as one unified canvas.

| Method | Description | |---|---| | new VirtualDisplay({ cols, rows }) | Creates a virtual grid. Exposes this.canvas at (cols * 32) x (rows * 32) pixels. | | registerDevice(index, device) | Assigns a connected IPixelDevice to a slot index. | | swapSlots(slotA, slotB) | Swaps the physical output mapping of two display slots. | | resetSlots() | Resets slot mapping back to default 1-to-1 order. | | isSwapped() | Returns true if the slot mapping has been modified from default. | | getCanvasContext() | Returns the CanvasRenderingContext2D for drawing on the unified canvas. | | async sync() | Slices the canvas and transmits individual 32x32 frames to each display in parallel. |


3. PixelTextEngine

Pixel-perfect text rendering for ultra-low resolution displays.

| Method | Description | |---|---| | wrapText(text, maxChars?) | Splits text into lines (max 5), fitting within maxChars per line (default: 6). | | getBestFontSize(ctx, text, maxWidth, maxHeight) | Returns the largest Silkscreen font size (8–24px) that fits the given bounds. | | drawCrispText(ctx, text, size, color, targetY, targetHeight) | Renders 1-bit threshold-filtered crisp text directly on a canvas context. No anti-aliasing. |


4. IPixelPreview

Digital twin simulator component. Renders live previews of display output inside a DOM container.

| Method / Option | Description | |---|---| | new IPixelPreview(container, options?) | Mounts preview cards into container. | | options.cols | Number of display columns (default: 1). | | options.rows | Number of display rows (default: 1). | | options.labelPrefix | Label prefix for each screen card (default: "Screen"). | | options.onSwap | Callback (isSwapped: boolean) => void fired when the swap state changes. | | update(virtualCanvas) | Reads virtual canvas width and repaints preview cards from the source canvas. | | toggleSwap() | Programmatically toggles the visual swap state and fires onSwap. | | setSwapState(bool) | Explicitly sets the visual swap state without firing onSwap. |


Quickstart Integration Example

<!DOCTYPE html>
<html lang="en">
<head>
    <title>iPixel Example</title>
</head>
<body>
    <button id="connect-btn">Connect Display</button>
    <button id="swap-btn" style="display:none;">⇄ Swap</button>
    <div id="preview-container"></div>

    <script type="module">
        import { IPixelDevice, VirtualDisplay, PixelTextEngine, IPixelPreview } from 'ipixel-remote';

        const display = new VirtualDisplay({ cols: 2, rows: 1 });
        const preview = new IPixelPreview(document.getElementById('preview-container'), {
            cols: 2,
            rows: 1,
            labelPrefix: 'Display',
            onSwap: (isSwapped) => {
                display.swapSlots(0, 1);
            }
        });

        document.getElementById('connect-btn').addEventListener('click', async () => {
            const dev = new IPixelDevice();
            await dev.connect();
            display.registerDevice(0, dev);

            // Draw text on the unified canvas
            const ctx = display.getCanvasContext();
            ctx.fillStyle = '#000000';
            ctx.fillRect(0, 0, 64, 32);
            PixelTextEngine.drawCrispText(ctx, 'HELLO', 14, '#00FFFF', 4, 24);

            // Send to physical display and update the preview twin
            await display.sync();
            preview.update(display.canvas);

            document.getElementById('swap-btn').style.display = 'inline';
        });

        document.getElementById('swap-btn').addEventListener('click', () => {
            preview.toggleSwap();
            display.sync();
        });
    </script>
</body>
</html>

Browser Compatibility

| Platform | Browser | Supported | |---|---|---| | 🖥️ Windows / macOS / Linux | Chrome | ✅ Full support | | 🖥️ Windows / macOS / Linux | Edge | ✅ Full support | | 🖥️ Windows / macOS / Linux | Opera | ✅ Full support | | 🖥️ Windows / macOS / Linux | Firefox | ❌ Not supported | | 📱 Android | Chrome | ✅ Full support | | 📱 Android | Samsung Internet | ⚠️ Partial support | | 📱 Android | Firefox | ❌ Not supported | | 🍎 iPhone / iPad (iOS) | Any browser | ❌ Not supported |

[!NOTE] Why does iOS not work? Apple requires all browsers on iPhone and iPad to use the WebKit rendering engine under the hood. This means Chrome, Firefox, and Edge on iOS are all restricted to WebKit's capabilities — and WebKit does not implement the Web Bluetooth API. This is an Apple platform restriction, not a library limitation. The only path to iPhone support would be a native iOS app using Swift's CoreBluetooth framework.


Links


License

MIT