diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6f372ae041295930485a1b5127ca1542269223eb..b525ad21fdd83e9e01ca4830c3dafaeab7be6b1c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Added
 
-*
+* Support for responsive physical keyboard
 
 ### Changed
 
diff --git a/examples/web/index.ts b/examples/web/index.ts
index 4fd1abe16bf97de22ea509d0080796eb2fb983ca..ff6e6bc8df5154ad80538f1d2a984366a06f336b 100644
--- a/examples/web/index.ts
+++ b/examples/web/index.ts
@@ -21,8 +21,6 @@ const LOGIC_HZ = 4194304;
 const VISUAL_HZ = 59.7275;
 const IDLE_HZ = 10;
 
-const FREQUENCY_DELTA = 400000;
-
 const SAMPLE_RATE = 2;
 
 const BACKGROUNDS = [
@@ -35,17 +33,6 @@ const BACKGROUNDS = [
     "3a5a40"
 ];
 
-const KEYS: Record<string, number> = {
-    ArrowUp: PadKey.Up,
-    ArrowDown: PadKey.Down,
-    ArrowLeft: PadKey.Left,
-    ArrowRight: PadKey.Right,
-    Enter: PadKey.Start,
-    " ": PadKey.Select,
-    a: PadKey.A,
-    s: PadKey.B
-};
-
 const KEYS_NAME: Record<string, number> = {
     ArrowUp: PadKey.Up,
     ArrowDown: PadKey.Down,
@@ -57,13 +44,6 @@ const KEYS_NAME: Record<string, number> = {
     B: PadKey.B
 };
 
-const ARROW_KEYS: Record<string, boolean> = {
-    ArrowUp: true,
-    ArrowDown: true,
-    ArrowLeft: true,
-    ArrowRight: true
-};
-
 const ROM_PATH = require("../../res/roms/20y.gb");
 
 /**
@@ -104,10 +84,6 @@ class GameboyEmulator extends EmulatorBase implements Emulator {
         // so that the global symbols become available
         await wasm();
 
-        // initializes the complete set of sub-systems
-        // and registers the event handlers
-        await this.register();
-
         // boots the emulator subsystem with the initial
         // ROM retrieved from a remote data source
         await this.boot({ loadRom: true, romPath: romUrl ?? undefined });
@@ -338,42 +314,6 @@ class GameboyEmulator extends EmulatorBase implements Emulator {
         this.trigger("booted");
     }
 
-    async register() {
-        await Promise.all([this.registerKeys()]);
-    }
-
-    registerKeys() {
-        document.addEventListener("keydown", (event) => {
-            const keyCode = KEYS[event.key];
-            const isArrow = ARROW_KEYS[event.key] ?? false;
-            if (isArrow) event.preventDefault();
-            if (keyCode !== undefined) {
-                this.gameBoy?.key_press(keyCode);
-                return;
-            }
-
-            switch (event.key) {
-                case "+":
-                    this.frequency += FREQUENCY_DELTA;
-                    break;
-
-                case "-":
-                    this.frequency -= FREQUENCY_DELTA;
-                    break;
-            }
-        });
-
-        document.addEventListener("keyup", (event) => {
-            const keyCode = KEYS[event.key];
-            const isArrow = ARROW_KEYS[event.key] ?? false;
-            if (isArrow) event.preventDefault();
-            if (keyCode !== undefined) {
-                this.gameBoy?.key_lift(keyCode);
-                return;
-            }
-        });
-    }
-
     setRom(name: string, data: Uint8Array, cartridge: Cartridge) {
         this.romName = name;
         this.romData = data;
@@ -454,6 +394,28 @@ class GameboyEmulator extends EmulatorBase implements Emulator {
         return this.fps;
     }
 
+    get registers(): Record<string, string | number> {
+        const registers = this.gameBoy?.registers();
+        if (!registers) return {};
+        return {
+            pc: registers.pc,
+            sp: registers.sp,
+            a: registers.a,
+            b: registers.b,
+            c: registers.c,
+            d: registers.d,
+            e: registers.e,
+            h: registers.h,
+            l: registers.l,
+            scy: registers.scy,
+            scx: registers.scx,
+            wy: registers.wy,
+            wx: registers.wx,
+            ly: registers.ly,
+            lyc: registers.lyc
+        };
+    }
+
     getTile(index: number): Uint8Array {
         return this.gameBoy?.get_tile_buffer(index) ?? new Uint8Array();
     }
@@ -536,28 +498,6 @@ class GameboyEmulator extends EmulatorBase implements Emulator {
             data: romData
         };
     }
-
-    get registers(): Record<string, string | number> {
-        const registers = this.gameBoy?.registers();
-        if (!registers) return {};
-        return {
-            pc: registers.pc,
-            sp: registers.sp,
-            a: registers.a,
-            b: registers.b,
-            c: registers.c,
-            d: registers.d,
-            e: registers.e,
-            h: registers.h,
-            l: registers.l,
-            scy: registers.scy,
-            scx: registers.scx,
-            wy: registers.wy,
-            wx: registers.wx,
-            ly: registers.ly,
-            lyc: registers.lyc
-        };
-    }
 }
 
 declare global {
diff --git a/examples/web/react/app.tsx b/examples/web/react/app.tsx
index f8982e1884ea4f665243e120630908c3f1b5a514..3f7cd2f147b17ac3ec20480ba2479811df293fdd 100644
--- a/examples/web/react/app.tsx
+++ b/examples/web/react/app.tsx
@@ -293,6 +293,14 @@ export const App: FC<AppProps> = ({
     }, [backgroundIndex]);
     useEffect(() => {
         switch (keyaction) {
+            case "Plus":
+                emulator.frequency += 400000;
+                setKeyaction(undefined);
+                break;
+            case "Minus":
+                emulator.frequency -= 400000;
+                setKeyaction(undefined);
+                break;
             case "Escape":
                 setFullscreenState(false);
                 setKeyaction(undefined);
@@ -305,10 +313,24 @@ export const App: FC<AppProps> = ({
     }, [keyaction]);
     useEffect(() => {
         const onKeyDown = (event: KeyboardEvent) => {
-            if (event.key === "Escape") {
-                setKeyaction("Escape");
-                event.stopPropagation();
-                event.preventDefault();
+            switch (event.key) {
+                case "+":
+                    setKeyaction("Plus");
+                    event.stopPropagation();
+                    event.preventDefault();
+                    break;
+
+                case "-":
+                    setKeyaction("Minus");
+                    event.stopPropagation();
+                    event.preventDefault();
+                    break;
+
+                case "Escape":
+                    setKeyaction("Escape");
+                    event.stopPropagation();
+                    event.preventDefault();
+                    break;
             }
             if (event.key === "f" && event.ctrlKey === true) {
                 setKeyaction("Fullscreen");
diff --git a/examples/web/react/components/keyboard-gb/keyboard-gb.tsx b/examples/web/react/components/keyboard-gb/keyboard-gb.tsx
index 0291cf8a808eae6a1014bb79a55c4de3efcf2cab..c59c31c02664ffbf64bec880edb263b4e1fd5355 100644
--- a/examples/web/react/components/keyboard-gb/keyboard-gb.tsx
+++ b/examples/web/react/components/keyboard-gb/keyboard-gb.tsx
@@ -1,7 +1,25 @@
-import React, { FC, useState } from "react";
+import React, { FC, useEffect, useRef, useState } from "react";
 
 import "./keyboard-gb.css";
 
+const KEYS: Record<string, string> = {
+    ArrowUp: "ArrowUp",
+    ArrowDown: "ArrowDown",
+    ArrowLeft: "ArrowLeft",
+    ArrowRight: "ArrowRight",
+    Enter: "Start",
+    " ": "Select",
+    a: "A",
+    s: "B"
+};
+
+const ARROW_KEYS: Record<string, boolean> = {
+    ArrowUp: true,
+    ArrowDown: true,
+    ArrowLeft: true,
+    ArrowRight: true
+};
+
 declare const require: any;
 
 type KeyboardGBProps = {
@@ -23,6 +41,8 @@ export const KeyboardGB: FC<KeyboardGBProps> = ({
 }) => {
     const containerClasses = () =>
         ["keyboard-container", fullscreen ? "fullscreen" : ""].join(" ");
+    const recordRef =
+        useRef<Record<string, React.Dispatch<React.SetStateAction<boolean>>>>();
     const classes = () =>
         [
             "keyboard",
@@ -30,6 +50,38 @@ export const KeyboardGB: FC<KeyboardGBProps> = ({
             fullscreen ? "fullscreen" : "",
             ...style
         ].join(" ");
+    useEffect(() => {
+        const _onKeyDown = (event: KeyboardEvent) => {
+            const keyCode = KEYS[event.key];
+            const isArrow = ARROW_KEYS[event.key] ?? false;
+            if (isArrow) event.preventDefault();
+            if (keyCode !== undefined) {
+                const records = recordRef.current ?? {};
+                const setter = records[keyCode];
+                setter(true);
+                onKeyDown && onKeyDown(keyCode);
+                return;
+            }
+        };
+        const _onKeyUp = (event: KeyboardEvent) => {
+            const keyCode = KEYS[event.key];
+            const isArrow = ARROW_KEYS[event.key] ?? false;
+            if (isArrow) event.preventDefault();
+            if (keyCode !== undefined) {
+                const records = recordRef.current ?? {};
+                const setter = records[keyCode];
+                setter(false);
+                onKeyUp && onKeyUp(keyCode);
+                return;
+            }
+        };
+        document.addEventListener("keydown", _onKeyDown);
+        document.addEventListener("keyup", _onKeyUp);
+        return () => {
+            document.removeEventListener("keydown", _onKeyDown);
+            document.removeEventListener("keyup", _onKeyUp);
+        };
+    }, []);
     const renderKey = (
         key: string,
         keyName?: string,
@@ -38,6 +90,9 @@ export const KeyboardGB: FC<KeyboardGBProps> = ({
     ) => {
         const [pressed, setPressed] = useState(selected);
         const classes = ["key", pressed ? "pressed" : "", ...styles].join(" ");
+        const records = recordRef.current ?? {};
+        records[keyName ?? key ?? "undefined"] = setPressed;
+        recordRef.current = records;
         return (
             <span
                 className={classes}