Skip to content

Keyboard Tester

Press every key to check it works — see key codes, held keys and rollover/ghosting.

  • 📱 Device Tool
  • ✓ Free
  • ✓ No sign-up
  • ✓ Private — runs in your browser
  • ✓ Any device
'},{c:'Slash',l:'/',sub:'?'}, {c:'ShiftRight',l:'⇧ Shift',w:2.75} ], [ {c:'ControlLeft',l:'Ctrl',w:1.4}, {c:'MetaLeft',l:'⌘ Meta',w:1.3}, {c:'AltLeft',l:'Alt',w:1.3}, {c:'Space',l:'Space',w:6.4}, {c:'AltRight',l:'Alt',w:1.3}, {c:'MetaRight',l:'⌘',w:1.3}, {c:'ContextMenu',l:'☰',w:1.3}, {c:'ControlRight',l:'Ctrl',w:1.4} ] ], // navigation / arrows cluster (rendered separately on wide screens) navRows: [ [{c:'Insert',l:'Ins'},{c:'Home',l:'Home'},{c:'PageUp',l:'PgUp'}], [{c:'Delete',l:'Del'},{c:'End',l:'End'},{c:'PageDown',l:'PgDn'}], [], [{c:'',l:'',spacer:true},{c:'ArrowUp',l:'↑'},{c:'',l:'',spacer:true}], [{c:'ArrowLeft',l:'←'},{c:'ArrowDown',l:'↓'},{c:'ArrowRight',l:'→'}] ], // ---- runtime state ---- held: {}, // code -> true while physically down tested: {}, // code -> true once ever pressed chatter: {}, // code -> count of suspicious re-triggers (key chatter) _lastUpAt: {}, // code -> timestamp of last keyup (for chatter detection) last: null, // { key, code, keyCode, which, location, repeat } maxRollover: 0, // most simultaneous keys ever registered presses: 0, // total keydown (non-repeat) events listening: false, capsOn: false, numpadEnabled: false, // toggle to show numpad in the testable set everCelebrated: false, // listeners _down: null, _up: null, _blur: null, init(){ this._down = (e) => this.onDown(e); this._up = (e) => this.onUp(e); this._blur = () => this.clearHeld(); window.addEventListener('keydown', this._down); window.addEventListener('keyup', this._up); window.addEventListener('blur', this._blur); // restore tested set + best rollover try { const t = localStorage.getItem('keyboard-tester:tested'); if(t) this.tested = JSON.parse(t) || {}; const r = localStorage.getItem('keyboard-tester:maxRollover'); if(r) this.maxRollover = parseInt(r) || 0; } catch(e){} this.listening = true; }, destroy(){ window.removeEventListener('keydown', this._down); window.removeEventListener('keyup', this._up); window.removeEventListener('blur', this._blur); }, // ---- the full set of codes we expect a 100%-complete keyboard to cover ---- get allCodes(){ const codes = []; this.rows.forEach(r => r.forEach(k => { if(k.c) codes.push(k.c); })); this.navRows.forEach(r => r.forEach(k => { if(k.c && !k.spacer) codes.push(k.c); })); if(this.numpadEnabled){ ['NumLock','NumpadDivide','NumpadMultiply','NumpadSubtract','NumpadAdd', 'NumpadEnter','NumpadDecimal','Numpad0','Numpad1','Numpad2','Numpad3', 'Numpad4','Numpad5','Numpad6','Numpad7','Numpad8','Numpad9'].forEach(c => codes.push(c)); } return codes; }, get testedCount(){ const all = this.allCodes; let n = 0; all.forEach(c => { if(this.tested[c]) n++; }); return n; }, get totalCount(){ return this.allCodes.length; }, get progressPct(){ if(!this.totalCount) return 0; return Math.round((this.testedCount / this.totalCount) * 100); }, get untestedList(){ return this.allCodes.filter(c => !this.tested[c]); }, get heldCount(){ return Object.keys(this.held).filter(k => this.held[k]).length; }, onDown(e){ // never let the page scroll / lose focus while testing const block = ['Space','Tab','Backspace','ArrowUp','ArrowDown','ArrowLeft','ArrowRight', 'PageUp','PageDown','Home','End','Enter','/','\\'']; // also swallow lone F-keys (F1 help, F5 refresh…) so they're testable; // modifier combos (Ctrl+C, Ctrl+F, Ctrl+Tab…) pass through to the browser const loneFKey = /^F([1-9]|1[0-2])$/.test(e.code) && !e.ctrlKey && !e.metaKey && !e.altKey; if(block.includes(e.code) || block.includes(e.key) || loneFKey) e.preventDefault(); const code = e.code || ('Key' + (e.key||'').toUpperCase()); this.last = { key: e.key === ' ' ? 'Space' : e.key, code: e.code || '(none)', keyCode: e.keyCode, which: e.which, location: e.location, repeat: e.repeat }; if(typeof e.getModifierState === 'function'){ this.capsOn = e.getModifierState('CapsLock'); } if(e.repeat){ return; } // held-key auto-repeat: state already set // key-chatter detection: a fresh keydown within ~35ms of the same key's // release is almost never human — it's a worn switch double-triggering if(code){ const lu = this._lastUpAt[code]; if(lu && (performance.now() - lu) < 35){ this.chatter[code] = (this.chatter[code] || 0) + 1; } } // mark held + tested if(code){ this.held[code] = true; if(!this.tested[code]){ this.tested[code] = true; this.persist(); if(window.WD && WD.sound) WD.sound.play('pop'); if(window.WD && WD.haptic) WD.haptic(6); } else { if(window.WD && WD.sound) WD.sound.play('click'); } } this.presses++; // rollover / ghosting: track max simultaneous physical keys const h = this.heldCount; if(h > this.maxRollover){ this.maxRollover = h; try { localStorage.setItem('keyboard-tester:maxRollover', String(h)); } catch(_){} } // celebrate full completion once if(this.testedCount >= this.totalCount && !this.everCelebrated){ this.everCelebrated = true; const el = this.$refs.kb; if(window.WD){ if(WD.celebrate){ const r = el ? el.getBoundingClientRect() : {left:innerWidth/2,top:innerHeight/2,width:0,height:0}; WD.celebrate(r.left + r.width/2, r.top + r.height/2); } else if(WD.confetti){ WD.confetti(innerWidth/2, innerHeight/2); } if(WD.toast) WD.toast('Every key tested! 🎉'); } } }, onUp(e){ const code = e.code || ('Key' + (e.key||'').toUpperCase()); if(code){ this.held[code] = false; this._lastUpAt[code] = performance.now(); } if(typeof e.getModifierState === 'function'){ this.capsOn = e.getModifierState('CapsLock'); } }, clearHeld(){ this.held = {}; }, persist(){ try { localStorage.setItem('keyboard-tester:tested', JSON.stringify(this.tested)); } catch(e){} }, keyClass(k){ if(k.spacer) return 'opacity-0 pointer-events-none'; let base = 'kt-key'; if(this.held[k.c]) base += ' kt-held'; else if(this.tested[k.c]) base += ' kt-tested'; return base; }, // tap support for touch devices — simulate a press of a given code tap(k){ if(k.spacer || !k.c) return; this.last = { key: k.l, code: k.c, keyCode: '(tap)', which:'(tap)', location: '-', repeat:false }; if(!this.tested[k.c]){ this.tested[k.c] = true; this.persist(); if(window.WD && WD.sound) WD.sound.play('pop'); if(window.WD && WD.haptic) WD.haptic(6); } else if(window.WD && WD.sound){ WD.sound.play('click'); } this.presses++; if(this.testedCount >= this.totalCount && !this.everCelebrated){ this.everCelebrated = true; if(window.WD){ if(WD.confetti) WD.confetti(innerWidth/2, innerHeight/2); if(WD.toast) WD.toast('Every key tested! 🎉'); } } }, reset(){ this.tested = {}; this.held = {}; this.chatter = {}; this._lastUpAt = {}; this.last = null; this.presses = 0; this.everCelebrated = false; try { localStorage.removeItem('keyboard-tester:tested'); } catch(e){} if(window.WD && WD.sound) WD.sound.play('click'); if(window.WD && WD.toast) WD.toast('Cleared — start pressing keys'); }, resetRollover(){ this.maxRollover = 0; try { localStorage.removeItem('keyboard-tester:maxRollover'); } catch(e){} }, rolloverLabel(){ const n = this.maxRollover; if(n >= 6) return 'N-key rollover (excellent)'; if(n >= 4) return n + '-key rollover (good)'; if(n >= 2) return n + '-key rollover'; if(n === 1) return '1 key — press more at once'; return 'press several keys together'; } }" x-init="init()" class="select-none">
Tested
/
Held now
Max rollover
Presses
All-keys progress
Anti-ghosting: — down right now
⚡ Possible key chatter: These keys re-triggered within 35 ms of being released — a classic sign of a worn switch that "double-types". Press them slowly a few times to confirm.
Still untested ()
🎉 Every key registered — your keyboard checks out!

