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

triki-controller

v0.3.0

Published

Driver for the Żabka Triki token (nRF52810 + LSM6DSL) — connect, stream the IMU, and read fused orientation (selectable Madgwick or VQF) to reuse the token as a motion controller. Web Bluetooth in the browser, or @abandonware/noble in Node.

Readme

triki-controller

A strongly-typed driver for the Żabka Triki BLE token (an nRF52810 BLE SoC + LSM6DSL accelerometer/gyroscope, shaped like a bottle cap). It connects over the Nordic UART Service, starts the IMU stream, parses the 14-byte motion frames, and optionally fuses orientation with a selectable 6-axis filter — Madgwick or VQF — so you can reuse the token as a motion controller. Web Bluetooth in the browser (zero runtime dependencies), or Node via the optional @abandonware/noble transport.

Triki motion controller demo

Browser support

In the browser, Web Bluetooth is required (see Node.js & custom transports for everything else):

  • ✅ Desktop Chrome / Edge / Opera (Chromium-based).
  • Safari and Firefox do not implement Web Bluetooth.
  • Must run in a secure contexthttps:// or http://localhost. Not file://.
  • connect() must be called from a user gesture (e.g. a click handler).
  • One BLE central connects at a time.

Install

npm install triki-controller

Quick start

import { TrikiController } from "triki-controller";

if (!TrikiController.isSupported()) {
  throw new Error("This browser has no Web Bluetooth.");
}

const triki = new TrikiController({ fusion: true, rateHz: 104 });

triki.on("connectionchange", (state) => {
  console.log("state:", state); // "disconnected" | "pairing" | "streaming"
});

triki.on("frame", (f) => {
  // f.gyro = { x, y, z } in deg/s, f.accel = { x, y, z } in g, f.t = ms
});

triki.on("orientation", (o) => {
  // o.quaternion = [w, x, y, z] (right-handed), o.euler = { roll, pitch, yaw } in degrees
});

triki.on("rate", (hz) => {
  console.log("streaming", hz, "Hz");
});

// Must be inside a click/tap handler:
document.querySelector("#connect")!.addEventListener("click", async () => {
  await triki.connect();
  await triki.setLed(true);     // green LED on
  await triki.setRate(208);     // bump the sample rate (26 / 52 / 104 / 208 / 416 Hz)
  triki.resetHeading();         // re-zero yaw whenever you like
});

Pick the filter with fusion: "madgwick" (default), "vqf", "accel" (accelerometer-only tilt — instant but jittery, no yaw), or "none" (true≡madgwick, false≡none). Raw frames only: { fusion: "none" }, listen to "frame". Switch live with setFusion(...). The MadgwickAHRS, VqfAHRS, AccelAHRS and FrameParser classes are also exported and usable standalone (the filters share an OrientationFilter interface).

API

new TrikiController(options?)options: fusion? ("madgwick"/"vqf"/"accel"/"none", default "madgwick"), rateHz? (default 104), beta? (Madgwick gain, default 0.08), tauAcc? (VQF accel low-pass seconds, default 2.0), gyroScale? (default 14.286), gyroBias? / accelBias? (per-axis correction vectors, default zeros), transport? (a TrikiTransport; defaults to the browser's WebBluetoothTransport).

| Member | Description | | --- | --- | | static isSupported() | true when navigator.bluetooth exists (SSR-safe). Only about the default Web Bluetooth transport — in Node with NobleTransport just call connect(). | | connect() | Show the picker, connect, start streaming. User gesture required. | | reconnect() | Reconnect to the last paired device without the picker. | | disconnect() | Disconnect. Idempotent; safe in any state — cancels a pending connect(). | | dispose() | Disconnect, release the transport (another controller may then attach), drop all listeners. | | setLed(on) | Green LED on/off (throws if the token has no LED characteristic). | | setRate(hz) | Set sample rate; applied live when streaming. | | resetHeading() | Re-zero yaw (no-op when fusion is off). | | setFusion(algo) | Switch filter at runtime ("madgwick"/"vqf"/"accel"/"none"). | | setBeta(v) / setTauAcc(v) | Tune the active Madgwick / VQF filter live. | | setGyroScale(scale) | Runtime gyro calibration. | | setGyroBias(v) / setAccelBias(v) | Set the per-axis correction vectors live. | | isConnected / state / rateHz / hasLed / battery / fusion / fusionAlgorithm | Getters. | | on(type, cb) / once / off | Typed subscription; on/once return an unsubscribe fn. |

Events: frame, orientation (fusion only), connectionchange (payload is the state string), rate (measured Hz), battery (level in percent, 0–100).

Node.js & custom transports

The BLE link is pluggable: pass any TrikiTransport via the transport option. To receive the token from a plain Node process, use the NobleTransport from the triki-controller/node entry (backed by the optional peer dependency @abandonware/noble — installed separately, imported lazily):

npm install triki-controller @abandonware/noble
import { TrikiController, NobleTransport } from "triki-controller/node";

const triki = new TrikiController({ transport: new NobleTransport() });
triki.on("orientation", (o) => console.log(o.euler));
await triki.connect(); // scans for a "TRIKI" token (15 s budget), connects, streams

Options, timeout/cancellation semantics, the full TrikiTransport contract, and a runnable example live in the library guide and example/node.mjs.

Caveats

  • Yaw drift. Fusion is 6-axis (gyro + accel, no magnetometer), so absolute heading drifts over time. Roll/pitch stay levelled by gravity. Call resetHeading() to re-zero yaw.
  • Math vs display frame. This library emits the right-handed math quaternion. The Triki demo page negates y and z ([w, x, -y, -z]) only for its on-screen roll/pitch/yaw readout (the 3D model is driven from the unmodified quaternion) — apply that negation yourself only if you mirror that readout; don't double-negate.
  • Throughput. Above ~208 Hz, BLE may not keep up; the rate event reports the actual measured throughput so you can react.

License

MIT © Flopsstuff