Signal · signalsVersion 5

keyboard_layout

The characters that 19 physical keys produce in the active keyboard layout, from navigator.keyboard.getLayoutMap(). Chromium only, no prompt, not in either id.

keyboard_layout at a glance (from the library code, signalsVersion 5)
Component namekeyboard_layout
In the visitorIdno (reported, kept out of both ids)
structuredId groupnone
Hashingplain value; a digest only when longer than 128 characters or not printable ASCII
Excluded underno browser class
Inner timeout1000 ms
Added insignalsVersion 2
In plain words

Keyboards in different countries put different letters on the same keys: the key that types Q on a US keyboard types A on a French one. Chromium browsers let a page ask which character each physical key produces. WhiskerJS asks for 19 telling keys and records the answer. Users switch layouts, so it is reported but kept out of the identifiers.

What it reads

navigator.keyboard.getLayoutMap() and, from the map, the character for these physical key codes: KeyQ, KeyW, KeyY, KeyZ, KeyA, KeyM, Semicolon, Quote, BracketLeft, BracketRight, Backquote, Backslash, IntlBackslash, Minus, Equal, Comma, Period, Slash, Digit2.

How the technique works

  1. Without navigator.keyboard.getLayoutMap the component is unavailable / no_api.
  2. The promise is awaited. If the result has no get method, unavailable / no_value.
  3. For each key, the character is taken; a missing or empty one becomes _.
  4. The 19 characters are joined without separators. A layout with characters outside printable ASCII (for example Cyrillic or Greek) is hashed.

The API needs no permission and shows no prompt.

Why it helps

The keys chosen separate the common layouts: QWERTY against QWERTZ (Y and Z swap) and AZERTY (A, Q, M move), and national variants that differ in punctuation, brackets and the extra key next to left Shift. For your server it is context about the user’s region and a check against languages.

Stability

Medium: it follows the layout that is active at that moment, and many people switch layouts. That is why it is outside both ids.

Browser support and what blocks it

  • Chromium desktop browsers (Chrome, Edge, Brave) offer the Keyboard Map API.
  • Firefox, Safari and iOS browsers: unavailable / no_api.
  • On Android, expect unavailable; check your own traffic.
  • In cross-origin iframes the browser refuses the call: blocked.

No browser class excludes it.

Privacy

A layout hints at a region or language, much like the language settings your server already sees in Accept-Language. No keystrokes are captured. Leave it out with collect({ exclude: ['keyboard_layout'] }) if you do not need it; no id changes.

How WhiskerJS handles noise and failures

Status ruleId When
ok 19 characters, or a digest for non-ASCII layouts
unavailable no_api no Keyboard Map API
unavailable no_value the map has no get
unavailable no_digest a non-ASCII layout without crypto.subtle
blocked security_error, threw the browser refused or failed the call (for example in a cross-origin iframe)
excluded caller_excluded you excluded it
timed_out timeout, aborted budget or abort

Example value

A US English layout gives qwyzam;'[]`\\-=,./2 (the IntlBackslash key reports \ on many layouts). Headless Chromium in the container returned an empty map: ___________________ (19 underscores). Firefox and WebKit: unavailable / no_api.

Questions and answers

Does keyboard_layout record what I type?

No. It asks the browser which character each of 19 physical keys would produce in the current layout. No key presses are read or recorded.

Why is it blocked in my embedded frame?

Browsers refuse getLayoutMap() in cross-origin iframes. WhiskerJS then reports blocked with ruleId security_error or threw, depending on the error the browser raises.

Why only underscores in my headless browser?

The layout map was empty, so every key was reported as the placeholder _. That is common in headless and virtual setups.