diff --git a/examples/web/index.css b/examples/web/index.css
index 850d93f7baf978e698ca0ee83e8f34f6d0722cdd..760e06b7131ed7a14d7247a220e149e38b29c5e9 100644
--- a/examples/web/index.css
+++ b/examples/web/index.css
@@ -308,108 +308,6 @@ p {
     -webkit-transform: scale(1.0, 1.0);
 }
 
-.tiny-button {
-    border-radius: 96px 96px 96px 96px;
-    -o-border-radius: 96px 96px 96px 96px;
-    -ms-border-radius: 96px 96px 96px 96px;
-    -moz-border-radius: 96px 96px 96px 96px;
-    -khtml-border-radius: 96px 96px 96px 96px;
-    -webkit-border-radius: 96px 96px 96px 96px;
-    cursor: pointer;
-    display: inline-block;
-    padding: 0px 8px 0px 8px;
-    user-select: none;
-    -o-user-select: none;
-    -ms-user-select: none;
-    -moz-user-select: none;
-    -khtml-user-select: none;
-    -webkit-user-select: none;
-}
-
-.tiny-button.border {
-    border: 1px solid #ffffff;
-}
-
-.tiny-button.padded {
-    padding: 4px 10px 4px 10px;
-}
-
-.tiny-button.padded-large {
-    padding: 4px 14px 4px 14px;
-}
-
-.tiny-button.rounded {
-    padding: 6px 6px 6px 6px;
-}
-
-.tiny-button.enabled {
-    background-color: #50cb93;
-}
-
-.tiny-button.file {
-    position: relative;
-}
-
-.tiny-button:hover {
-    background-color: #50cb93;
-}
-
-.tiny-button.red:hover {
-    background-color: #e63946;
-}
-
-.tiny-button:active {
-    background-color: #2a9d8f;
-}
-
-.tiny-button.red:active {
-    background-color: #bf2a37;
-}
-
-.tiny-button > img {
-    margin-right: 6px;
-    margin-top: 2px;
-    vertical-align: top;
-    width: 13px;
-}
-
-.tiny-button > img.medium {
-    width: 20px;
-}
-
-.tiny-button > img.large {
-    width: 28px;
-}
-
-.tiny-button > img.very-large {
-    width: 38px;
-}
-
-.tiny-button.no-text > img {
-    margin-right: 0px;
-    margin-top: 0px;
-}
-
-.tiny-button.file > input[type="file"] {
-    cursor: pointer;
-    height: 100%;
-    left: 0px;
-    opacity: 0;
-    -o-opacity: 0;
-    -ms-opacity: 0;
-    -moz-opacity: 0;
-    -khtml-opacity: 0;
-    -webkit-opacity: 0;
-    position: absolute;
-    top: 0px;
-    vertical-align: top;
-    width: 100%;
-}
-
-.tiny-button.file > input[type="file"]::-webkit-file-upload-button {
-    cursor: pointer;
-}
-
 .overlay {
     align-items: center;
     background-color: rgba(80, 203, 147, 0.95);
@@ -575,12 +473,12 @@ p {
     -webkit-user-select: none;
 }
 
-.modal-container > .modal .modal-buttons > .tiny-button {
+.modal-container > .modal .modal-buttons > .button.tiny {
     margin-right: 12px;
     min-width: 120px;
 }
 
-.modal-container > .modal .modal-buttons > .tiny-button:last-child {
+.modal-container > .modal .modal-buttons > .button.tiny:last-child {
     margin-right: 0px;
 }
 
diff --git a/examples/web/index.html b/examples/web/index.html
index cf2eabe02ff66a3186780d071d275b84ba009b82..96e55af52a1ea780ba04910b22e6937eb99dfc2d 100644
--- a/examples/web/index.html
+++ b/examples/web/index.html
@@ -77,16 +77,16 @@
             <div id="section-diag" class="section">
                 <dl class="diag">
                     <dt>Engine</dt>
-                    <dd id="engine" class="tiny-button">-</dd>
+                    <dd id="engine" class="button tiny">-</dd>
                     <dt>ROM</dt>
                     <dd id="rom-name">-</dd>
                     <dt>ROM Size</dt>
                     <dd><span id="rom-size">-</span> bytes</dd>
                     <dt>CPU Frequency</dt>
                     <dd>
-                        <span id="logic-frequency-minus" class="tiny-button">-</span>
+                        <span id="logic-frequency-minus" class="button tiny">-</span>
                         <span id="logic-frequency">-</span> Hz
-                        <span id="logic-frequency-plus" class="tiny-button">+</span></dd>
+                        <span id="logic-frequency-plus" class="button tiny">+</span></dd>
                     <dt>Framerate</dt>
                     <dd><span id="fps-count">-</span> fps</dd>
                 </dl>
@@ -94,31 +94,31 @@
             <div id="separator-diag" class="separator"></div>
             <div class="section">
                 <div class="button-area">
-                    <span id="button-pause" class="tiny-button border padded">
+                    <span id="button-pause" class="button tiny border padded">
                         <img src="res/pause.svg" alt="pause" /><span>Pause</span>
                     </span>
-                    <span id="button-reset" class="tiny-button border padded">
+                    <span id="button-reset" class="button tiny border padded">
                         <img src="res/reset.svg" alt="reset" /><span>Reset</span>
                     </span>
-                    <span id="button-benchmark" class="tiny-button border padded">
+                    <span id="button-benchmark" class="button tiny border padded">
                         <img src="res/bolt.svg" alt="bolt" /><span>Benchmark</span>
                     </span>
-                    <span id="button-fullscreen" class="tiny-button border padded">
+                    <span id="button-fullscreen" class="button tiny border padded">
                         <img src="res/maximise.svg" alt="maximise" /><span>Fullscreen</span>
                     </span>
-                    <span id="button-keyboard" class="tiny-button border padded">
+                    <span id="button-keyboard" class="button tiny border padded">
                         <img src="res/dialpad.svg" alt="info" /><span>Keyboard</span>
                     </span>
-                    <span id="button-information" class="tiny-button border padded enabled">
+                    <span id="button-information" class="button tiny border padded enabled">
                         <img src="res/info.svg" alt="info" /><span>Information</span>
                     </span>
-                    <span id="button-debug" class="tiny-button border padded">
+                    <span id="button-debug" class="button tiny border padded">
                         <img src="res/bug.svg" alt="bug" /><span>Debug</span>
                     </span>
-                    <span id="button-theme" class="tiny-button border padded">
+                    <span id="button-theme" class="button tiny border padded">
                         <img src="res/marker.svg" alt="marker" /><span>Theme</span>
                     </span>
-                    <span id="button-upload" class="tiny-button border padded file">
+                    <span id="button-upload" class="button tiny border padded file">
                         <img src="res/upload.svg" alt="upload" /><span>Load ROM</span>
                         <input type="file" id="button-upload-file" name="button-upload-file" accept=".gb">
                     </span>
@@ -133,15 +133,15 @@
 <div id="modal-container" class="modal-container">
     <div id="modal" class="modal">
         <div class="modal-top-buttons">
-            <span id="modal-close" class="tiny-button rounded no-text">
+            <span id="modal-close" class="button tiny rounded no-text">
                 <img class="medium" src="res/close.svg" alt="close" />
             </span>
         </div>
         <h2 id="modal-title" class="modal-title"></h2>
         <p id="modal-text" class="modal-text"></p>
         <div class="modal-buttons">
-            <span id="modal-cancel" class="tiny-button red border padded-large">Cancel</span>
-            <span id="modal-confirm" class="tiny-button border padded-large">Confirm</span>
+            <span id="modal-cancel" class="button tiny red border padded-large">Cancel</span>
+            <span id="modal-confirm" class="button tiny border padded-large">Confirm</span>
         </div>
     </div>
 </div>
diff --git a/examples/web/index.ts b/examples/web/index.ts
index 270a8246d8a560ddfe602c46e7fab458de440511..a690c1f61053d0b93cc5774a2deb58ba14fc53b3 100644
--- a/examples/web/index.ts
+++ b/examples/web/index.ts
@@ -1,5 +1,5 @@
-import { createApp } from "vue";
-import Boytacean from "./vue/app.vue";
+import { startApp } from "./react/app";
+
 import { default as _wasm, GameBoy, PadKey, PpuMode } from "./lib/boytacean.js";
 import info from "./package.json";
 
@@ -11,7 +11,7 @@ const TIMER_HZ = 60;
 const IDLE_HZ = 10;
 
 const FREQUENCY_DELTA = 60;
- 
+
 const DISPLAY_WIDTH = 160;
 const DISPLAY_HEIGHT = 144;
 const DISPLAY_RATIO = DISPLAY_WIDTH / DISPLAY_HEIGHT;
@@ -1099,9 +1099,7 @@ const wasm = async () => {
 };
 
 (async () => {
-    (globalThis as any).__VUE_OPTIONS_API__ = true;
-    (globalThis as any).__VUE_PROD_DEVTOOLS__ = false;
-    createApp(Boytacean).mount("#app");
+    startApp("app");
 
     const emulator = new Emulator();
     await emulator.main();
diff --git a/examples/web/package.json b/examples/web/package.json
index cb5ebe5407f63490f73e6551f1c2fd59881f0dc2..bd9fcb70798e3b72a042fa86fc302295e7cf3079 100644
--- a/examples/web/package.json
+++ b/examples/web/package.json
@@ -17,10 +17,13 @@
     "source": "index.ts",
     "devDependencies": {
         "@parcel/transformer-typescript-tsc": "^2.6.2",
-        "@parcel/transformer-vue": "^2.6.2",
+        "@types/react": "^18.0.15",
+        "@types/react-dom": "^18.0.6",
         "parcel": "^2.6.2",
         "prettier": "^2.7.1",
-        "typescript": "^4.7.4",
-        "vue": "^3.2.37"
+        "process": "^0.11.10",
+        "react": "^18.2.0",
+        "react-dom": "^18.2.0",
+        "typescript": "^4.7.4"
     }
 }
diff --git a/examples/web/react/app.css b/examples/web/react/app.css
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/examples/web/react/app.tsx b/examples/web/react/app.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..64bc0bf03460c257fbf1411810acd5f0f1900ab0
--- /dev/null
+++ b/examples/web/react/app.tsx
@@ -0,0 +1,18 @@
+import React from "react";
+import ReactDOM from "react-dom/client";
+
+import { Button } from "./components";
+
+import "./app.css";
+
+export const App = () => {
+    const getText = () => "Hello World";
+    return <Button text={ getText() } />;
+}
+
+export const startApp = (element: string) => {
+    const root = ReactDOM.createRoot(document.getElementById(element)!);
+    root.render(<App />);
+}
+
+export default App;
diff --git a/examples/web/react/components/button/button.css b/examples/web/react/components/button/button.css
new file mode 100644
index 0000000000000000000000000000000000000000..ccb443e871af171d789dd8f4b6ab7ed81492e7f8
--- /dev/null
+++ b/examples/web/react/components/button/button.css
@@ -0,0 +1,105 @@
+.button {
+    font-size: 12px;
+    cursor: pointer;
+}
+
+.button.tiny {
+    border-radius: 96px 96px 96px 96px;
+    -o-border-radius: 96px 96px 96px 96px;
+    -ms-border-radius: 96px 96px 96px 96px;
+    -moz-border-radius: 96px 96px 96px 96px;
+    -khtml-border-radius: 96px 96px 96px 96px;
+    -webkit-border-radius: 96px 96px 96px 96px;
+    display: inline-block;
+    padding: 0px 8px 0px 8px;
+    user-select: none;
+    -o-user-select: none;
+    -ms-user-select: none;
+    -moz-user-select: none;
+    -khtml-user-select: none;
+    -webkit-user-select: none;
+}
+
+.button.tiny.border {
+    border: 1px solid #ffffff;
+}
+
+.button.tiny.padded {
+    padding: 4px 10px 4px 10px;
+}
+
+.button.tiny.padded-large {
+    padding: 4px 14px 4px 14px;
+}
+
+.button.tiny.rounded {
+    padding: 6px 6px 6px 6px;
+}
+
+.button.tiny.enabled {
+    background-color: #50cb93;
+}
+
+.button.tiny.file {
+    position: relative;
+}
+
+.button.tiny:hover {
+    background-color: #50cb93;
+}
+
+.button.tiny.red:hover {
+    background-color: #e63946;
+}
+
+.button.tiny:active {
+    background-color: #2a9d8f;
+}
+
+.button.tiny.red:active {
+    background-color: #bf2a37;
+}
+
+.button.tiny > img {
+    margin-right: 6px;
+    margin-top: 2px;
+    vertical-align: top;
+    width: 13px;
+}
+
+.button.tiny > img.medium {
+    width: 20px;
+}
+
+.button.tiny > img.large {
+    width: 28px;
+}
+
+.button.tiny > img.very-large {
+    width: 38px;
+}
+
+.button.tiny.no-text > img {
+    margin-right: 0px;
+    margin-top: 0px;
+}
+
+.button.tiny.file > input[type="file"] {
+    cursor: pointer;
+    height: 100%;
+    left: 0px;
+    opacity: 0;
+    -o-opacity: 0;
+    -ms-opacity: 0;
+    -moz-opacity: 0;
+    -khtml-opacity: 0;
+    -webkit-opacity: 0;
+    position: absolute;
+    top: 0px;
+    vertical-align: top;
+    width: 100%;
+}
+
+.button.tiny.file > input[type="file"]::-webkit-file-upload-button {
+    cursor: pointer;
+}
\ No newline at end of file
diff --git a/examples/web/react/components/button/button.tsx b/examples/web/react/components/button/button.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..c8dda14cad07032861efccc585a29992f93d8c8f
--- /dev/null
+++ b/examples/web/react/components/button/button.tsx
@@ -0,0 +1,13 @@
+import React, { FC } from "react";
+
+import "./button.css";
+
+export const Button: FC<{ text: string, style?: string[] }> = ({ text, style = ["tiny", "border"] }) => {
+    const onClick = () => {
+        alert("Hello World");
+    }
+
+    const classes = () => ["button", ...style].join(" ")
+
+    return <span className={classes()} onClick={onClick}>{ text }</span>;
+}
diff --git a/examples/web/react/components/index.ts b/examples/web/react/components/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..34aa90a0cb858bd19cd0e9156b56e22e658ca03e
--- /dev/null
+++ b/examples/web/react/components/index.ts
@@ -0,0 +1 @@
+export * from "./button/button";
diff --git a/examples/web/shims-vue.d.ts b/examples/web/shims-vue.d.ts
deleted file mode 100644
index ce8ffebfab3ec34641cc0dcb25dc689796ddac09..0000000000000000000000000000000000000000
--- a/examples/web/shims-vue.d.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-declare module "*.vue" {
-    import Vue from "vue";
-    export default Vue;
-}
diff --git a/examples/web/tsconfig.json b/examples/web/tsconfig.json
index 8fbd457e4f663d87bd2c211a14d2354d6413c4c5..8f83dd8e31c5ab0b458c75bafaedad1b74ca9698 100644
--- a/examples/web/tsconfig.json
+++ b/examples/web/tsconfig.json
@@ -13,6 +13,7 @@
         "strictFunctionTypes": true,
         "strictPropertyInitialization": true,
         "sourceMap": true,
+        "jsx": "react",
         "outDir": ".",
         "baseUrl": ".",
         "lib": ["es2017", "dom"],
diff --git a/examples/web/vue/app.vue b/examples/web/vue/app.vue
deleted file mode 100644
index f9265d496a7f1dfbd8b9bd4b907e2451b5ea796d..0000000000000000000000000000000000000000
--- a/examples/web/vue/app.vue
+++ /dev/null
@@ -1,23 +0,0 @@
-<template>
-    <div v-bind:class="'hello'" v-on:click="() => count++">Hello {{ name }} {{ count}}!</div>
-</template>
-
-<style scoped>
-.hello {
-    cursor: pointer;
-    user-select: none;
-}
-</style>
-
-<script lang="ts">
-export const Boytacean = {
-    data() {
-        return {
-            name: "Vue",
-            count: 1
-        };
-    }
-};
-
-export default Boytacean;
-</script>
diff --git a/examples/web/vue/components/button/button.vue b/examples/web/vue/components/button/button.vue
deleted file mode 100644
index fe4c1679e018f02dfbe6817ecca95552f214e41a..0000000000000000000000000000000000000000
--- a/examples/web/vue/components/button/button.vue
+++ /dev/null
@@ -1,16 +0,0 @@
-<template>
-    <div v-on:click="() => count++">Hello {{ name }} {{ count}}!</div>
-</template>
-
-<script>
-export const Button = {
-    data() {
-        return {
-            name: "Vue",
-            count: 1
-        };
-    }
-};
-
-export default Button;
-</script>
diff --git a/examples/web/vue/components/index.ts b/examples/web/vue/components/index.ts
deleted file mode 100644
index d54b4044ae173dbdfd4a88f2108f0fc123255160..0000000000000000000000000000000000000000
--- a/examples/web/vue/components/index.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { App } from "vue";
-
-import Button from "./button/button.vue";
-
-const install = (Vue: App) => {
-    Vue.component("vue", Button);
-};
-
-export {
-    Button
-};
-
-export default install;
diff --git a/examples/web/vue/index.ts b/examples/web/vue/index.ts
deleted file mode 100644
index 3b2fc5285d3463d54079e7d72d6d3ba800fb0f8c..0000000000000000000000000000000000000000
--- a/examples/web/vue/index.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { App } from "vue";
-
-import Components from "./components";
-
-const install = (Vue: App) => {
-    Vue.use(Components);
-};
-
-export * from "./components";
-
-export default install;