100% client-side — nothing is sent anywhere. Touch devices: tap keys to mark them. Tip: hold 6+ keys at once to verify full N-key rollover.

Why you’ll love Keyboard Tester

Instant & freeNo signup, no paywall, no limits — Keyboard Tester works the moment the page loads.
Completely privateEverything runs in your browser. Nothing you enter is ever uploaded or stored on a server.
Works on any deviceFully responsive and touch-friendly — use it on your phone, tablet or desktop.
Uses your real deviceTaps into your actual camera, mic or motion sensors — right in the browser.

How to use it

  1. 01

    Open it

    No download and no login — the tool is ready right at the top of this page.

  2. 02

    Use it

    Enter your details or start interacting. Everything updates live as you go.

  3. 03

    Get your result

    Copy, download or share your result in a single tap. That’s it.

About Keyboard Tester

Press any key and the on-screen keyboard lights it up while it is held, then keeps it marked as tested — so you can methodically work across a full board, including the navigation cluster, arrow keys and an optional numpad, until the progress bar hits 100%. A live readout shows exactly what the browser received for each press: the key character, the physical code, the legacy keyCode and which values, the key location, and whether Caps Lock is on. Everything runs client-side; no keystroke leaves your device, and your tested set is saved locally so you can come back and finish later.

Beyond "does every key work", the tester checks two hardware faults that are hard to spot otherwise. The first is rollover and ghosting: cheap membrane keyboards wire keys in a matrix that can only register a few simultaneous presses, so fast gaming combos silently drop keys. Hold several keys at once and the max-rollover stat records how many registered together — six or more at once is effectively N-key rollover.

