How Romeo learned to answer to its own name

Romeo ships a custom-trained Hey Romeo wake word, picks the right model format for your hardware, and hands your voice off to capture without extra clicks.

A microphone with a purple light behind it

Romeo listens for its own name. The desktop app ships with a custom-trained "Hey Romeo" wake word instead of the stock phrases that come with open-source wake-word engines, so you can start talking to Romeo without touching the keyboard. The model is bundled inside the app in both ONNX and TFLite formats, and openWakeWord handles detection. A toggle in Settings under Preferences switches listening off completely, at the engine itself rather than only in the interface.

Every launch, the app writes four values to the agent backend's config: wake word enabled, openwakeword as the provider, the phrase "Hey Romeo", and the path to the bundled model. Before writing, it checks that the model file exists. The write runs every time, so the setup can never drift back to a generic wake word. Hearing the phrase does not open the mic by itself, so when the backend fires a wake.detected event, the app starts voice capture and flips the Record button to Stop.

Your hardware decides which model format loads. Apple Silicon Macs load the TFLite model, because openWakeWord's ONNX backend returns near-zero scores on macOS ARM64, a known upstream bug. Windows and Intel Macs use the ONNX file. The iPad companion view uses the same TFLite model through a separate Python detector. It reads 16 kHz mono audio in 80 ms frames and confirms a wake after two frames above a 0.6 score threshold, the same defaults the desktop listener uses.

Packaging caused the biggest snag. Electron bundles app files into an asar archive, and the wake-word engine runs as a separate process that can't read files inside it. The fix unpacks the wakewords folder at build time and resolves the model directory one way for packaged builds and another for source. A related bug appeared later on the iPad side: its detector looked for the model next to its own folder, which only works in source. Now the app passes the exact path through an environment variable, using the same directory logic.