📱 Device Tool
⌨️

Keyboard Tester

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

100% free No sign-up Private & secure Works on 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 & free

No signup, no paywall, no limits — Keyboard Tester works the moment the page loads.

🔒

Completely private

Everything runs in your browser. Nothing you enter is ever uploaded or stored on a server.

📱

Works on any device

Fully responsive and touch-friendly — use it on your phone, tablet or desktop.

📱

Uses your real device

Taps into your actual camera, mic or motion sensors — right in the browser.

How to use it

1

Open it

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

2

Use it

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

3

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

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.

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.

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.

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.

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.

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.

No. All sensor data is processed live on your device and never leaves it or gets stored.

More Device & Sensor Tools

From our collection of 317 free tools.

We built this. We can build yours.

Keyboard Tester is one of 200+ free tools from Workaholic Developers — a software & AI studio. Need a website, app, AI agent or automation? Let’s talk.

🇮🇳 Built by Workaholic Developers

We built this little toy in days. Imagine what we’ll build for you.

AI agents, web apps, automation, custom tools — designed and engineered fast, on whatever tech fits the job. If you can describe it, we can build it.

We use cookies

We use cookies to enhance your browsing experience, analyze site traffic, and personalize content. Learn more