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

signalk-bignumbers

v0.0.19

Published

The simplest SignalK racing display: one to four huge numbers, nothing to install per screen, digits that never jump, zero lag.

Readme

signalk-bignumbers

Big, high-contrast number displays for SignalK — mast and repeater screens for racing, showing one to four values large enough to read from the rail.

A screen strapped to a mast in bright sun, filled by the number 0.0 labelled STW and kt, black on white

Why this exists

A racing repeater has to do three things:

  • Perform. Show what the instrument is reading now, at whatever rate the sensor produces it.
  • Be readable. At a glance, from metres away, at an angle, in spray, in full sun or full dark.
  • Be trivial to deploy. Minutes from a bare screen to a live number, with nothing installed or configured on the display.

How it meets them:

Fast. Someone is trimming to the number, so late data is worse than none. A delta arrives and paints: no queue, no batch, no requestAnimationFrame, no animation, no rate limit — a 10 Hz source updates the screen ten times a second. Nothing is smoothed, averaged or damped; that belongs upstream in SignalK. See Latency and load.

Large, clear numbers. One value fills the screen; up to four share it, all at the same digit size. Fixed high-contrast themes, not a colour picker: dark digits on a bright ground for sun, and night reds that light only the red sub-pixel to spare night vision. A value with no update for 3 seconds drops to dashes, so a dead feed looks dead rather than holding a stale reading.

Digits that don't jump. A number whose digits shift sideways can't be read from a moving boat, so the layout is fixed before any value arrives: the digit template (xx.x) reserves a column per digit, figures are tabular-nums, leading zeros are hidden rather than removed, and a value that can go negative reserves its minus column. 9.9 to 10.0 and back moves nothing but the digits. A reading too big for its template shows ^^.^ rather than clipping a digit and passing 140 off as 40.

No bloat. Static files: two pages and one shared table of presentations. No build step, bundler, framework, dependency, backend or plugin. A live display uses about 3% of a Pi Zero 2 W's CPU.

Nothing to install or configure on the display. Anything with a browser is a display the moment you open a URL on it: phone, old iPad, laptop, Pi with an HDMI panel. Deploying one is open the URL, read the name off the screen, add it in the webapp; configuring it is picking a path and a presentation from two dropdowns. The display holds no config and no credentials, so changing what it shows never means touching it.

Not an MFD: no charts, gauges, graphs, history, AIS, alarms, nothing to touch.

Quick start

Five minutes, using a phone as the display.

1. Install the webapp — the only thing that gets installed anywhere. In SignalK's admin UI: Appstore → Available, find Big Numbers, Install, then restart the server.

2. Open this on the phone. Substitute your own server address for signalk.local:3000 throughout — the same host and port you use to reach SignalK's admin UI.

http://signalk.local:3000/signalk-bignumbers/instrument.html?display=phone

The screen fills with phone and "Not configured yet" — its name, and nothing stored under it yet. phone is just a label you picked in the URL; use the same word in the next step.

3. Tell it what to show. From any browser, open http://signalk.local:3000/signalk-bignumbers/ (also in SignalK's Webapps menu). Log in at the top, + Add display, type phone, pick a path and a presentation, name it, Save.

4. Watch the phone. Within about 5 seconds it switches from its name to the number. No reload, no restart.

That phone is done. Changing what it shows is Edit; going from one value to four is + Add another number. Nothing on the phone changes either time.

Proper install

The quick start is the whole software install. What's left is making a device stay on the air unattended.

The two differ in one thing: where the name comes from. On a phone you type it into the URL; on a Pi, systemd fills in the hostname, so one service file works on every Pi in the fleet.

Phone or tablet

