Package Exports
- mellon
Readme
What's the elvish word for "friend" ?
Mellon
Offline, fully in-browser hotword / wake-word detection powered by EfficientWord-Net (ResNet-50 ArcFace).
- 100% offline — ONNX inference runs in the browser via WebAssembly; no server, no cloud.
- Speaker-independent — the model generalises across voices out of the box.
- Custom words — enroll any phrase with ≥ 3 audio samples.
- TypeScript-ready — ships with full
.d.tsdeclarations.
Table of contents
Installation
npm install mellonQuick start
import { Detector } from 'mellon'
const hotWordDetection = new Detector([
{
name: 'openDoors',
triggers: [{ name: 'mellon', defaultRefPath: '/mellon-assets/mellon_ref.json' }],
onMatch: () => console.log('opening the doors...')
},
{
name: 'startEngine',
triggers: [
{ name: 'start', defaultRefPath: '/mellon-assets/start_ref.json' },
{ name: 'go', defaultRefPath: '/mellon-assets/go_ref.json' }
],
onMatch: (triggerNameMatched, confidence) => {
console.log({ triggerNameMatched, confidence })
console.log('starting engine...')
}
},
{
name: 'stopEngine',
triggers: [
{ name: 'stop', defaultRefPath: '/mellon-assets/stop_ref.json' },
{ name: 'wait', defaultRefPath: '/mellon-assets/wait_ref.json' }
],
onMatch: (triggerNameMatched, confidence) => {
console.log({ triggerNameMatched, confidence })
console.log('stopping engine...')
}
}
])
await hotWordDetection.start() // opens the mic and listens for all registered triggersEnrolling custom words
import { Detector, EnrollmentSession, Storage } from 'mellon'
const hotwordDetection = new Detector([{
name: 'startEngine',
triggers: [{ name: 'start' }],
onMatch: (triggerNameMatched, confidence) => { console.log('starting engine...') }
}])
// 1. Create an enrollment session
const session = new EnrollmentSession('start')
// 2. Record at least 3 samples (1.5 s each)
await session.recordSample()
await session.recordSample()
await session.recordSample()
// Optionally remove a bad take (0-based index)
// session.deleteSample(1)
// 3. Generate reference embeddings
const ref = await session.generateRef()
// 4a. Use immediately in the running detector
hotwordDetection.addCustomWord(ref)
await hotwordDetection.start()
// 4b. Persist for future sessions
Storage.saveWord(ref)API reference
Detector
The easiest way to use the library. Wraps mic access, AudioWorklet wiring, and detector management into a single class.
class Detector {
constructor(commands: Command[], config?: MellonConfig)
readonly threshold: number // read/write; persisted in localStorage
readonly listening: boolean
init(): Promise<void>
start(): Promise<void>
stop(): Promise<void>
addCustomWord(ref: WordRef): void
// Storage helpers — static, work without a Detector instance
static loadWords(storageKey?: string): WordRef[]
static saveWord(ref: WordRef, storageKey?: string): void
static deleteWord(wordName: string, storageKey?: string): void
}Storage
Static helpers for persisting enrolled word references in localStorage.
class Storage {
static loadWords(storageKey?: string): WordRef[]
static saveWord(ref: WordRef, storageKey?: string): void
static deleteWord(wordName: string, storageKey?: string): void
}EnrollmentSession
Records audio samples from the mic (or uploaded files) and generates reference embeddings for a new custom word.
class EnrollmentSession {
constructor(wordName: string, config?: EnrollmentSessionConfig)
recordSample(): Promise<number> // records 1.5 s; returns new sample count
deleteSample(index: number): number // removes sample at index; returns new count
generateRef(): Promise<WordRef> // requires ≥ 3 samples
}WordRef shape
interface WordRef {
word_name: string // e.g. 'hello'
model_type?: string
embeddings: number[][] // N × 256 vectors
}Compatible with the EfficientWord-Net _ref.json format — you can import reference files generated by the Python toolkit directly.
Science behind the lib
Check out this paper.
License
MIT