Three Ways to Give a Web App Real Keyboard Shortcuts on Windows

Three Ways to Give a Web App Real Keyboard Shortcuts on Windows


I’ve been moving a bunch of my little Windows tools into the browser lately. Progressive Web Apps, WebAssembly, the whole deal. No installer, works on my phone, updates itself. Pretty great.

Then I hit a wall.

I built a Pomodoro timer as a web app and wanted to hit a key to pause it while I was buried in Excel. Nope. The browser only hears the keyboard when the browser is the window in front. Switch to Excel and your web app is deaf.

That’s the sandbox doing its job. Web pages are NOT supposed to spy on your keyboard from the background. Good policy. Annoying for me.

Have you ever built something 95% of the way and then found out the last 5% was the part you actually cared about? Yeah, that.

So I spent a morning on it, asked a handful of AI models to poke holes in my plan, and ended up with three working approaches. Each one trades convenience for power. Now on with the show…

First, the plain-English picture

Think of your web app as someone in a soundproof room. They can hear you fine when you’re in the room with them (the browser is in front). Step outside and they hear nothing.

Unlock Global Hotkeys for Progressive Web Apps 🚀


Unlock Global Hotkeys for Progressive Web Apps 🚀


The three approaches are three different ways of getting a message into that room from the hallway:

  • Approach 1: Only talk when you’re already in the room. (Shortcuts that work while the browser is active.)
  • Approach 2: Have the building manager relay a message. (A Chrome extension registers the shortcut.)
  • Approach 3: Install a doorbell. (A tiny AutoHotkey helper on the PC listens and rings the page.)

Approach 1: Shortcuts while the browser is in front

This is the easy one and it’s free. A few lines of JavaScript listen for keys and call your app’s code. Ctrl+P, Alt+S, F2, whatever you like. If your app is WebAssembly, the JavaScript just passes the event along.

// Works only while the page is the active window
document.addEventListener(“keydown”, e => {
if (e.ctrlKey && e.key === “p”) {
e.preventDefault();
Timer.Toggle();
}
});

Good: nothing to install, any key combo, works in every browser on every device.

Bad: useless the moment you click into another program. For a timer you want to control from anywhere, that’s a deal breaker.

Approach 2: A Chrome extension with global shortcuts

This one surprised me. Chrome extensions get one privilege that web pages never get: they can ask Chrome to register a shortcut with Windows that fires even when Chrome is in the background. Chrome does the registering. No exe, nothing to download outside the browser.

You declare the shortcuts in the extension’s manifest and flag them as global:

// manifest.json (the part that matters)
“commands”: {
“toggle”: {
“suggested_key”: { “default”: “Ctrl+Shift+1” },
“description”: “Start or pause the timer”,
“global”: true
}
}

When the key fires, the extension’s background script sends a one-word message (“toggle”) to your app’s tab. If no tab is open, it opens one. Your app just listens for messages from that extension and runs the matching function.

Here’s the catch, and it’s a big one. Chrome only allows global shortcuts on Ctrl+Shift plus a digit, 0 through 9. Not Ctrl+P. Not Ctrl+Alt+anything. Ctrl+Shift+1, Ctrl+Shift+2, and so on. That’s a hard rule in Chrome. Ten shortcuts, take it or leave it.

A few more limits:

  • Only four suggested shortcuts per extension. Users can add more by hand in Chrome’s shortcut settings.
  • Chrome (or Edge) has to be running. Close the browser and the shortcuts die with it.
  • Firefox has the extension API but not the global part. Safari doesn’t play at all.
  • It’s a doorbell only. The extension can’t tell your app which program you were in, can’t paste text into Excel for you, can’t touch the clipboard.

Good: one-click install from the Chrome Web Store, no scary “Windows protected your PC” screen, updates automatically, one extension can serve all of your web apps.

Bad: Ctrl+Shift+digit or nothing.

Putting an extension in the Chrome Web Store costs a one-time $5 developer fee and a review that takes a few days. For testing you can load it unpacked on your own machine in about a minute.

Approach 3: A tiny AutoHotkey helper (the doorbell)