For a repeater someone carries, or a screen up for one race. An old iPad in a waterproof case works on the mast.

  1. Open the display URL in Safari or Chrome, with a name you pick: .../instrument.html?display=bow
  2. Add to Home Screen — it launches fullscreen, no address bar.
  3. Turn off auto-lock. The page holds no wake-lock, so a screen timeout takes the display off the air. On iOS: Settings → Display & Brightness → Auto-Lock → Never.
  4. Keep it on power. A bright screen on live data drains a battery fast.
  5. Optional: iOS Guided Access (Settings → Accessibility → Guided Access) locks it to the page.

Name it for where it goes — bow, pit, helm — not for the device. Swapping in a different phone is then one URL to open.

Raspberry Pi Zero 2 W (permanent mast display)

For a screen that lives on the boat: it comes up on power, has no battery or lock screen, and doesn't mind the rain. A Zero 2 W runs one with plenty of headroom.

Full walkthrough: docs/raspberry-pi-kiosk.md. In outline:

  1. Raspberry Pi OS (Trixie) console-only, no desktop.
  2. sudo apt install -y cog — a minimal WPE WebKit shell that renders straight to the framebuffer via DRM/KMS. No X11, no Wayland, no compositor between the delta and the glass.
  3. Test by hand: cog --platform=drm "http://signalk.local:3000/signalk-bignumbers/instrument.html?display=$(hostname)"
  4. Make it permanent with a systemd unit using %H in place of the hostname, so the file is identical on every Pi.
  5. Free tty1 (systemctl disable getty@tty1) and add consoleblank=0 to /boot/firmware/cmdline.txt.

Because the name is the hostname, deploying one is: name the Pi, plug it in, read the name off its screen, add it in the webapp.

Anything else

| Display | How it opens the page | |---|---| | Raspberry Pi Zero 2 W + HDMI panel | boots into it fullscreen, hostname as its name (guide) | | iPhone / iPad | Safari, Add to Home Screen | | Android phone or tablet | Chrome, Add to Home screen | | Laptop | any browser, fullscreen |

Mixed fleets are managed from one list, with no distinction between a mast Pi and a borrowed phone.


Reference

Installing and updating

It's a SignalK webapp — static files, no plugin and no backend. Normally you install and update it from the admin UI's Appstore, which restarts into the new version.

To run the development version instead, install from GitHub:

cd ~/.signalk
npm install github:ghotihook/signalk-bignumbers
sudo systemctl restart signalk    # or however your server is run

github:owner/repo clones the repo rather than looking the name up in the npm registry, so this gets the current main. Add #<tag> or #<branch> to pin one:

npm install github:ghotihook/signalk-bignumbers#0.0.19

cd ~/.signalk matters: signalk-server scans that directory's node_modules for webapps at startup, so anywhere else leaves it invisible. Either way, Big Numbers appears in the Webapps menu after the restart, served at http://<your-server>:3000/signalk-bignumbers/.

The webapp

One row per display, showing what it's set to. Edit changes it and the display picks it up within about 5 seconds; Delete sends it back to showing its own name, ready to be reassigned. A display's name is fixed once created, and is letters, digits, dots and hyphens only — on a Pi it has to match the hostname character for character, and either way it goes into a URL.

Per value:

| Field | Meaning | |---|---| | Path | What to show, picked from the paths this server is currently reporting. A compound value appears once per key (navigation.attitude → roll). A path the server has stopped reporting stays selected on a saved display, marked not currently reporting. | | Presentation | How to show it: the conversion from SignalK's SI units, the unit label, the digit layout and the sign handling, in one pick. Grouped by kind — Angle, Speed, Depth, Distance, Temperature, Time, Electrical, Other, Number. Each is labelled with its digit template (Knots — xx.x), with ± where it reserves a sign column. | | Display name | The label above the number. | | Colours | Fixed high-contrast pairs, grouped Day, Dusk and Night. See Colours. |

Path, Presentation and Display name are all required: Save and Preview outline anything left blank and say what it needs.

The presentations are listed in public/formats.js, which the editor and the display both read, so the two can't disagree about what one means. A few of them:

