Keyboard Event Tester
Press any key to see every property your browser puts on the
KeyboardEvent — key, code, location, modifier state, the deprecated numeric
codes and both timestamps — plus a running event log you can filter and export.
Live event inspector
Capturing keyboard events. Focus this page and press any key.
Shortcut capture
Hold your modifiers and press a key…
Press Esc to leave shortcut capture. Tab, Esc and F12 are never suppressed, so you can always leave the tool with the keyboard alone.
| Canonical id | — |
|---|---|
| By physical key | — |
| By character | — |
| Reserved? | — |
Identity
- type
- —
- key
- —
- code
- —
- location
- —
- repeat
- —
- isTrusted
- —
- isComposing
- —
- target
- —
Modifier state
- ctrlKey
- —
- shiftKey
- —
- altKey
- —
- metaKey
- —
- getModifierState('AltGraph')
- —
- getModifierState('CapsLock')
- —
- getModifierState('NumLock')
- —
- getModifierState('ScrollLock')
- —
Timing
- timeStamp (raw)
- —
- normalized to performance timeline
- —
- performance.now() in handler
- —
- handler delay (now − timeStamp)
- —
- since previous event
- —
Browser-side values only. Measure browser input event latency properly with the main latency test.
Legacy numeric properties Deprecated
- keyCode
- —
- which
- —
- charCode
- —
Do not use these in new code. Use
event.key for the character the user typed and event.code
for the physical key. Browsers still populate these three for backward compatibility
only; their values are inconsistent across browsers and keyboard layouts.
Matching code for the last key pressed
A ready-to-paste handler that matches exactly what you just
pressed. It follows the most recent keydown, so releasing the key does not
overwrite it.
// Press a key to generate a matching handler.
Dead key & IME scratch field
Dead keys and input method editors do not produce one key event per character. Watch the timeline below to see what your browser actually emits.
Event timeline
No events logged yet.
| # | Type | key | code | Modifiers | t (ms) | Δ prev |
|---|---|---|---|---|---|---|
| Press a key to start logging. | ||||||
Advanced: last event as JSON
Every property this page reads, serialised. Handy for pasting into a bug report.
{}
Measurement environment
Timer resolution: — High-resolution timing: — Browser: — Platform: —
These conditions affect your result. A browser that rounds its clock to 1 ms cannot resolve sub-millisecond differences, so jitter will look artificially quantised. How we measure.
What this test cannot tell you
This inspector shows you the KeyboardEvent objects your browser hands to
JavaScript, and nothing earlier. It cannot see switch actuation, keyboard firmware,
matrix scanning, USB or Bluetooth transmission, driver behaviour, or how your operating
system remapped a key before the browser saw it. Timestamps here describe browser-side
timing, not keyboard hardware latency.
It also cannot show you a key event that never arrives. Combinations claimed by the OS or the browser are absent from the log entirely, and their absence is not evidence that your keyboard failed to send them.
key, code and keyCode: what each one actually means
event.key is the value the active keyboard layout produces — a character
like "q", or a named value like "Enter", "ArrowLeft"
or "Shift". It changes when the user switches layout, and it changes when a
modifier is held: the same key gives "a" and "A".
event.code is the physical key position, named after where that key sits on
a US QWERTY board. It does not change with the layout, the modifiers or the language. If
you want WASD movement to work for a French user without remapping, code is
the property you want.
One physical key, four layouts, one code.
| event.code | QWERTY key | AZERTY key | QWERTZ key | Dvorak key |
|---|---|---|---|---|
| KeyQ | "q" | "a" | "q" | "'" |
| KeyA | "a" | "q" | "a" | "a" |
| KeyW | "w" | "z" | "w" | "," |
| KeyZ | "z" | "w" | "y" | ";" |
| KeyY | "y" | "y" | "z" | "f" |
| Digit1 | "1" | "&" | "1" | "1" |
Values shown are the unshifted event.key for a standard
variant of each layout. Regional variants exist and can differ — press the key on your own
machine and read the inspector above rather than trusting any table, including this one.
The third property, event.keyCode, is a legacy number. It predates both of
the others, never behaved the same way in every browser, and is derived differently
depending on the layout and the browser's compatibility rules. It is marked deprecated in
the UI Events specification. Browsers still populate it for old code, but there is no
reason to write new code against it.
Event order, and why keypress is deprecated
A single press of a character key normally produces keydown, then
keypress, then keyup. Holding the key produces a stream of extra
keydown events with repeat === true, at the interval your
operating system's key-repeat setting defines — that is an OS setting, not a hardware
property of the keyboard.
keypress is deprecated: it only fired for keys that produced a character,
it never had consistent behaviour for modifier and function keys, and it does not handle
text produced by input methods at all. For text input, listen to the input
event on the field instead; for key handling, use keydown.
Modifier state is more than four booleans
ctrlKey, shiftKey, altKey and metaKey
cover the common cases, but event.getModifierState(name) answers questions the
booleans cannot. getModifierState('CapsLock') is how you detect the classic
"your password looks wrong because Caps Lock is on" case. 'NumLock' and
'ScrollLock' report lock state the same way.
getModifierState('AltGraph') matters on European layouts, where AltGr
produces characters such as @, € and #. On Windows,
AltGr is commonly reported as Ctrl and Alt held together, so a shortcut bound to Ctrl+Alt+E
can steal a character a German or Polish user was simply trying to type. Checking
getModifierState('AltGraph') and bailing out is the fix.
Shortcuts a web page can never capture
Some key combinations are consumed before any web page sees them. When that happens
there is no event to inspect and no event to cancel — preventDefault() has
nothing to act on, because your handler was never called. Which combinations are reserved
depends on the operating system, the browser and the user's own settings, so treat the
table below as typical behaviour rather than a guarantee.
| Combination | Usually taken by | Reaches the page? |
|---|---|---|
| Ctrl+Alt+Del | Windows secure attention sequence | Never |
| Alt+Tab, Cmd+Tab | OS window switcher | Never |
| Windows / Cmd key combinations | OS shell (Win+L, Cmd+Space, …) | Rarely |
| Ctrl+W, Ctrl+N, Ctrl+T, Ctrl+Shift+T | Browser tab and window management | Usually not |
| F11, F12, Ctrl+Shift+I | Browser fullscreen and developer tools | Usually not |
| Ctrl+Shift+Esc | Windows Task Manager | Never |
| Print Screen, media keys | OS or hardware layer | Often not, or partially |
Combinations that do reach the page, such as Ctrl+S or Ctrl+P, can be
intercepted with preventDefault() — but do it sparingly. Screen reader users
depend on key combinations that look unremarkable to everyone else. Turn on shortcut
capture above and try a few: anything that appears in the inspector is yours to claim,
and anything that does not never will be.
Dead keys, IME composition and the 229 problem
A dead key produces no character on its own — it waits for the next keystroke to
combine into an accented letter. Browsers usually report the dead key itself as
event.key === "Dead", then deliver the composed character afterwards. Code
that assumes one keystroke equals one character will mis-handle it.
Input method editors, used for Japanese, Chinese, Korean and other scripts, go further:
a whole sequence of keystrokes belongs to an in-progress composition. While that is
happening, event.isComposing is true, and many browsers report
event.key as "Process" with the legacy keyCode set
to 229. If your shortcut handler ignores that, pressing Enter to confirm a
candidate word will also fire your "submit" shortcut. Guard with
if (event.isComposing) return; and prefer the compositionstart
and compositionend events when you need the boundaries. The scratch field
above logs all three so you can see the real sequence your browser emits.
What event.timeStamp is and is not
KeyboardEvent.timeStamp is a DOMHighResTimeStamp on the same
monotonic timeline as performance.now(), stamped when the browser creates the
event object. Subtracting it from a performance.now() reading taken at the top
of your handler gives you the browser-side queueing and main-thread scheduling delay — the
handler delay figure in the inspector above.
It is not the moment the switch closed. Everything before the browser — the keyboard's own scanning and firmware, the USB or Bluetooth link, the OS input stack — is already finished by the time that number exists, and none of it is visible from JavaScript. Some engines also hand back an epoch-scale value instead of a page-relative one, which is why this page normalises the timestamp before showing it. The browser input latency test uses the same normalised figure across many samples, and browser keyboard event timing explains the pipeline in full.
Related keyboard tests
Frequently asked questions
Should I use event.key or event.code for a keyboard shortcut?
Use event.code when the physical position of the key is what matters,
such as WASD movement in a game, because it stays the same on every layout. Use
event.key when the character the user actually typed is what matters, such
as pressing the letter S to save. For a shortcut like Ctrl+S, event.key is
usually the better choice, because a user on AZERTY expects the key labelled S to work.
Why does event.key change when I switch keyboard layouts?
event.key reports the character or named value the active layout
produces, so the same physical key gives "q" on QWERTY and "a"
on AZERTY. event.code reports the physical key position using US QWERTY
names, so that same key stays KeyQ on every layout. Neither is wrong; they
answer different questions.
Why is event.keyCode deprecated, and what should I use instead?
keyCode, charCode and which are legacy numeric
properties that never had consistent cross-browser or cross-layout behaviour, which is
why the UI Events specification marks them deprecated. Browsers still populate them for
compatibility, so old code keeps working, but new code should use event.key
for the character and event.code for the physical key.
Why can't this page capture Ctrl+W, Alt+Tab or the Windows key?
Some combinations are claimed by the operating system or the browser before any page
sees them, so no keydown event is ever delivered to JavaScript. Alt+Tab and
Ctrl+Alt+Del are handled by the OS, and tab and window management shortcuts are usually
handled by the browser. Because the event never reaches the page, calling
preventDefault() cannot override them. Exactly which combinations are
reserved varies by browser, operating system and user configuration.
What is event.isComposing and why do my keydown handlers misfire with an IME?
When an input method editor is composing text, for example when typing Japanese or
Chinese, keystrokes belong to the composition rather than to your shortcut. During
composition, event.isComposing is true and many browsers
report event.key as "Process" with a legacy
keyCode of 229. Ignoring keydown events while
event.isComposing is true prevents shortcuts firing in the middle of
someone's word.
Is event.timeStamp the time the key was physically pressed?
No. event.timeStamp is stamped when the browser creates the event
object, after the operating system has already delivered the input to the browser. It
does not include switch actuation, keyboard firmware, USB or Bluetooth transmission, or
OS scheduling. It is useful for measuring browser-side ordering and delay, and this page
shows it on the same timeline as performance.now() so you can compare the
two directly.