The DJI Mic Mini's pairing button sends no data — not over USB, not over Bluetooth. DJI Click gives it a job anyway: it listens to the mic's own capsule, recognises the mechanical click of the button by sound, and posts mouse button 4 or 5. A wireless remote you already own, clipped to your shirt.
Bind it to push-to-talk, dictation (Wispr Flow), an OBS scene switch — anything that takes a mouse button.
A shirt clip, a desk, a keyboard, a cough — the room is full of transients that look like clicks. DJI Click runs three gates in series, cheapest first:
| Gate | What it does | Cost |
|---|---|---|
| Amplitude + hysteresis | Quiet audio is never analysed; one click can't split into phantom onsets | ~0 |
| Physical prior | A fixed description of a plastic dome-switch snap (rise ≤ 7 ms, spectral centroid, crest, kurtosis) — derived from 114 verified clicks vs 14,000 room sounds. Rejects ~96% of the environment before any model runs | tiny |
| Learned classifier | A 13-feature logistic regression that separates your click from your room, trained in a 30-second calibration | small |
A press/release doublet gate then confirms the pair before any mouse event fires. Total load: about 1% of one core. The realtime audio thread only copies samples; every bit of DSP runs on a worker thread.
Menu bar → Calibrate… → press the button ten times → press five more to verify. Done.
The wizard is transactional: nothing touches disk until you finish, and closing the window rolls everything back. While you calibrate, the physical prior counts only real clicks — the window shows "ignored 14 other sounds" as proof it isn't guessing. Mark any press as "real", "false", or "it missed one" later and the model retrains itself in seconds, with one-click revert.
Each input device gets its own profile — model, threshold, and training data switch together. Plug the receiver in and out freely; detection resumes by itself.
Bluetooth microphones are excluded on purpose. Mic capture over classic Bluetooth is an HFP/SCO voice link, and SCO reserves radio slots at the baseband — your Bluetooth keyboard and mouse lag the entire time, and no macOS setting changes that. The USB receiver carries the same audio at 48 kHz with zero radio cost, so that's the path.
git clone https://github.com/paperfoot/dji-click.git
cd dji-click
./install.shGrant Microphone and Accessibility when macOS asks. That's it — the ripple icon appears in your menu bar, already listening.
app/
├── djidetect.py detector — DSP, physical prior, classifier, per-device
│ profiles, calibration state machine, JSON+SSE API :8770
├── djiclickbar.py menu bar item; supervises the detector process
├── nativeui.py native settings window (AppKit)
├── wizardui.py native calibration wizard (AppKit)
├── audiodev.py device enumeration, USB/Bluetooth transport detection
└── model48.pkl factory model, replaced by your calibration
The detector is a separate process behind a localhost JSON API; the windows are thin clients over it. A UI fault can never stop detection.
Hard-won facts, in case you build something similar: click rise time is invariant to how you press (2.08 vs 2.09 ms across thumb, nail, and moving-hand presses), Bluetooth's mSBC codec destroys the waveform phase that transient detection needs, and a fixed refractory period splits one click into phantom onsets — hysteresis is the fix.
- macOS only (CoreGraphics event injection, AppKit UI).
- Unsigned — first launch needs right-click → Open, or build your own trust.
- The mic must be able to hear the button; a deadcat windscreen is fine down to about −6 dB, heavy wind protection is not.
If this saved your pairing button from a life of idleness, star the repo — and follow @longevityboris for more of the same.
Built by Boris Djordjevic at Paperfoot AI.