The second is key chatter, the classic symptom of a worn or dirty mechanical switch that "double-types" letters. The tester timestamps every release, and if the same key fires a fresh keydown within 35 milliseconds of being let go — far faster than a human can genuinely re-press — it flags that key with a chatter count so you know which switch to clean or replace.

Popular uses

  • ✦ Check every key on a used or second-hand keyboard before buying it
  • ✦ Diagnose double-typing letters with the key-chatter detector
  • ✦ Verify a gaming keyboard's N-key rollover claim by holding 6+ keys
  • ✦ Document which keys died after a spill before deciding on a repair
  • ✦ Look up JavaScript key and code values while building keyboard shortcuts

Frequently asked questions

What is keyboard ghosting and how do I test for it here?
Ghosting happens when a keyboard's internal matrix cannot distinguish certain combinations, so pressing a third or fourth key either does not register or registers a phantom key you never pressed. To test it, hold a realistic gaming combo — W, A, S, D plus Space and Shift — and watch the "Held now" and "Max rollover" stats. If keys stop lighting up while physically held, you have found your board's rollover limit; 6-key or higher is excellent.
What does the key chatter warning mean?
Chatter (also called switch bounce) is when a worn, dirty or defective switch electrically re-triggers on a single press, typing "tthe" when you typed "the". The tester flags any key that sends a new keydown within 35 ms of its own release — an interval no human re-press achieves. Press the flagged key slowly several times to confirm; if it keeps chattering, the usual fixes are compressed air, contact cleaner, or replacing that switch.
What is the difference between key, code and keyCode in the readout?
code identifies the physical key position and ignores your layout — the key right of Tab is always KeyQ, even on an AZERTY keyboard where it types "a". key is the actual character or action your layout produces, so it changes with language and Shift state. keyCode and which are deprecated numeric IDs kept only for legacy scripts; new code should use key or code, which is why this tester shows all of them side by side.
Why do some keys, like Fn, never register?
The Fn key is handled entirely inside the keyboard's own controller and never sends an event to the operating system, so no browser tool can see it — only its effect on other keys. A few other keys can be swallowed before reaching the page too: the OS may reserve certain shortcuts, and the tester deliberately lets modifier combos like Ctrl+C pass through to the browser while capturing lone presses of everything on the board.
Can I use this to check a laptop or second-hand keyboard for dead keys?
Yes — that is the main job. Work through the board and the "Still untested" chips list exactly which keys have never registered, which is the fastest way to document dead keys after a spill or before buying a used keyboard. Progress is saved in your browser, so you can clean the keyboard, come back, and re-press only the problem keys. On touch devices you can tap on-screen keys to mark them manually.
Is Keyboard Tester free to use?
Keyboard Tester is 100% free — no signup, no watermarks and no usage limits. It’s one of 200+ free tools we build and give away.
Is my camera or mic data uploaded?
No. All sensor data is processed live on your device and never leaves it or gets stored.

We built this. We can build yours.

Keyboard Tester is one of our free tools. Need a website, app, AI agent or automation? Fixed price — and you own the code.