JavaScript Keycode Info

Press any key to inspect its JavaScript KeyboardEvent properties in real time.

Last reviewed: April 2026

New to this tool? Click here for instructions

Click here or press any key
-
waiting for key…
Ctrl Alt Shift Meta ? Repeat
event.key -
event.code -
event.keyCode (deprecated) -
event.which (deprecated) -
event.location -
event.charCode (deprecated) -

Last 10 key presses. Switch to Live tab and press keys to populate.

No keys pressed yet.

Common keyboard codes for quick reference.

Key Name event.key event.code keyCode Category
Press any key to see its event properties.

Press any key on your keyboard and instantly inspect every property the browser exposes on the KeyboardEvent object — event.key, event.code, the legacy keyCode and which values, the physical-location flag, and the four modifier booleans (ctrlKey, altKey, shiftKey, metaKey). Everything runs entirely in your browser. No keystrokes are uploaded, stored, or logged.

What This Tool Does

The JavaScript Keycode Info tool is a live keyboard event inspector built on the W3C UI Events specification. When a key is pressed, the browser dispatches a KeyboardEvent with a fixed set of read-only properties. This tool attaches a keydown listener to the document and renders every relevant property of that event into a properties grid the moment a key is captured — there is no debounce, no buffering, no remote call. The properties grid surfaces the seven values that matter in practice for keyboard handling code: event.key (the logical character or named key), event.code (the physical key identifier), the legacy event.keyCode and event.which (kept for backward compatibility), event.location (which copy of a duplicated key was pressed), event.charCode (only meaningful for keypress), and the four modifier flags as boolean chips.

Three views complement the live readout. The History tab retains the last ten key presses with their codes and any modifiers held at the time, useful for verifying that a sequence of keys fired in the order you expected. The Reference tab lists 40 common keys — control keys, navigation keys, modifiers, digits, letters, numpad equivalents, function keys, and punctuation — with each row showing the event.key, event.code, and legacy keyCode together. Both keydown and keyup events expose identical property shapes, but only keydown is bound here; keypress has been deprecated by the W3C since 2014 and now fires inconsistently across browsers. A Copy JSON button serializes the most recent event to a clipboard-ready JSON object — handy for pasting into a bug report or a test fixture.

How to Use It

The interface is built for keyboard-first interaction — open the page and start pressing keys.

Focus the Capture Zone

The dashed-border box at the top is the capture zone. Clicking it makes the tool's intent visible — a "Listening" label confirms it has focus — but in practice you do not need to focus anything. A document-level keydown listener catches every key press anywhere on the page. The capture zone exists for accessibility and screen readers, and to visually anchor the tool's purpose.

Press Any Key to See Its Properties

The moment a key is pressed, the giant glyph displays the value of event.key, the smaller label below shows event.code, and the properties grid populates with all six event properties. Modifier chips light up green for whichever of Ctrl, Alt, Shift, Meta, and Repeat were active at the instant the key fired. Hold a key down for a beat and you will see the Repeat chip light up — this is how the browser signals that the operating system's key-repeat timer has triggered another keydown dispatch for the same physical key.

Distinguish keydown, keypress, and keyup

The three keyboard event types fire in a specific order: keydown the moment the key goes down, then keypress (deprecated) for character-producing keys only, then keyup when the key is released. This tool listens to keydown because it captures every key — including modifiers, function keys, arrows, and dead keys — and fires immediately. keypress would miss every non-character key. keyup fires once per release regardless of how long the key was held, which is useful for hotkey-release detection but unhelpful for inspecting a key's identity.

Switch Views and Export

The three chip buttons at the top toggle between Live, History, and Reference views without losing state — switching to History after pressing a key returns to a populated list. Click Copy JSON to copy the last event as JSON. Copy History dumps the recent ten events as a JSON array. Both outputs are formatted with two-space indentation, ready to paste into a test file or a debugging note.

Worked Example: Pressing ArrowLeft, Then Shift+ArrowLeft

