Skip to content

Conversation

@CarlQLange
Copy link
Contributor

@CarlQLange CarlQLange commented Sep 14, 2025

Safari/WebKit sometimes reports physical Arrow keys with KeyboardEvent.location === 3 (numpad). The Emscripten input glue ignores those for directional input, so arrows don't work in Safari.

This PR adds a small, WebKit-scoped capture-phase shim to remap Arrow key events with location 3 to location 0, prevents page scrolling on Arrow keys while the canvas is focused, and ensures the canvas is focusable and focused on start.

Changes:

  • build/html/export.html: add WebKit remap + prevent scroll + focus; add tabindex=0
  • build/html/index.html: same as above for the basic player
  • build/webapp/index.html: same as above for the webapp

Tested locally in Safari: arrows now reach the game.

The preceding text and the patch written by Claude, but the fix does work.

… key events with KeyboardEvent.location === 3 (numpad) to location: 0 on WebKit in capture phase\n- Prevent page scrolling on Arrow keys while canvas is focused\n- Ensure canvas is focusable (tabindex=0) and focused on start\n\nThis addresses Safari/WebKit misclassification of physical Arrow keys so games receive directional input correctly.
@CarlQLange CarlQLange changed the title web: Fix Safari Arrow keys in HTML export/webapp [web export] Fix Safari Arrow keys in HTML export/webapp Sep 14, 2025
@nesbox
Copy link
Owner

nesbox commented Sep 15, 2025

The PR looks too big for such a small thing to me, but I'm not a web dev :)
I think we need someone with web development experience to review the PR

@CarlQLange
Copy link
Contributor Author

100% fair. What's funny is I've been a web guy for 15 years and I don't really grok what's going on here either. I will try and engage my brain and take a closer look myself in a while, instead of babysitting AI like a fool.

@CarlQLange
Copy link
Contributor Author

CarlQLange commented Sep 17, 2025

So I would agree that the code is a little verbose perhaps, but it does do the right thing.

The issue is that Safari (at least on my MacBook Air M4) process the arrow keys on the built-in keyboard as being part of the numpad, which sets the key location to 4 (https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/location).

However some part of TIC-80 further down the stack does not consume the events with this location. The 'easiest' solution for me was to add this shim, which essentially watches for the arrow key input on safari and remaps its location from 3 (numpad) to 0 (standard location).

The shim itself is maybe a little verbose but in my eyes it does the right thing and it's relatively well encapsulated. It's also gated for WebKit so it shouldn't affect other browsers, just Safari and Safari-related (Orion for instance).

      (function(){
            const ua = navigator.userAgent || '';
            const isWebKit = /AppleWebKit/i.test(ua);
            if (!isWebKit) return;

            const synthesizeArrowWithLocation0 = (orig) => {
                try {
                    const ev = new KeyboardEvent(orig.type, {
                        key: orig.key,
                        code: orig.code,
                        location: 0,
                        repeat: orig.repeat,
                        ctrlKey: orig.ctrlKey,
                        shiftKey: orig.shiftKey,
                        altKey: orig.altKey,
                        metaKey: orig.metaKey,
                        bubbles: true,
                        cancelable: true,
                    });
                    Object.defineProperty(ev, 'which', { get: () => orig.which });
                    Object.defineProperty(ev, 'keyCode', { get: () => orig.keyCode });
                    return ev;
                } catch { return null; }
            };

            const remapArrowLocationCapture = (e) => {
                const isArrow = (e.key && e.key.startsWith('Arrow')) || (e.code && e.code.startsWith('Arrow'));
                if (!isArrow) return;
                if (e.location === 3) {
                    const syn = synthesizeArrowWithLocation0(e);
                    if (syn) {
                        e.stopImmediatePropagation();
                        e.preventDefault();
                        (e.target || document.getElementById('canvas')).dispatchEvent(syn);
                    }
                }
            };

            ['keydown','keyup'].forEach(t => document.addEventListener(t, remapArrowLocationCapture, true));
        })();

It's in a closure so we don't pollute the global scope. Essentially we just add two event listeners on the document, for keydown and keyup. That listener checks if it's an arrow key, and if so, checks if the location is 3 (numpad). If so, it synthesises a new event with the correct location and kills the existing one with stopImmediatePropagation and preventDefault. Then it dispatches the synthesised one. We can't just do e.location = 0 because keyboard event properties are read-only.

There is a second part to this that prevents scrolling when the canvas is focused, but I guess that should be removed for this PR - more of a nice-to-have.

@CarlQLange
Copy link
Contributor Author

And a final note: The semicolons needed adding because otherwise the closure creates a syntax error:

var Module = {canvas: document.getElementById('canvas'), arguments:['cart.tic']}(function(){ … }()

Anyway, I wanted to say a really big thank you for TIC-80. I paid for the Pro version the other day on Itch. I did most of a degree in game development but got sidetracked along the way and haven't written a game in over a decade. After discovering TIC-80 I've written 2 in a week. I really love it, so thank you very much!

Copy link
Owner

@nesbox nesbox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, lets try the PR
thank you for the help

@github-project-automation github-project-automation bot moved this to In progress in dev version 1.2 Sep 20, 2025
@nesbox nesbox merged commit da3ea61 into nesbox:main Sep 20, 2025
12 checks passed
@github-project-automation github-project-automation bot moved this from In progress to Done in dev version 1.2 Sep 20, 2025
@CarlQLange
Copy link
Contributor Author

I guess the other solution is to enable the base system to accept arrow keys from the numpad as well. Tbh that's probably cleaner but I wouldn't know where the processing is for it.

Anyway, thanks for accepting and thanks for TIC-80! ❤️

@CarlQLange
Copy link
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants