A click on every keystroke — the sound of a mechanical keyboard, on
the keyboard you already have.
A small desktop app that plays a short click each time you press a key,
anywhere on the system.
An app that listens to your keyboard everywhere earns suspicion, so here is exactly what it does and does not do.
It never learns which keys you press. The listener reads only the raw hardware key code, and only long enough to tell a fresh press from a held-down key repeating. It never resolves that code into a letter, never assembles words, and never records a sequence.
Nothing leaves your computer. There is no network code in the app at all — no analytics, no telemetry, no logging of keystrokes to disk. The key code is used to decide "play a click or not," and then it is gone.
It watches, it never intercepts. On both platforms the hook is installed in listen-only mode and passes every keystroke straight through, so it cannot alter or swallow what you type.
The whole listener is one short file you can read in a few minutes:
src-tauri/src/keylistener.rs.
- A click on every key, system-wide — in any app, not just this window.
- Three built-in sounds, generated as clean WAVs, plus a volume control.
- Your own sounds. Drop a WAV, MP3, OGG or FLAC onto the window and it joins the list, kept across restarts.
- Held keys click once. Holding a key down plays one click, not a machine-gun burst, because auto-repeat is filtered out.
- Lives in the system tray. Closing the window hides it there rather than quitting; toggle the clicks or quit from the tray menu.
- Start with the system, optionally, via the autostart toggle.
A Tauri app: a Rust core does the real work, a React window is the control panel.
src-tauri/src/
├── keylistener.rs Global key hook — Windows and macOS, listen-only
├── audio.rs Low-latency playback, debounced, on its own thread
├── lib.rs Tray, window-to-tray behaviour, IPC commands, autostart
└── main.rs Entry point
src/
├── App.tsx The window: toggle, volume, sound picker, drop zone
└── components/ ToggleButton, VolumeSlider, SoundPicker, DropZone, …
Each key press travels from the OS hook, over an mpsc channel, to a dedicated
audio thread that plays the current sound. Keeping the hook callback tiny
matters: the OS gives a low-level keyboard hook only a few hundred milliseconds
to return before it silently removes it, so the callback does nothing but note
the event and hand it off.
Platform listeners
| OS | Mechanism | Why |
|---|---|---|
| Windows | WH_KEYBOARD_LL low-level hook |
The standard system-wide keyboard hook |
| macOS | CGEventTap, listen-only |
Avoids the TSMGetInputSourceProperty crash that rdev triggers by resolving key names — which this app never needs |
You need Rust, Node.js, and the platform toolchain Tauri needs — on Windows that is the MSVC Build Tools, the Windows SDK and WebView2 (WebView2 ships with Windows 11). See Tauri's prerequisites for the full list.
git clone https://github.com/burakSahinkaya/KeyClickSoundApp.git
cd KeyClickSoundApp
npm install
npm run tauri dev # run it
npm run tauri build # produce an installer in src-tauri/target/release/bundle/On macOS the first run needs keyboard permissions: System Settings → Privacy & Security, granting both Accessibility and Input Monitoring (in dev, grant them to your terminal). Windows needs nothing — the hook installs on launch.
The built-in click sounds are generated rather than recorded; the small Rust
program in generate_sounds/ writes the WAVs in
src-tauri/sounds/. Regenerate them with cargo run from that folder.
MIT.