The two presses below — first a bare arrow key, then the same key with Shift held — illustrate exactly how modifier state changes the boolean flags while leaving the key's identity untouched.

  1. Press ArrowLeft alone. The properties grid shows event.key = "ArrowLeft", event.code = "ArrowLeft", event.keyCode = 37 (the legacy value, still emitted by every browser), event.which = 37 (mirrors keyCode), event.location = 0 (standard, not numpad), and event.charCode = 0 (non-printing key). All four modifier chips stay dim because no modifier was held.
  2. Press Shift+ArrowLeft. The key's identity is unchanged: event.key, event.code, and event.keyCode still report "ArrowLeft", "ArrowLeft", and 37 respectively. The only difference is that event.shiftKey is now true and the Shift chip lights up green. The modifier flags are a parallel channel that report which auxiliary keys were held at the moment the primary key fired — they never alter the primary key's reported identity.
  3. Read the lesson. To detect a key without caring about modifiers, check event.key or event.code in isolation. To detect a specific combination like Shift+ArrowLeft for selection extension, AND the key check against the modifier boolean: if (event.key === "ArrowLeft" && event.shiftKey). To detect the key while explicitly excluding modified variants, negate the modifiers: if (event.key === "ArrowLeft" && !event.shiftKey && !event.ctrlKey).
ArrowLeft vs. Shift+ArrowLeft: Property-by-Property Comparison
Property ArrowLeft Shift+ArrowLeft
event.key"ArrowLeft""ArrowLeft"
event.code"ArrowLeft""ArrowLeft"
event.keyCode3737
event.which3737
event.location00
event.charCode00
event.shiftKeyfalsetrue
event.ctrlKeyfalsefalse
event.altKeyfalsefalse
event.metaKeyfalsefalse
The key's reported identity is identical in both events; only the shiftKey boolean changes. This is the property of modifier flags that makes them safe to AND against a key check without affecting the key match.

Common Use Cases

Game Keyboard Mapping (WASD vs. Arrow Keys)

Browser game developers routinely map both WASD and arrow keys to the same movement actions so that left-handed players and laptop users on compact keyboards have an equivalent option. Use event.code rather than event.key for this mapping: code === "KeyW" stays true on AZERTY layouts where the same physical key produces the letter Z, while key === "w" would silently break for those users. The arrow-key codes (ArrowUp, ArrowDown, ArrowLeft, ArrowRight) are layout-independent and identical for both event.key and event.code. For a complete game input layer, you also want to listen on both keydown and keyup to track the held/released state of each direction independently, since players frequently press multiple direction keys simultaneously.

Text Editor Shortcuts

Code editors and rich-text editors bind dozens of keyboard shortcuts that must coexist with the browser's own shortcuts. The pattern is invariant: keydown listener at the document or editor root, check the key plus modifier combination, call preventDefault() when the editor owns the keystroke, leave the event alone otherwise. event.code is preferable to event.key for shortcuts where the mnemonic matters: Ctrl+S to save should fire on the physical S key position regardless of layout, which only event.code === "KeyS" guarantees. For shortcuts where the character matters — Ctrl+/ to toggle a comment — event.key is the right choice because the slash key position varies across layouts.

Accessibility and Tab Order Debugging

Pressing Tab and Shift+Tab in this tool surfaces exactly how the browser dispatches focus-change keys. The Tab key fires event.key === "Tab", code === "Tab", keyCode === 9. When developing keyboard navigation for screen-reader users, the immediate question is whether your custom widget swallows Tab incorrectly — bind a listener, log every keydown event with event.key === "Tab", and you can confirm focus traversal without leaving the browser. The WAI-ARIA Authoring Practices document specifies which keys each widget pattern must handle (arrows for menus and tab lists, Enter and Space for activation, Escape for dismissal); this tool is the fastest way to confirm those keys are reaching your handler.

Form Validation: Enter Key vs. Newline

