|
| Platform | Requirement |
|---|---|
| Windows 10/11 | No additional software. Uses built-in System.Speech.Recognition |
| macOS | Node.js 22 or later (LTS) for the speech engine child process |
| Linux | Node.js 22 or later (LTS) for the speech engine child process |
On macOS and Linux a local speech model (~17MB) is downloaded on first use and cached. If Node.js is installed via nvm or fnm and not on VS Code's PATH, set wakeWord.nodePath to the full path of your node executable.
First Run Consent
The first time the extension tries to listen, a modal dialog explains exactly what happens: continuous microphone use, fully local processing. You must click "Allow Microphone Listening" to proceed.
If you decline, listening does not start. You can enable it any time via the status bar or command palette, which will re-prompt for consent. Reset the consent prompt with Wake Word: Reset Microphone Consent.
Wake Phrase Routing
The core feature. Each spoken phrase maps to a VS Code command.
Default routes
These work out of the box with no configuration:
| You say | What opens | Command |
|---|---|---|
| "Hey Copilot" | GitHub Copilot Chat | workbench.action.chat.open |
| "Hey Claude" | Claude Code | claude-vscode.focus |
| "Hey Computer" | Terminal | workbench.action.terminal.focus |
The Claude route uses manual handoff (see Handoff mode): after it fires, listening stays paused until you click Wake: Paused in the status bar. The other two resume after the cooldown.
Custom routes
Add your own phrases in settings.json. Any spoken English phrase works:
{
"wakeWord.routes": [
{
"label": "Copilot",
"phrase": "hey copilot",
"command": "workbench.action.chat.open"
},
{
"label": "Claude",
"phrase": "hey claude",
"command": "claude-vscode.focus"
},
{
"label": "Search",
"phrase": "search files",
"command": "workbench.action.quickOpen"
},
{
"label": "Commands",
"phrase": "open commands",
"command": "workbench.action.showCommands"
}
]
}
The speech engine uses a constrained grammar built from your configured phrases for accurate matching.
Phrase aliases
The phrase field accepts a string or an array of strings. Use arrays to map multiple trigger phrases to the same command:
{
"label": "Claude",
"phrase": ["hey claude", "open claude"],
"command": "claude-vscode.focus"
}
Per-route cooldown
Override the global cooldown for individual routes with cooldownSeconds:
{
"label": "Terminal",
"phrase": "hey computer",
"command": "workbench.action.terminal.focus",
"cooldownSeconds": 10
}
Handoff mode
handoff chooses how listening comes back after a route fires:
"timer"(default): listening resumes aftercooldownSeconds."manual": listening stays paused until you click the status bar or run Wake Word: Enable Listening. Use this for assistants whose voice sessions run longer than the cooldown, so Wake Word does not restart under them and compete for the microphone.
{
"label": "Claude",
"phrase": "hey claude",
"command": "claude-vscode.focus",
"handoff": "manual"
}
The status bar shows Wake: Paused while a manual route waits. The default Claude route uses manual handoff.
The handoff
When a wake phrase is detected:
- The extension asks the speech engine to close the microphone and waits for it to confirm, then kills the process (forcing it after 500 ms if no confirmation arrives)
- The target VS Code command fires (opening the assistant)
- The assistant's voice mode takes over the microphone with no contention
- After
wakeWord.cooldownSeconds(default: 30), wake word listening resumes. A route withhandoff: "manual"waits for you instead - Status bar shows a live countdown (
Wake: 30s → Wake: 29s → ...) during handoff, then returns to "Wake: Listening". A manual route shows "Wake: Paused" instead, with no countdown
This ensures only one thing uses the mic at a time.
Multiple windows
If you have more than one editor window open, only one listens at a time. The first window to start listening takes a lock; the others show Wake: Other window in the status bar and stand by. When the listening window closes, crashes, or has listening disabled, a standing-by window takes over within about ten seconds. The lock is held through the cooldown after a detection, so a second window never opens the microphone while an assistant has it.
The lock lives in the extension's global storage, which windows of the same editor share. Windows of different editor products do not see each other's lock.
Settings
| Setting | Default | Description |
|---|---|---|
wakeWord.routes |
[] |
Wake phrase routing table. Uses defaults if empty. |
wakeWord.cooldownSeconds |
30 |
Seconds to pause after handoff before resuming. Routes with handoff: "manual" wait for you instead |
wakeWord.enableOnStartup |
true |
Start listening when the editor opens |
wakeWord.showNotificationOnDetection |
true |
Show notification when wake phrase is heard |
wakeWord.pauseOnFocusLoss |
false |
Pause listening when the editor loses focus, resume on regain |
wakeWord.confidenceThreshold |
0.3 |
Minimum confidence score (0.1–0.9) for wake phrase detection |
wakeWord.confirmationMode |
false |
Require the wake phrase twice within 5 seconds before triggering. Reduces false positives in noisy environments. |
wakeWord.engine |
auto |
Speech engine: auto (platform default), windows (System.Speech), or sherpa (cross-platform) |
wakeWord.nodePath |
"" |
Path to Node.js executable. Leave empty to auto-detect. Set this if the engine cannot find Node.js (macOS/Linux with nvm or fnm). |
wakeWord.audioDevice |
"" |
Microphone to use: a case-insensitive substring of the device name (e.g. "USB") or a device index. Empty for the system default. Sherpa engine only. |
Choosing a microphone
wakeWord.audioDevice selects the input device for the sherpa engine. Use any case-insensitive substring of the device name as it appears in your system sound settings, or the device's index number:
{
"wakeWord.audioDevice": "Blue Yeti"
}
Changing it restarts the engine. If the value matches no device, or more than one, the error notification says so and names the value. The Windows engine (System.Speech) always uses the system default input device, so set wakeWord.engine to sherpa to choose a device on Windows.
Calibrating
Wake Word: Calibrate listens for 15 seconds and logs every wake phrase it hears, with the time and, on the Windows engine, the confidence score, without firing any route. Say each of your phrases a few times at your normal distance, then open the Wake Word output channel: the summary groups detections by phrase with the count, the average confidence, and the minimum. No detections means the microphone, the room, or the threshold needs attention; a minimum close to wakeWord.confidenceThreshold means that phrase is on the edge. The sherpa engine reports no score, so its summary has counts only. Whatever the extension was doing before, listening, a cooldown, or a manual pause, is put back afterwards.
Commands
- Wake Word: Enable Listening -- start the detector
- Wake Word: Disable Listening -- stop the detector
- Wake Word: Toggle Listening -- toggle on/off (also via status bar click)
- Wake Word: Reset Microphone Consent -- clear consent and re-prompt
- Wake Word: Open Settings -- open the Settings editor filtered to Wake Word (also linked from the status bar tooltip)
- Wake Word: Calibrate -- listen for 15 seconds and log what is heard without firing any route (see Calibrating)
Common command IDs
Useful values for the command field in your routes. Command IDs listed are for VS Code. Cursor and other editors may use different IDs for the same features.
| Assistant / Feature | Command ID |
|---|---|
| GitHub Copilot Chat | workbench.action.chat.open |
| Claude Code | claude-vscode.focus |
| VS Code Speech dictation | workbench.action.editorDictation.start |
| Command Palette | workbench.action.showCommands |
| Focus Terminal | workbench.action.terminal.focus |
| Quick Open | workbench.action.quickOpen |
| Toggle Sidebar | workbench.action.toggleSidebarVisibility |
| New File | workbench.action.files.newUntitledFile |
How It Works (Technical)
The extension selects a speech engine based on platform (or the wakeWord.engine setting) and spawns it as a background child process. Both engines communicate via stdout using the same protocol: READY, DETECTED:<phrase>|<confidence>, ERROR:<message>, DEBUG:<info>. The sherpa engine also sends RELEASED once it has closed the microphone.
Windows engine (default on Windows)
Spawns a PowerShell process using System.Speech.Recognition.SpeechRecognitionEngine, the same engine built into Windows. A constrained grammar is built from your configured phrases and passed via encoded command. Zero model downloads; the speech engine ships with Windows.
Sherpa engine (default on macOS/Linux, optional on Windows)
Spawns audio-engine.js under system Node.js (not Electron). The child process uses decibri for mic capture and sherpa-onnx for keyword spotting. Running under system Node.js is required because Electron's Node.js runtime cannot load native audio addons. A local speech model (~17MB) is downloaded to VS Code's global storage on first use and cached.
Captured audio passes through voice activity detection (Silero VAD) before it reaches the keyword spotter, so the spotter only runs while someone is speaking and an idle editor does not decode silence. decibri also conditions the signal on the way through: DC offset removal, an 80 Hz high-pass to drop rumble below the voice band, and automatic gain control targeting -18 dBFS so the confidence threshold sees a consistent level.
Shared flow
- Extension builds phrase list and spawns the engine process
- Engine writes
READYwhen the mic is open - Each detection above the confidence threshold is written to stdout as
DETECTED:<phrase>|<confidence> - The extension reads stdout and fires the corresponding command
- On handoff, the engine process is killed to release the microphone
- After the cooldown countdown, a new engine process starts
Zero runtime npm dependencies in the extension host. All native dependencies are isolated in the engine/ child process.
Troubleshooting
| Problem | Solution |
|---|---|
| Engine starts but never detects phrases | Run Wake Word: Calibrate to see what the engine hears. Try lowering wakeWord.confidenceThreshold (e.g. 0.2). Speak clearly and close to your microphone. |
| Too many false positives | Enable wakeWord.confirmationMode, which requires the phrase twice within 5 seconds: say it, pause about three seconds, say it again. The status bar shows Wake: Confirm between the two. Also try raising wakeWord.confidenceThreshold (e.g. 0.5 or higher) and using longer, more distinctive wake phrases. |
| Listening does not resume after a wake phrase | The route uses handoff: "manual", which the default Claude route does. Click Wake: Paused in the status bar or run Wake Word: Enable Listening. Set handoff to "timer" on that route to resume after the cooldown instead. |
| "Failed to start speech engine" | Ensure your microphone is connected and not in use by another application. Check your system sound settings. |
| Status bar shows "Wake: Error" | Click the status bar item to retry. Check the Output panel for details. If the error persists, try Wake Word: Reset Microphone Consent and re-enable. |
| Extension keeps restarting | The engine retries up to 3 times on crash with increasing delays. If it fails after 3 retries, check that your audio device is working. |
| "Could not find Node.js" (macOS/Linux) | Set wakeWord.nodePath to the full path of your node executable (e.g. /opt/homebrew/bin/node). Common when using nvm or fnm. |
| Microphone access denied (macOS) | Open System Settings → Privacy & Security → Microphone and enable access for VS Code (or your editor). |
| Model download fails | Check your internet connection. The model is ~17MB downloaded from GitHub. If behind a proxy, ensure HTTPS traffic to github.com is allowed. |
| High CPU while idle (macOS/Linux) | The sherpa engine gates keyword spotting on voice activity detection, so a quiet room should cost close to nothing. Sustained CPU with no one speaking usually means a noisy input: check the correct microphone is selected and lower its input gain. |
| Status bar shows "Wake: Other window" | Another window of the same editor is listening. Only one listens at a time, and this window takes over automatically when that one stops. To move listening here now, disable it in the other window. |
| Wrong microphone is used | With the sherpa engine, set wakeWord.audioDevice to part of the device's name (e.g. "USB") or its index. The Windows engine always uses the system default input; change that in Windows sound settings. |
| "No microphone matching ... was found" | The wakeWord.audioDevice value did not match any input device. Compare it with the device names in your system sound settings, or clear it to use the default. |
Privacy
All speech recognition runs locally on your machine. No audio data ever leaves your device. On Windows, speech is processed by the built-in System.Speech.Recognition engine in memory. On macOS and Linux, a local sherpa-onnx model processes audio in the engine child process. Nothing is recorded, stored, or transmitted.
Platform Support
| Platform | Status | Engine |
|---|---|---|
| Windows 10/11 | Supported | Windows built-in System.Speech |
| macOS | Supported | sherpa-onnx (requires Node.js 22 or later) |
| Linux | Supported | sherpa-onnx (requires Node.js 22 or later) |
Compatibility
| Editor | Install method |
|---|---|
| VS Code | Marketplace or code --install-extension analytics-in-motion.wake-word |
| Cursor | Open VSX or .vsix from GitHub Releases |
| Windsurf | Open VSX or .vsix from GitHub Releases |
| Other VS Code forks | .vsix from GitHub Releases |
License
Apache 2.0