| Presentation | Shows | |---|---| | Wind angle, ±180° — ±xxx | -143°, folded back from 0…360 if the source sends it that way | | Heading, course, direction, 0–360° — xxx | 183°, in a three-digit column so it can't shift | | Knots — xx.x | 8.4 kt from SignalK's m/s | | Celsius — ±xx.x | 18.3 °C from SignalK's Kelvin | | Duration — hh:mm:ss | 01:02:05 from a count of seconds, e.g. time to go | | Start timer — ±mm:ss | -00:10 once the gun has gone, where Duration would read 00:10 | | Clock, local time — hh:mm:ss | 14:05:09 from a timestamp |

To show something no presentation covers, write the conversion keys into the URL directly — see Direct URLs. Written into a stored config instead, they show in the editor as Custom, and saving keeps them until you pick a listed presentation.

+ Add another number puts up to four values on the same screen. They split it into equal horizontal bands, top to bottom in the order listed, each with its own colours, all at the same digit size. Four on a landscape screen sit two by two instead, left to right then down: stacked, each band would be a wide strip with small digits. One wide value, such as an hh:mm:ss timer, shrinks all four, since every band shares its size. Bands sharing a background are divided by a hairline; otherwise the colour change is the divide. At night keep every band on black — one bright band costs the dark adaptation the night themes protect.

Preview opens the config in a new tab before saving. It needs the values filled in first, though not the display's name — an incomplete one previews as a screen you didn't configure: a value with no path drops out of the URL, and one with no presentation shows the raw SI number in a single digit column.

Colours

Fixed pairs, chosen for reading distance rather than taste. Day themes put black digits on a bright background: a brighter screen reads better, and reflections of sky and sails vanish against it where they'd show on black.

Dusk and night put light digits on black. Red and dim red light only the red sub-pixel, which the eye's night-vision rods barely register; amber lights green too, so it suits dusk rather than full dark. Dim red is for a screen whose backlight can't be turned down — the page can't dim it.

With more than one value, a different background per band lets a glance find the right number. The day colours stay distinct to red-green colour-blind crew — about one man in twelve — except lime beside yellow and lavender beside sky blue, so don't put either pair next to each other.

A display saved with a colour earlier versions offered moves to its replacement the next time it's edited and saved; until then it keeps its colours.

Configuration and permissions

Configs live in signalk-server's built-in applicationData store, keyed by the display's name. Displays read it anonymously; saving requires a SignalK login, which the webapp prompts for and keeps in the browser's localStorage.

A display holds no credentials, so if signalk-server's security is on it needs Security → Settings → Allow Readonly Access enabled, or nothing reaches the screen at all. The webapp checks this on load and warns if it's off, and a display that can't read its config says so rather than falling through to the "not configured" screen.

The editor's path dropdown is the boat's own data: it reads /signalk/v1/api/vessels/self and lists what the server is reporting right now. With the instruments off there is nothing to pick from, so set a display up with its sources live. A path already saved on a display stays in its dropdown either way, marked not currently reporting.

An unconfigured display polls every 5s waiting for a config to appear; a running one re-checks its own every 5s and reloads if it changed. Restarting a display to change what it shows is never necessary.

Reading the screen

  • A value with no update for 3 seconds fades to dashes. It never holds a stale reading.
  • A reading too big for its presentation shows ^ in every digit (^^.^) rather than widening past the screen edge — 140 m on a xx.x depth, say. Pick a presentation with more digits.
  • The dot top-right is green while the connection to SignalK is live, dark red when it isn't. A dropped connection retries every second and recovers on its own, showing the next live value rather than replaying what was missed.

Latency and load

Late data is worse than no data. A number two seconds old looks current, and nothing on screen says otherwise.