In a single-line <input>, the Enter key submits the enclosing form by default. In a <textarea>, Enter inserts a newline character. Forms that want chat-app behavior — Enter sends, Shift+Enter inserts a newline — must distinguish the two by combining event.key === "Enter" with !event.shiftKey, then call preventDefault() for the bare-Enter case. The IME composition state matters too: while a user is composing a CJK character, pressing Enter to commit the candidate should not also submit the form. Check event.isComposing alongside the key match; if it is true, the Enter belongs to the IME and should not be intercepted.

Browser Quirk Debugging

Cross-browser keyboard handling regularly surfaces inconsistencies that only an event inspector can pin down. Edge and Internet Explorer historically reported the Backspace key with different keyIdentifier values, Safari emits keyCode = 229 for certain dead keys, Firefox on Linux reports different codes for the right-side Alt key compared to Chrome on the same machine. When a keyboard shortcut works in development and silently fails in production for one user, the fastest diagnostic step is to ask them to load this tool and screenshot the event properties grid for the problem keystroke — the discrepancy almost always becomes obvious immediately.

Edge Cases and Limitations

Keyboard event handling has more sharp edges than its API surface suggests. The cases below come up regularly in production code.

IME composition events block keydown. When a CJK Input Method Editor — Pinyin, Hiragana, Hangul — is active, the user types Latin letters that the IME aggregates into a composition. During composition, keydown fires with event.key === "Process" and event.keyCode === 229, signaling that the IME is handling the input. The actual composed character arrives via compositionstart, compositionupdate, and compositionend events on the input element. Code that listens to keydown alone will see only "Process" for the duration of composition and miss every character the user committed.

keypress is deprecated and unreliable. The W3C deprecated keypress in the 2017 UI Events specification revision. Chrome stopped firing it for non-character keys in 2014; Safari, Firefox, and Edge followed at varying paces. For new code, use keydown for key identity and the input event for character input — keypress is supported only for legacy compatibility and should not appear in new handlers. The corresponding event.charCode property is similarly deprecated and now returns 0 for keydown on every browser.

Mobile virtual keyboards rarely fire keydown. iOS Safari and Android Chrome dispatch the input event for most character keys typed on the on-screen keyboard, but omit keydown entirely or fire it with key = "Unidentified" and keyCode = 229. The Enter key, Backspace, and the arrow keys on some virtual keyboards do produce keydown, but most letter keys do not. Any mobile-compatible input handler must listen to input on the target element and synthesize keystroke semantics from the resulting value changes — relying on keyboard events for mobile text entry is a guaranteed bug.

Dead keys produce sequenced events. Dead keys — the accent keys on European keyboards and the diacritic keys on US-International — fire a keydown event with event.key === "Dead", followed by no character output until the next keypress completes the composition. Pressing the dead-key acute then the letter "e" produces "é" via a sequence of two keydown events plus matching composition events. Code that maps individual keydown events to characters will misinterpret the sequence; composition events are again the correct API.

event.key and event.code diverge on non-QWERTY layouts. On an AZERTY French keyboard, the physical key in the QWERTY "A" position produces the letter "Q" via event.key, while event.code still returns "KeyA". On Dvorak, the QWERTY "S" position produces "O"; on Colemak, "S" produces "R". For shortcuts and games where the physical position is the affordance, always prefer event.code. For text-input logic where the character matters, prefer event.key. Layout-independent gameplay on the left-handed Dvorak layout requires the physical-key approach to remain playable.

Locked modifier states reflect in getModifierState. CapsLock, NumLock, and ScrollLock are locking modifiers — they latch on and off rather than acting as held-while-pressed modifiers. Their state is not exposed through event.ctrlKey-style booleans on every event, but event.getModifierState("CapsLock") queries the current latched state on any keyboard event. This matters for password fields, where indicating CapsLock-on is a standard usability affordance.

Behind the Scenes: How Keyboard Events Travel from Hardware to JavaScript

From Scan Code to USB HID Usage ID

