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
code=keyCode=which=location=held / repeatingCaps Lock ON
Press any key — the on-screen keyboard lights up and stays marked as tested. Hold several keys at once to check for ghosting.
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.
📢 Ad slot — configure AdSense in Admin → Ads & Monetization
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. You can even install this site as an app.
Yes. The tool is fully responsive and touch-friendly, so it works great on phones and tablets as well as computers.
Workaholic Developers — 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.
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.