📱 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 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','/','\\'']; if(block.includes(e.code) || block.includes(e.key)) e.preventDefault(); // F-keys & some combos we let through except for browser-fighting ones 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 // 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; 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.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()" @keydown.window.prevent.stop="" class="select-none">
Tested
/
Held now
Max rollover
Presses
All-keys progress
Anti-ghosting: down right now
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.

Frequently asked questions

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.

Completely. Keyboard Tester runs entirely in your browser (client-side). Nothing you type, upload or generate is sent to our servers, so your data never leaves your device.

No. It works instantly in any modern web browser — Chrome, Safari, Edge or Firefox — on desktop and mobile.

Yes. The tool is fully responsive and touch-friendly, so it works great on phones and tablets as well as computers.

Global Maple System — a software & AI studio. We built Keyboard Tester (and 200+ other free tools) to show what we can do. Need something custom built? We’d love to help.

We built this. We can build yours.

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