Stop Double Typing: A Global AutoHotkey Debounce Fix for āChatteringā Keyboards
If youāve ever typed a clean sentence and then spotted a random extra letter - such as āthhisā, āspacceā, āhellooā - you already know the rage. Itās not always a software glitch, and itās not always āfat fingersā. Many keyboards (even premium ones) can develop a persistent issue over time: a single key press intermittently registers as two presses. My own recurring offender is a Logitech G915 X Lightspeed. Great board, great feel⦠and yet, the double-typing āchatterā can be maddening. I wanted a fix that: - Works globally (not just for a couple of keys) - Doesnāt break key-hold behavior (auto-repeat when holding a key down should still work) - Can be disabled automatically in specific apps (games, for example) - Is easy to tweak and maintain This article is the solution I ended up with: a global debouncer written for AutoHotkey v2, designed specifically to suppress rapid ābounceā double taps while letting normal holds pass through. What ādebounceā means (in human terms) When a keyboard āchattersā, Windows receives two near-identical key press events in a very short time spanāoften just a few milliseconds apart. If we set a threshold (for example 150 ms) and ignore the second press that arrives too quickly, we effectively ādebounceā the key. The trick is doing this without mistakenly blocking legitimate input, especially when a key is held down and Windows generates repeated keydown events (auto-repeat). How the script avoids the āhold keyā pitfall A naive debouncer will suppress repeated keydowns even when youāre holding the key. Thatās awful for gaming and also annoying for normal typing (holding Backspace, holding a character, etc.). My approach is simple: - Track whether a key is currently physically down (isDown map). - Only apply debounce logic when we detect a fresh tap (up > down key transitions). - If the key is already down, treat repeated keydowns as auto-repeat and pass them through. Result: accidental double-taps get blocked, key holds still behave normally. The AutoHotkey v2 script Requirements: AutoHotkey v2 installed. Save this as .ahk (e.g. Keyboard-Debounce.ahk) and run it. Tweakable settings: - debounceThreshold: debounce window in milliseconds (start at 150; adjust as needed) - ignoreWindows: apps where debounce should be disabled (e.g. games) - excludedSC: keys you explicitly do not want to debounce ; Keyboard-Debouncer v0.1 ; https://www.ryadel.com/en/keyboard-double-typing-fix-windows/ ; https://github.com/Ryadel/AutoHotkey-Scripts/scripts/Keyboard-Debouncer/ #Requires AutoHotkey v2.0 #UseHook true ; ========================= ; CONFIG ; ========================= global debounceThreshold := 150 ; milliseconds global lastTap := Map() ; last valid tap time per key global isDown := Map() ; physical down state per key global ignoreWindows := ; Only enable debounce when current window is NOT in ignore list HotIf(WinNotIgnored) ; Exclude modifier keys (recommended) and a few sensitive keys ; Note: "Canc" on IT keyboards often refers to Backspace in common usage. global excludedSC := Map( ; Modifiers "SC02A", true, ; LShift "SC036", true, ; RShift "SC01D", true, ; LCtrl "SC11D", true, ; RCtrl (extended) "SC038", true, ; LAlt "SC138", true, ; RAlt / AltGr (extended) "SC15B", true, ; LWin (extended) "SC15C", true, ; RWin (extended) "SC00E", true, ; Backspace (often called "Canc" by habit) "SC152", true, ; Insert "SC153", true ; Delete ) ; ========================= ; HANDLERS ; ========================= DebounceDown(*) { global debounceThreshold, lastTap, isDown hk := A_ThisHotkey sc := RegExReplace(hk, "^$?*?") ; "$*SC01E" -> "SC01E" ; If already physically down, it's auto-repeat: do NOT debounce if isDown.Has(sc) && isDown { Send("{Blind}{" sc "}") return } ; First real press (tap) isDown := true now := A_TickCount ; Suppress rapid second tap (bounce) if lastTap.Has(sc) && (now - lastTap "SC01E up" sc := RegExReplace(sc, "s+up$") ; "SC01E up" -> "SC01E" isDown := false } ; ========================= ; HOTKEY GENERATION ; ========================= ; Generate hotkeys for scancodes (broad coverage) Loop 0xFF { sc := Format("SC{:03X}", A_Index) if excludedSC.Has(sc) continue try { Hotkey("$*" sc, DebounceDown, "On") Hotkey("$*" sc " up", KeyUp, "On") } } ; ========================= ; WINDOW FILTER ; ========================= WinNotIgnored(*) { global ignoreWindows for pattern in ignoreWindows { if WinActive(pattern) return false } return true } How to install and run it (quick checklist) - Install AutoHotkey v2. - Create a file named KeyboardDebounceGlobal.ahk. - Paste the script above and save. - Double-click the file to run it (you should see the AHK icon in the tray). - Type normally and see if the double-typing disappears. Startup tip: If you want it to run at boot, place a shortcut in the Windows Startup folder. How I tune the threshold Every keyboard and every āchatterā pattern is different. My starting point is: - 150 ms if the issue is frequent and obvious - 100ā120 ms if it feels too aggressive - 180ā220 ms if the bounce is severe If you notice that legitimate fast double-taps (like āttā in a word) get eaten, lower the threshold. If chatter still slips through, raise it slightly. Excluding apps (important for games and latency-sensitive software) The ignoreWindows list disables debounce automatically whenever one of those windows is active. Add entries like: - "ahk_exe yourgame.exe" - "ahk_class SomeWindowClass" That way I keep my typing sane in Windows, but I donāt risk āweird input feelā inside games. Excluding specific keys (Backspace/Canc, Del, Ins) I deliberately exclude some keys from debounce because theyāre commonly used held down or in editing workflows. In the script, you can add/remove scancodes inside excludedSC. The defaults include: - Backspace: SC00E - Insert: SC152 - Delete: SC153 Limitations and honest expectations This is a software workaround. If your keyboard is physically failing, the ārealā fix is hardware repair/replacement. But if the issue is intermittent, hard to reproduce, or youāre trying to extend the usable life of a keyboard you love, this approach can be a surprisingly effective long-term bandage. In my case, it turned a chronic annoyance into something I simply donāt think about anymore: exactly what I wanted.