So as little as possible sits between the delta and the glass:

  • Subscriptions use policy: "instant", no period, no minPeriod. Values arrive as they change, not on a timer.
  • Each delta renders synchronously on arrival. No queue, no requestAnimationFrame, no setTimeout, no batching.
  • Nothing animates — a tweened number shows readings the boat never took. Value changes carry transition: none.
  • No smoothing, averaging or damping. Damping belongs upstream in SignalK, where every display gets it.
  • No history, replay or backfill. A reconnected display shows the next live value, not what it missed.

There's no rate limit either, so a 10 Hz source updates the screen ten times a second. That stays cheap:

  • A display receives only the paths it shows: the socket opens ?subscribe=none and subscribes by path, so the rest of the bus is never sent.
  • Between deltas nothing paints. CPU follows the data rate, not the clock.
  • An update writes the sign and digits, nothing else. Digit widths are reserved, so a new value doesn't reflow or resize the text; fitDisplay() runs at startup and on resize, not per value.
  • Past the refresh rate the browser coalesces writes into one paint, so a source faster than the panel costs parsing, not drawing.

If a Pi still can't keep up, cut values or slow the source — never buffer.

Direct URLs

A display can be configured entirely from its URL, with no stored config and no login — useful for testing, or a one-off screen:

instrument.html?path=environment.wind.speedApparent&name=AWS&format=speed-kn

| Parameter | Meaning | |---|---| | path | SignalK path to subscribe to (required) | | name | Label shown top-left of the band (required) | | format | A presentation from public/formats.js — speed-kn, angle-180, temp-c, dur-hms and so on. Stands in for layout, neg, wrap, unit, factor and offset | | field | Key to read from a compound value, e.g. roll | | layout | Digit template, e.g. xxx or xx.xx — sets the autosize and decimal places | | neg | true to reserve room for a minus sign | | wrap | true to fold the converted value into −180…180 | | unit | Shown in brackets after the name: STW (kt) | | factor, offset | Unit conversion: shown = raw * factor + offset | | bg, fg | Theme colours for this value's band; unsuffixed they also set the screen's. Hex only (#000000, #fff) — anything else is ignored | | host | SignalK server, if not the one serving the page (whole screen) | | cursor | 1 shows the mouse pointer, which is hidden otherwise (whole screen) | | display | Stored-config identifier — used instead of all of the above |

For later values, suffix every per-value key with 2, 3 or 4. The unsuffixed keys are the first value, so any single-value URL still means what it did (wrapped here for readability — it's one line):

instrument.html?path=navigation.speedOverGround&name=SOG&format=speed-kn
               &path2=environment.depth.belowTransducer&name2=DPT&format2=depth-m
               &path3=navigation.attitude&field3=roll&name3=Heel&format3=angle-small

A missing path2 ends the list, so values can't have gaps. host, cursor and display belong to the display as a whole and are never suffixed.

format is shorthand, not a replacement: layout, neg, wrap, unit, factor and offset still work on their own, and given alongside a format they override it. So every URL written before presentations existed still means exactly what it meant then, and a conversion the list doesn't cover can still be spelled out key by key.

The one exception is the time presentations: durations and clocks are reachable only through format, never through the raw keys.

bg and fg do double duty: suffixed (bg2, fg3) they colour just that band; unsuffixed they set the first band's colours and the screen's, which is what shows behind the "not configured" and error screens — those exist before there are any bands.

The webapp's Preview button builds these URLs for you.

What's here

  • public/index.html — the webapp. Lists, adds, edits and deletes displays.
  • public/instrument.html — the display itself. Fills the screen with up to four numbers, autosized to fit.
  • public/formats.js — the presentations, read by both pages: each one's conversion, unit, digit layout and sign handling.
  • docs/raspberry-pi-kiosk.md — building a display from a Pi.
  • dev/dummy_signalk.py — a fake SignalK server that sweeps values through a range, for testing without a boat. Speaks only the delta protocol, so it drives ?path= URLs, not ?display= ones.

License

MIT — see LICENSE. Bundles Roboto, Apache License 2.0.