A keypress on a physical keyboard starts as a scan code emitted by the keyboard's microcontroller. On a USB keyboard, that scan code is mapped onto a USB HID Usage ID as defined in the USB HID Usage Tables specification. The Usage ID for the letter "A" is 0x04; for Enter, 0x28; for ArrowLeft, 0x50. The operating system's HID driver translates the Usage ID into an OS-level virtual key code (Windows VK_*, macOS NSEvent characters, Linux evdev codes) and dispatches it to whichever process owns keyboard focus.

From OS Virtual Key to KeyboardEvent

The browser receives the OS-level key event and normalizes it into a KeyboardEvent structure that matches the W3C UI Events specification. The normalization step is non-trivial: the browser must map the OS virtual key code to a stable, cross-platform event.code string ("KeyA", "ArrowLeft", "Enter"), apply the active keyboard layout to derive event.key, populate the legacy keyCode field with the historical Windows VK value, and set the modifier booleans from the OS-reported modifier state. The resulting KeyboardEvent is the same shape regardless of whether the underlying hardware is a USB keyboard, a Bluetooth keyboard, an on-screen keyboard, or a virtual keyboard driven by an assistive technology stack like AT-SPI on Linux or NSAccessibility on macOS.

event.code: Physical Key vs. event.key: Logical Character

The split between event.code and event.key is the single most important design decision in the modern KeyboardEvent API. event.code is the physical key, identified by position on a US QWERTY keyboard, and it never varies with keyboard layout, modifier state, or IME state — pressing the physical key in the position of QWERTY's "A" always reports code === "KeyA". event.key is the logical character or named key produced by the user, applying the active keyboard layout, modifiers, and IME composition state — pressing that same physical key on AZERTY reports key === "q". The two together give you both physical and logical layers in a single event, eliminating most of the layout-detection guesswork that plagued early-2000s keyboard code.

Why keyCode Is "Legacy" but Still Universal

The event.keyCode field predates the W3C UI Events specification by more than a decade. It originated as a Windows-specific virtual key code that Netscape Navigator exposed to JavaScript in the mid-1990s, then propagated to every browser via de facto standardization. The W3C explicitly deprecated keyCode in the 2017 UI Events revision in favor of event.code and event.key, but browser vendors retain it indefinitely because removing it would break a measurable fraction of the open web. The deprecation is real — Chrome and Firefox no longer emit standardized keyCode values for non-Latin keyboard layouts, and certain newer keys (the Compose key on some Linux distributions, the Globe key on Apple Silicon Macs) report keyCode = 0. New code should reach for event.key or event.code; legacy code that already uses keyCode continues to function on Latin layouts.

Accessibility Stacks: AT-SPI and Assistive Input

Screen readers and switch-control assistive technologies do not send their input through the operating system's standard keyboard event stream. On Linux, AT-SPI (Assistive Technology Service Provider Interface) brokers events between assistive applications and the focused application. On macOS, NSAccessibility serves the same role. The browser presents these synthesized events through the same KeyboardEvent API surface, which is why a screen reader's pass-through Tab key looks identical to a real Tab keystroke to your JavaScript handler. The corollary is that keyboard handlers built correctly against the W3C UI Events spec automatically work for AT users — no special case needed.

Comparison: This Tool vs. Chrome DevTools vs. keycode.info vs. jsFiddle

Several established tools and techniques surface keyboard event properties. The right choice depends on what you are debugging.

Keyboard Event Debugging Tools: When Each Is the Right Choice
Tool Best For Strengths Limitations
This Tool Quick property inspection plus history All seven KeyboardEvent properties in one grid, last-ten history, JSON copy, reference table of common keys Does not break execution; reads events from the document, not from an arbitrary target
Chrome DevTools Event Listener Breakpoints Debugging an existing handler that is misbehaving Sources panel > Event Listener Breakpoints > Keyboard > keydown pauses execution the moment any keydown listener fires, with the full call stack and live event object Setup overhead; halts page execution; only meaningful when you already have a handler attached and want to trace into it
keycode.info Quick lookup of legacy keyCode values Single-purpose, fast load, focused on the keyCode integer Does not show event.code or modifier state; emphasizes a deprecated property; no history or reference table
jsFiddle / CodePen with a custom snippet Testing event handling logic, not just properties Lets you write the actual handler that will ship; can call preventDefault, attach to specific elements, test event-flow interactions You must write the inspection code yourself; turnaround is slower than a purpose-built tool
The DevTools breakpoint is unmatched when an existing handler is the suspect. This tool is the right starting point when you do not know what the browser will report for a given keypress.

