diff --git a/examples/web/chip_ahoyto.d.ts b/examples/web/chip_ahoyto.d.ts
index f7ec932319bfaa08932b3ae56c837cdccee942d8..77a84e7b09140f1e0bc08057123af9e331d6602c 100644
--- a/examples/web/chip_ahoyto.d.ts
+++ b/examples/web/chip_ahoyto.d.ts
@@ -38,6 +38,14 @@ export class Chip8Neo {
 /**
 */
   clock_st_ws(): void;
+/**
+* @param {number} key
+*/
+  key_press_ws(key: number): void;
+/**
+* @param {number} key
+*/
+  key_lift_ws(key: number): void;
 }
 
 export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
@@ -53,6 +61,8 @@ export interface InitOutput {
   readonly chip8neo_clock_ws: (a: number) => void;
   readonly chip8neo_clock_dt_ws: (a: number) => void;
   readonly chip8neo_clock_st_ws: (a: number) => void;
+  readonly chip8neo_key_press_ws: (a: number, b: number) => void;
+  readonly chip8neo_key_lift_ws: (a: number, b: number) => void;
   readonly __wbg_chip8classic_free: (a: number) => void;
   readonly chip8classic_new: () => number;
   readonly __wbindgen_malloc: (a: number) => number;
diff --git a/examples/web/chip_ahoyto.js b/examples/web/chip_ahoyto.js
index f66794cc02fdde22d7e4adba7c682f0f046c8971..c83bdce964192eb55dedb6c28076f9889d7a43fa 100644
--- a/examples/web/chip_ahoyto.js
+++ b/examples/web/chip_ahoyto.js
@@ -180,6 +180,18 @@ export class Chip8Neo {
     clock_st_ws() {
         wasm.chip8neo_clock_st_ws(this.ptr);
     }
+    /**
+    * @param {number} key
+    */
+    key_press_ws(key) {
+        wasm.chip8neo_key_press_ws(this.ptr, key);
+    }
+    /**
+    * @param {number} key
+    */
+    key_lift_ws(key) {
+        wasm.chip8neo_key_lift_ws(this.ptr, key);
+    }
 }
 
 async function load(module, imports) {
diff --git a/examples/web/chip_ahoyto_bg.wasm b/examples/web/chip_ahoyto_bg.wasm
index 863046d35e007e90f80b2f3235a5beb152ce8643..8ebcfa0bd0912d5f934cf9ea18f88daeb2dd8275 100644
Binary files a/examples/web/chip_ahoyto_bg.wasm and b/examples/web/chip_ahoyto_bg.wasm differ
diff --git a/examples/web/chip_ahoyto_bg.wasm.d.ts b/examples/web/chip_ahoyto_bg.wasm.d.ts
index 20b66726d8b15bbaf361078b41cdbca4932d4a0a..d5813e8d652675613b57214d6de048b5aa3d2b01 100644
--- a/examples/web/chip_ahoyto_bg.wasm.d.ts
+++ b/examples/web/chip_ahoyto_bg.wasm.d.ts
@@ -10,6 +10,8 @@ export function chip8neo_vram_ws(a: number, b: number): void;
 export function chip8neo_clock_ws(a: number): void;
 export function chip8neo_clock_dt_ws(a: number): void;
 export function chip8neo_clock_st_ws(a: number): void;
+export function chip8neo_key_press_ws(a: number, b: number): void;
+export function chip8neo_key_lift_ws(a: number, b: number): void;
 export function __wbg_chip8classic_free(a: number): void;
 export function chip8classic_new(): number;
 export function __wbindgen_malloc(a: number): number;
diff --git a/examples/web/index.js b/examples/web/index.js
index 65ad984fc876f9df8ed4c0debf89aa7bdec20c28..4ba8e46f5d471b67b6984f7bda49d87e33044b3a 100644
--- a/examples/web/index.js
+++ b/examples/web/index.js
@@ -6,10 +6,29 @@ import {
 const PIXEL_SET_COLOR = 0x50cb93ff;
 const PIXEL_UNSET_COLOR = 0x1b1a17ff;
 
-const LOGIC_HZ = 480;
+let LOGIC_HZ = 480;
 const TIMER_HZ = 60;
 const VISUAL_HZ = 60;
 
+const KEYS = {
+    "1": 0x01,
+    "2": 0x02,
+    "3": 0x03,
+    "4": 0x0c,
+    "q": 0x04,
+    "w": 0x05,
+    "e": 0x06,
+    "r": 0x0d,
+    "a": 0x07,
+    "s": 0x08,
+    "d": 0x09,
+    "f": 0x0e,
+    "z": 0x0a,
+    "x": 0x00,
+    "c": 0x0b,
+    "v": 0x0f
+}
+
 const ROM = "res/roms/pong.ch8";
 
 const state = {
@@ -27,9 +46,10 @@ const state = {
     // so that the global symbols become available
     await wasm();
 
-    // initializes the canvas sub-sytem
-    initCanvas();
-    registerDrop();
+    // initializes the complete set of sub-systems
+    // and registers the event handlers
+    init();
+    register();
 
     // loads the ROM data and converts it into the
     // target u8 array bufffer
@@ -69,14 +89,19 @@ const state = {
     }
 })();
 
+const register = () => {
+    registerDrop();
+    registerKeys();
+}
+
 const registerDrop = () => {
-    document.addEventListener("drop", async (e) => {
-        e.preventDefault();
-        e.stopPropagation();
+    document.addEventListener("drop", async (event) => {
+        event.preventDefault();
+        event.stopPropagation();
 
-        if (!e.dataTransfer.files) return;
+        if (!event.dataTransfer.files) return;
 
-        const file = e.dataTransfer.files[0];
+        const file = event.dataTransfer.files[0];
 
         const arrayBuffer = await file.arrayBuffer();
         const data = new Uint8Array(arrayBuffer);
@@ -84,12 +109,44 @@ const registerDrop = () => {
         state.chip8.reset_hard_ws();
         state.chip8.load_rom_ws(data);
     });
-    document.addEventListener("dragover", async (e) => {
-        e.preventDefault();
-        e.stopPropagation();
+    document.addEventListener("dragover", async (event) => {
+        event.preventDefault();
+        event.stopPropagation();
 
         console.info("draging over");
     });
+};
+
+const registerKeys = () => {
+    document.addEventListener("keydown", (event) => {
+        const keyCode = KEYS[event.key];
+        if (keyCode) {
+            state.chip8.key_press_ws(keyCode);
+            return;
+        }
+
+        switch(event.key) {
+            case "+":
+                LOGIC_HZ += 60;
+                break;
+
+            case "-":
+                LOGIC_HZ += 60;
+                break;
+        }
+    });
+
+    document.addEventListener("keyup", (event) => {
+        const keyCode = KEYS[event.key];
+        if (keyCode) {
+            state.chip8.key_lift_ws(keyCode);
+            return;
+        }
+    });
+}
+
+const init = () => {
+    initCanvas();
 }
 
 const initCanvas = () => {
@@ -108,7 +165,7 @@ const initCanvas = () => {
 
     state.image = state.canvasCtx.createImageData(state.canvas.width, state.canvas.height);
     state.videoBuff = new DataView(state.image.data.buffer);
-}
+};
 
 const updateCanvas = (pixels) => {
     for (let i = 0; i < pixels.length; i++) {
@@ -116,4 +173,4 @@ const updateCanvas = (pixels) => {
     }
     state.canvasCtx.putImageData(state.image, 0, 0);
     state.canvasScaledCtx.drawImage(state.canvas, 0, 0);
-}
+};
diff --git a/src/chip8_neo.rs b/src/chip8_neo.rs
index 17a7c82e5203733a34ef24bef740cd92eb53a84a..fa81ef540b7b1412a4d6e964e06172e64ca5e932 100644
--- a/src/chip8_neo.rs
+++ b/src/chip8_neo.rs
@@ -393,6 +393,14 @@ impl Chip8Neo {
     pub fn clock_st_ws(&mut self) {
         self.clock_st()
     }
+
+    pub fn key_press_ws(&mut self, key: u8) {
+        self.key_press(key)
+    }
+
+    pub fn key_lift_ws(&mut self, key: u8) {
+        self.key_lift(key)
+    }
 }
 
 impl Default for Chip8Neo {