{"id":20079691,"url":"https://github.com/mathquis/node-personal-wakeword","last_synced_at":"2025-10-24T16:34:09.492Z","repository":{"id":40704168,"uuid":"270010274","full_name":"mathquis/node-personal-wakeword","owner":"mathquis","description":"Personal wake word detector","archived":false,"fork":false,"pushed_at":"2023-07-12T01:39:51.000Z","size":105,"stargazers_count":63,"open_issues_count":3,"forks_count":8,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-22T20:51:45.390Z","etag":null,"topics":["dtw","hotword-detection","hotword-detector","mfcc","node","wakeword"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mathquis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-06-06T14:45:18.000Z","updated_at":"2025-02-19T18:44:16.000Z","dependencies_parsed_at":"2024-11-13T15:37:58.269Z","dependency_job_id":null,"html_url":"https://github.com/mathquis/node-personal-wakeword","commit_stats":{"total_commits":62,"total_committers":4,"mean_commits":15.5,"dds":"0.16129032258064513","last_synced_commit":"53a700c499bf6d9072f9e31a31c9d94924b71284"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathquis%2Fnode-personal-wakeword","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathquis%2Fnode-personal-wakeword/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathquis%2Fnode-personal-wakeword/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mathquis%2Fnode-personal-wakeword/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mathquis","download_url":"https://codeload.github.com/mathquis/node-personal-wakeword/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252592777,"owners_count":21773332,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dtw","hotword-detection","hotword-detector","mfcc","node","wakeword"],"created_at":"2024-11-13T15:24:07.077Z","updated_at":"2025-10-24T16:34:09.428Z","avatar_url":"https://github.com/mathquis.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# node-personal-wakeword\n\nBased on https://medium.com/snips-ai/machine-learning-on-voice-a-gentle-introduction-with-snips-personal-wake-word-detector-133bd6fb568e\n\n### Installation\n\n```bash\nnpm i @mathquis/node-personal-wakeword\n```\n\n### Usage\n\n```javascript\nconst WakewordDetector = require('@mathquis/node-personal-wakeword')\nconst Mic              = require('mic')\nconst Stream           = require('stream')\n\nasync function main() {\n  // Create a new wakeword detection engine\n  let detector = new WakewordDetector({\n    /*\n    sampleRate: 16000,\n    bitLength: 16,\n    frameShiftMS: 10.0,\n    frameLengthMS: 30.0, // Must be a multiple of frameShiftMS\n    vad: true, // Use VAD detection\n    vadMode: WakewordDetector.VadMode.AGGRESSIVE, // See node-vad modes\n    vadDebounceTime: 500,\n    band: 5, // DTW window width\n    ref: 0.22, // See Snips paper for explanation about this parameter\n    preEmphasisCoefficient: 0.97, // Pre-emphasis ratio\n    */\n    threshold: 0.5 // Default value\n  })\n\n  // *****\n\n  // KEYWORD MANAGEMENT\n\n  // Add a new keyword using multiple \"templates\"\n  await detector.addKeyword('alexa', [\n    // WAV templates (trimmed with no noise!)\n    './keywords/alexa1.wav',\n    './keywords/alexa2.wav',\n    './keywords/alexa3.wav'\n  ], {\n    // Options\n    disableAveraging: true, // Disabled by default, disable templates averaging (note that resources consumption will increase)\n    threshold: 0.52 // Per keyword threshold\n  })\n\n  // Keywords can be enabled/disabled at runtime\n  detector.disableKeyword('alexa')\n  detector.enableKeyword('alexa')\n\n  // *****\n\n  // EVENTS\n\n  // The detector will emit a \"ready\" event when its internal audio frame buffer is filled\n  detector.on('ready', () =\u003e {\n    console.log('listening...')\n  })\n\n  // The detector will emit an \"error\" event when it encounters an error (VAD, feature extraction, etc.)\n  detector.on('error', err =\u003e {\n    console.error(err.stack)\n  })\n\n  // The detector will emit a \"vad-silence\" event when no voice is heard\n  detector.on('vad-silence', () =\u003e {\n    console.log('Hearing silence...')\n  })\n\n  // The detector will emit a \"vad-voice\" event when it hears a voice\n  detector.on('vad-voice', () =\u003e {\n    console.log('Hearing voices...')\n  })\n\n  // The detector will emit a \"data\" event when it has detected a keyword in the audio stream\n  /* The event payload is:\n    {\n      \"keyword\"     : \"alexa\", // The detected keyword\n      \"score\"       : 0.56878768987, // The detection score\n      \"threshold\"   : 0.5, // The detection threshold used (global or keyword)\n      \"frames\"      : 89, // The number of audio frames used in the detection\n      \"timestamp\"   : 1592574404789, // The detection timestamp (ms)\n      \"audioData\"   : \u003cBuffer\u003e // The utterance audio data (can be written to a file for debugging)\n    }\n  */\n  detector.on('data', ({keyword, score, threshold, timestamp}) =\u003e {\n    console.log(`Detected \"${keyword}\" with score ${score} / ${threshold}`)\n  })\n\n  // Note that as the detector is a transform stream the standard \"data\" event also works...\n  // I just added the \"keyword\" event for clarity :)\n\n  // *****\n\n  // STREAMS\n\n  // As an alternative to events, the detector is a transform stream that takes audio buffers in and output keyword detection payload\n  const detectionStream = new Stream.Writable({\n    objectMode: true,\n    write: (data, enc, done) =\u003e {\n      // `data` is equivalent to \"data\" event payload\n      console.log(data)\n      done()\n    }\n  })\n\n  detector.pipe(detectionStream)\n\n  // *****\n\n  // Create an audio stream from an audio recorder (arecord, sox, etc.)\n  let recorder = Mic({\n    channels      : detector.channels, // Defaults to 1\n    rate          : detector.sampleRate, // Defaults to 16000\n    bitwidth      : detector.bitLength // Defaults to 16\n  })\n\n  let stream = recorder.getAudioStream()\n\n  // Pipe to wakeword detector\n  stream.pipe(detector)\n\n  recorder.start()\n\n  // Destroy the recorder and detector after 10s\n  setTimeout(() =\u003e {\n    stream.unpipe(detector)\n    stream.removeAllListeners()\n    stream.destroy()\n    stream = null\n\n    recorder = null\n\n    detector.removeAllListeners()\n    detector.destroy()\n    detector = null\n  }, 10000)\n}\n\nmain()\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmathquis%2Fnode-personal-wakeword","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmathquis%2Fnode-personal-wakeword","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmathquis%2Fnode-personal-wakeword/lists"}