@kikita-labs/wake-word
v0.1.0-alpha.4
Published
Typed Node.js runtime for self-contained Kikita wake-word models.
Maintainers
Readme
@kikita-labs/wake-word
Typed Node.js binding for loading and running self-contained Kikita .kww wake-word
models through the shared Rust core.
npm install @kikita-labs/wake-wordThe package is application-neutral. Consumers provide mono PCM plus its source sample rate; Discord, Opus, microphone capture, and framework integration remain outside the runtime.
import { WakeWordModel } from "@kikita-labs/wake-word";
const model = await WakeWordModel.fromBundle("model.kww");
const detector = await model.createDetector({ sourceSampleRateHz: 48_000 });
detector.on("detected", (event) => console.log(event.score));
detector.on("error", (error) => console.error(error));
await detector.process(pcmChunk);
await detector.close();
await model.dispose();process() emits accepted detections and returns diagnostic frame results for advanced
consumers. Call it for each ordered PCM chunk received from Discord, a microphone, a file
decoder, or another source. A Node Readable can instead use standard backpressure:
pcmReadable.pipe(detector);pipe() is optional. It only connects an existing Node audio stream to the detector and
automatically respects backpressure.
The bundle source may be a filesystem path or already downloaded bytes:
const fromDisk = await WakeWordModel.fromBundle("./models/model.kww");
const response = await fetch(signedModelUrl);
if (!response.ok) throw new Error(`Model download failed: ${response.status}`);
const fromR2 = await WakeWordModel.fromBundle(
new Uint8Array(await response.arrayBuffer()),
);The runtime does not perform network requests or own a disk cache. Keep one loaded model for the application lifetime. An application may persist an S3/R2 download in its own cache and pass that path on the next start. Create one detector per ordered audio source, user, or voice channel.
NestJS keeps the model in a singleton provider and can publish accepted detections through RxJS:
import { Injectable } from "@nestjs/common";
import { Subject } from "rxjs";
import { type FrameResult, WakeWordModel } from "@kikita-labs/wake-word";
@Injectable()
export class WakeWordService {
readonly detected$ = new Subject<FrameResult>();
async createSource(model: WakeWordModel, sourceSampleRateHz: number) {
const detector = await model.createDetector({ sourceSampleRateHz });
detector.on("detected", (event) => this.detected$.next(event));
return detector;
}
}Browser Angular cannot load the native Node package. NestJS runs inference and sends a small detection event to Angular over WebSocket, SSE, IPC, or another application transport. A future browser/WASM binding is separate work.
For low-level access, createStream() and processPcmS16le() remain available. One
source chunk may complete zero, one, or several inference windows, so that API returns an
array.
pcmChunk may be a Buffer, Uint8Array, or Int16Array. Byte inputs are decoded as
little-endian signed 16-bit mono PCM. The detector copies input data, keeps resampling and
detection state per source, and rejects concurrent manual calls. Close detectors before
their shared model.
The runtime owns resampling, feature inference, candidate scoring, temporal confirmation, cooldown, and an embedded verifier when the bundle declares one. It deliberately does not decode Opus, capture microphones, or depend on Discord, Electron, NestJS, or another application framework.
The npm tarball contains prebuilt Node-API binaries and statically linked ONNX Runtime.
End users need Node.js 24 or newer on a supported platform, not Rust, node-gyp, or a
separate ONNX package.
Supported by the first alpha: Windows x64, glibc Linux x64, and Apple Silicon macOS.
See the repository for the .kww
specification, compatibility matrix, security policy, and release notes.