This is the one I actually shipped first, because global hotkeys are what AutoHotkey was born to do.

The idea: a very small program sits in the Windows tray. Its ONLY job is to listen for hotkeys and forward a one-word command to the web app over a local connection on the PC itself. The helper knows nothing about timers or tasks. It’s a wire. The web app owns all the logic.

The heart of it is embarrassingly short:

; Register a global hotkey and push a command to the web page
Hotkey(“^+p”, (*) => SendToPage(“toggle”))SendToPage(Cmd) {
; Snapshot the window the user was in, so the page can act on it later
Hwnd := WinGetID(“A”)
Json := ‘{“cmd”:”‘ Cmd ‘”,”hwnd”:’ Hwnd ‘}’
PushToBrowser(Json) ; local connection, 127.0.0.1 only
}

The real script is longer (pairing, a settings cache, a tray menu), but that’s the whole concept.

Some things I learned building it:

  • No AutoHotkey install required. The helper compiles to a single 1.3 MB exe with the engine baked in. Users double-click it once.
  • Pairing is one click. The web app opens a special link (like a mailto: link, but for the helper). Windows launches the helper, Chrome asks “Open HotkeyBridge?” once, and you’re connected. No typing codes.
  • One helper serves every app. My timer, my recorder, whatever comes next. Each site pairs separately and gets its own hotkeys.
  • The page picks the keys. There’s a “Change” button in the app’s settings. Press the new combo and the helper re-registers it live. Even the Win key works, which browsers can’t normally see.
  • The helper remembers. Hotkeys stay registered between sessions. If a key fires and the app isn’t open, the helper opens it for you.
  • Bonus power. Because it’s AutoHotkey, the helper can tell the page which window you were in. The page can later say “paste this into that window.” A pure web app can never do that.

Good: any key combo at all, works with the browser closed, can act on other programs, and it’s tiny.

Bad: it’s an exe. Unsigned exes get the SmartScreen warning on first run, and it’s Windows only.

Watch out for hotkey collisions. My first default was Ctrl+Alt+P, which I already use in another AutoHotkey script. Whoever registers a key first wins in Windows, so the web app silently lost. I switched the default to Ctrl+Shift+P and added a check that reports “that key is already taken” instead of failing quietly.

Side by side

Browser in frontChrome extensionAutoHotkey helper
Works from other programsNoYes, while Chrome is runningYes, always
Key combos allowedAnythingCtrl+Shift+0 to 9 onlyAnything, including Win key
User has to installNothingAn extension (one click)A small exe (one download)
BrowsersAllChrome, EdgeAll (Windows only)
Can act on other windowsNoNoYes
Best forEveryone, as a baselineCasual users, a few actionsPower users, Windows automation

What I’d actually do

All three. They stack.

Approach 1 costs nothing, so every app gets it. Approach 2 is the easy on-ramp for people who just want to pause a timer without hunting for the window. Approach 3 is for the folks (like me) who want Win+F2 to do something useful in three different apps and don’t mind a tray icon.

The cool thing is the app itself doesn’t care which path the key came from. All three end up calling the same function. The plumbing changes, the app doesn’t.

One thing I’ve learned from this: the “browser can’t do that” wall is usually more like a fence. A tiny bit of native code on the right side of the fence, and suddenly a web app can do most of what a desktop app can. And you keep all the good parts of the web app: no installer for the main thing, phone support, instant updates.

How about you? Are you building things in the browser and hitting walls like this? Hit reply and tell me which wall. There’s a decent chance a small AutoHotkey script is the ladder. 😊

P.S. The helper approach is where I see the real opportunity. Keep the app in the browser, keep the native piece tiny, and let AutoHotkey do what it’s best at. If you want to learn AutoHotkey well enough to build these bridges yourself, our courses at the-Automator.com are the fastest way I know.

Joe Glines

Joe Glines spent 20 years as a corporate data scientist before going all in on automation. He now uses AI to build automation in AutoHotkey, Python and Rust, has made 1,500+ tutorials, and runs the-Automator with a small team that has shipped 450+ time-saving tools.

Comments are closed.