Skip to content
Blog
Case studySeptember 2, 2026

My game taught the wrong key

On the itch page, the tutorial plates announced A and D. On an AZERTY keyboard, the key to press is engraved Q. The very first gesture the game teaches showed the wrong key to its audience. The cause: the web does not know what your keyboard engraves.

Found by opening the itch page, not by the safety net

Sillon's 0.1.0 demo had just landed on itch, playable in the browser. I open the page, like a player would. Under the driver's feet, the two plates of the very first gesture announce "A" and "D". On my AZERTY keyboard, the key to press is engraved Q. The whistle announced Q where you actually press A. The game was teaching the wrong key, from the first second, to the French-speaking part of its audience. And my 57 browser assertions were green: none of them re-read those particular plates.

The strength that becomes a trap

Godot records keys by physical position. That is a strength: the "A" of a QWERTY keyboard comes out as Q on an AZERTY layout on its own, WASD-equivalent controls are free, the player's hand finds the same keys without me doing anything. But naming a key on screen requires the reverse operation: knowing what the player's layout engraves at that position.

On desktop, the engine can answer. On the web export, the function that translates a position into an engraving returns "Not supported by this display server", and I read that in the console instead of assuming it. My fallback then dropped, silently, to the QWERTY name of the position. That is the wrong key: not an invented value, a default taken for an answer.

The real scope is small and measurable: between QWERTY and AZERTY, only the positions A, Q, W, Z and M change engraving, and my input configuration uses just two of them. Two wrong letters were enough to break the first contact.

Never name what you cannot confirm

The shipped fix comes down to one rule: never name what you cannot confirm. On the web, an ambiguous letter now yields nothing, and the display moves on to the action's next event. Walking left also carries the left arrow: the web shows "Left", true on every keyboard. The whistle only has its letter: it shows nothing at all. A mute key cap is less harmful than a lying one.

Even the arrows are written as words: the arrow character rendered as tofu in the web font, and the regulator's triangles are drawn, never typed. A geometric shape cannot be missing from a font.

The clean solution that killed the build

The browser can actually answer: the keyboard layout API did give me its 48 positions, verified in the page. But it answers through a promise, after boot, while my key caps are built during scene initialization. Awaiting it there crashes the wasm with a memory access error: the web build no longer starts at all, and my eight browser suites dropped at once to no verdict. I shipped the honest fallback and wrote the failure mode down in the code; the real translation will wait for 0.1.1.

The path nobody plays

One safety net problem remained: this empty-label path is never taken in testing. Native reads its layout, the headless harness is not concerned. So a flag forces the game to believe it sits on an unknown keyboard, and the net plays it on every run, re-reading the labels actually placed on screen. Red under both mutations I tried against it.

Document controls by key position, never by letter, as long as your targets include the web: there, the engine knows the position, not the engraving. And when a runtime cannot answer, the honest fallback is silence, not another machine's default.

What I take away

The bug was not the missing translation, it was the fallback that answered anyway. Every time a platform amputates a capability, the question to ask is: what does my code return instead, and is it an admission or an invention? A system that admits its limits gets fixed; a system that invents gets discovered on the public page, by a player.