thalovant/mirror-wakeword
The wake word "Mirror", said once, in English or with a French, Spanish, German or Italian accent.
Not recommended. At its default threshold it finds 58 % of the English wake words over the 18 stress conditions (72 % after silence, 28 % with someone talking over it) for 0.21 false wakes per hour on films as they are and 0.74 across a room. At 0.3 in-room false wakes per hour v1 found 34 % of the English wake words, v2 58 %.
It is a 0.9 MB ONNX head (two stateful GRU heads averaged in one graph) trained with wakeforge on the
streamed features of TigreGotico/wakehubert-tiny (int8), with
the recipe and the tests of thalovant/thalovant-wakeword
(v5's stateful heads, v3's tests), so every number below is comparable with that card's. This is v2 (main, git
tag v2; v1 stays at tag v1).
A stateful head needs software with the stateful-GRU path: thalovant-voice 0.12.6 or later, or ovos-ww-plugin-wakeforge's stateful-GRU branch; the released plugin and the Android and iOS apps (1.0.6) cannot load it and should keep tag v1. It is one of six mirror
wake words published together: mirror,
mirror-mirror,
oh-mirror-mirror,
miroir,
miroir-miroir,
oh-miroir-miroir.
Use
Thalovant
Desktop and Raspberry Pi satellite (thalovant-voice 0.12.6 or later), voice.yaml:
wake:
word: https://huggingface.co/thalovant/mirror-wakeword/resolve/v2/mirror.onnx
sha256: e933cb93fe0b81d1bff69085958329cd073eb0d0a05803354b190b29a0f05a0e
Android and iOS apps: Settings โ Wake word โ "Your own modelโฆ", then this file. The model carries the threshold it was validated at, which the satellite and the apps count as 0.5 on their sensitivity scale.
OpenVoiceOS
pip install --pre "ovos-ww-plugin-wakeforge>=0.18.0a1"
mycroft.conf:
{
"listener": {"wake_word": "mirror"},
"hotwords": {
"mirror": {
"module": "ovos-ww-plugin-wakeforge",
"model": "https://huggingface.co/thalovant/mirror-wakeword/resolve/v2/mirror.onnx",
"listen": true,
"active": true
},
"hey_mycroft": {"active": false}
}
}
The plugin downloads the model once, fetches the featurizer the model names at a pinned revision, and uses the model's own threshold. On x86 PCs without AVX-512 the released plugin scores up to 0.07 off until OpenVoiceOS/ovos-ww-plugin-wakeforge#55 is merged; ARM boards, Apple silicon and AVX-512 CPUs score exactly.
Threshold
The default, 0.4729, makes 0.3 false wakes per hour on 73 h of in-room audio (16 + 28 held-out films and 20 h of English and French YouTube streamed through the plugin, every wake recorded with the plugin never firing): one candidate wake every three hours or so of television speech, the same for all six mirror words. v1 set its threshold on development speech instead, which once counted right put the six models between 0.4 and 0.8 in-room false wakes per hour (see Versions).
| in-room false wakes/h | threshold |
English wake words found (18 conditions) | all voices |
|---|---|---|---|
| 0.1 | 0.6118 | 51 % | 51 % |
| 0.2 | 0.5352 | 55 % | 56 % |
| 0.3 (default) | 0.4729 | 58 % | 60 % |
| 0.5 | 0.4238 | 61 % | 63 % |
How good it is
Every number is on audio the model never trained on: speakers, voices, videos, rooms, noises and music tracks
held out of training. Raw reports: stress_recall.json, stress_recall_clean.json,
stress_false_wakes.json, films.json, films_room.json,
plugin_validation.json, cross_accept.json,
pause_test.txt.
False wakes at the default threshold, streamed through ovos-plugin-manager as OVOS runs it, the plugin at this very threshold (a recording at a lower floor hides the peak of every climb, since the plugin resets after a detection; v1's film rows were recorded that way and under-counted) (198 h in all, 62 false wakes, 0.31/h; Thalovant v3 on the same audio: 0.40/h). 2 of them were somebody saying "mirror" (the video's subtitles hold it); the rest are misfires on other speech.
| audio | hours | false wakes | per hour |
|---|---|---|---|
| read speech (MLS, LibriSpeech test) + AudioSet | 70 | 26 | 0.37 |
| YouTube videos (YODAS2, 7 languages) | 72 | 22 | 0.31 |
| European Parliament debates (VoxPopuli, 8 languages) | 36 | 10 | 0.28 |
| music, with and without vocals (MUSAN) | 20 | 4 | 0.20 |
| 16 held-out feature films (public domain, 1932-1968) and Blender open movies, the soundtrack as is | 19 | 4 | 0.21 |
| the same films played by a TV across a room (test room responses, room noise) | 19 | 14 | 0.74 |
Raw: films.json, films_room.json (every wake from 0.3 up, the plugin never firing while recording).
Wake words found at the default threshold, each held-out clip in each condition (edge-tts voices excluded from training and clips voice-converted onto unseen real speakers, 425 clips whose rendering a phoneme model confirms says the phrase; v1 counted conversions of rejected renderings as misses; en, fr, es, de, it). Mean over the 18 conditions: 60 % (Thalovant v3, all its clips: 77 %); English voices 58 %, the other languages' voices 62 %.
| condition | found | Thalovant v3 |
|---|---|---|
| after silence | 72 % | 91 % |
| speech right before it, no pause | 71 % | 78 % |
| a command right after it | 71 % | 89 % |
| in the middle of a sentence | 67 % | 71 % |
| far side of a real room (-36 dBFS, room noise) | 79 % | 78 % |
| noise, 10 dB SNR | 65 % | 88 % |
| noise, 5 dB SNR | 57 % | 82 % |
| noise, 0 dB SNR | 51 % | 69 % |
| music with vocals, 10 dB | 66 % | 86 % |
| music with vocals, 5 dB | 47 % | 70 % |
| another person talking over it, 5 dB | 28 % | 42 % |
| phone line (8 kHz G.711) | 68 % | 82 % |
| Opus at 12 kbit/s | 71 % | 88 % |
| said fast (x1.25) | 56 % | 84 % |
| said slowly (x0.8) | 72 % | 83 % |
| quiet speaker (-48 dBFS) | 49 % | 74 % |
| clipped microphone | 45 % | 60 % |
| pitch +4 semitones (child-like) | 50 % | 73 % |
Through the OVOS plugin (30-minute mixes of speech, noise and silence, 1280-sample chunks): 62 % of 159 wake words found, 6 of 88 near misses accepted, 0 false activations; a detection comes a median 92 ms after the end of the word (90 % within 154 ms); 2.0 ms of CPU per second of audio on the training machine (one thread).
Which phrase wakes it
Held-out clips of each of the six phrases (edge-tts test voices and their real-speaker conversions whose rendering passes the phrase's phoneme check, after 1.5 s of silence, through the plugin), at the default threshold:
| said | Mirror | Mirror, mirror | Oh, mirror, mirror | Miroir | Miroir, miroir | ร miroir, miroir |
|---|---|---|---|---|---|---|
| woken | 80 % | 89 % | 84 % | 13 % | 14 % | 7 % |
Training
Positives: synthetic speech of the phrase (English voices in every accent the engines have, and French, Spanish, German and Italian voices reading the English): OmniVoice including child voices, edge-tts, Piper, Kokoro/StyleTTS2, PocketTTS, Supertonic, Magpie, Google; every clip once more voice-converted (Chatterbox) onto an MLS or LibriSpeech speaker; every clip checked by a phoneme model (WakePhoneHuBERT, cutoff -0.25).
Negatives: MLS and LibriSpeech speech, 112 h of YouTube videos (YODAS2, CC BY 3.0; other videos than the test's), music (MUSAN, other artists than the test's), AudioSet; near-miss words and phrases people say ("Mira", "error", "Mary, Mary", "mirror image"...), and the 2 s before every detection of a first model on that YouTube and music audio (those that say the phrase itself left out).
Real-room conditions in training: speech right before and after the word, another talker over it, room responses up to 1.2 s with their tail (MIT IR survey, RWCP, simulated rooms), quieter audio.
Two stateful GRU heads (128 units, one layer) distilled from the word's window ensemble (the four-head v2 ensemble: v1's two heads and two retrained without the near-homophone decoys) on its training streams (knowledge distillation, wakeforge
stream_heads.py fit --head gru), averaged in one graph (ens_gru.py); each head's epoch chosen on 97 h of development speech that includes 60 h of other YouTube videos; the threshold set on in-room audio (see Threshold).
Versions
- v2 (
main, git tagv2, 2026-11-10): two stateful GRU heads distilled from the window ensemble replace the window pair; at 0.3 in-room false wakes per hour they find 58 % of the English wake words against v1's 34 % (55 % against 15 % at 0.2/h). The near-miss decoys "Mira", "Meera", "Myra", "Mere" and "Nearer" (near-homophones of "mirror" in non-rhotic accents) no longer train the new heads: they had taught v1 to half-reject the word itself. The threshold is now set on in-room audio (v1: 0.3 false wakes/h on development speech, which put the six models at very different in-room rates). The numbers above come from a corrected harness: false wakes recorded with the plugin at the model's own threshold (a lower recording floor hid 60-90 % of them at the operating point), recall on clips whose rendering really says the phrase. - v1 (git tag
v1, 2026-11-06): two window heads (with and without voice-converted clips) averaged, threshold at 0.3 false wakes/h on development speech.
Limits
- Every test voice is synthetic or voice-converted; nobody has recorded "Mirror" for the tests yet.
- Someone talking over the word at a similar level (a loud TV) is the hardest case (28 % found).
- The single word wakes whenever somebody says it, in any sentence: the model decides as the word ends, before it can hear what follows.
Model tree for thalovant/mirror-wakeword
Base model
facebook/hubert-base-ls960