For most day-to-day debugging — figuring out which event.code a key reports on an unfamiliar layout, confirming that a modifier combination fires the way you expect, or assembling a reference list of arrow-key codes before writing a game input layer — this tool gives you the answer in one keystroke. For pursuing a regression where you suspect a third-party library is swallowing your event, Chrome DevTools' Event Listener Breakpoint feature is the better choice because it surfaces the call stack the moment any keyboard handler runs. For building a reproduction in a bug report, a jsFiddle or CodePen snippet that reproduces the failing handler is more persuasive than a screenshot of event properties.

Frequently Asked Questions

The W3C UI Events specification deprecated keyCode in favor of event.key and event.code because keyCode values were never standardized across browsers and operating systems. Browser vendors retain keyCode indefinitely for backward compatibility because removing it would break a significant portion of the open web. For new code, always use event.key (logical character) or event.code (physical key).
event.key is the logical, layout-aware character the user produced — affected by Shift, CapsLock, IME composition, and the active keyboard layout. event.code is the physical key identifier and never changes regardless of layout or modifier state. Pressing the A key on a QWERTY keyboard gives key="a" but code="KeyA"; on an AZERTY keyboard the same physical key gives key="q" but still code="KeyA".
Listen for keydown, check event.ctrlKey (or event.metaKey on macOS) along with event.key === "s", then call event.preventDefault() before the browser default handler runs. The keydown listener must be attached high enough in the event flow (often window or document) and must fire before the browser executes its built-in shortcut, which is why preventDefault on keydown — not keyup — is the correct approach.
Mobile virtual keyboards on iOS and Android often dispatch only the input event for most character keys, omitting keydown and keyup or firing them with key="Unidentified" and keyCode=229. The 229 code specifically indicates that the IME or virtual keyboard is processing the input and the keypress has not yet resolved to a final character. For mobile-compatible input handling, listen to the input event on the target element rather than relying on keyboard events.
Dead keys fire keydown with event.key === "Dead" and a code identifying the physical key. The next keypress completes the composition. To handle dead-key sequences reliably, listen for compositionstart, compositionupdate, and compositionend events instead of trying to track keydown sequences manually — the composition events surface the in-progress and final composed string consistently across IMEs and dead-key layouts.
event.keyCode and event.key reflect the logical character produced by the active keyboard layout. On AZERTY, the physical key in the top-left letter row position 1 produces "a" on QWERTY but "q" on AZERTY, so event.key and keyCode differ accordingly. event.code stays constant — both layouts report code="KeyA" for that physical position. For games or shortcuts that should track physical key position rather than letter, prefer event.code.
event.key is identical for both ("1"), but event.code differs: the main row gives code="Digit1" while the numpad gives code="Numpad1". Additionally, event.location returns 3 (DOM_KEY_LOCATION_NUMPAD) for numpad keys and 0 (DOM_KEY_LOCATION_STANDARD) for the main row. Either code or location is sufficient to distinguish the two; using both is defensive but redundant.
Calling event.preventDefault() on a keydown event for F5 will stop the refresh in most browsers, but some reserved shortcuts (F11 fullscreen, Ctrl+W close tab, Ctrl+T new tab) cannot be intercepted from JavaScript for security reasons. F5 and Ctrl+R are interceptable, but blocking refresh is hostile UX in most contexts — users expect the browser to honor their shortcuts. Block only when the page genuinely owns the keystroke, such as a fullscreen editor with unsaved changes.