diff --git a/examples/web/gb.ts b/examples/web/gb.ts
new file mode 100644
index 0000000000000000000000000000000000000000..dd4ac3d0b4197e1be4e25cb1984bb396a77f8974
--- /dev/null
+++ b/examples/web/gb.ts
@@ -0,0 +1,538 @@
+import {
+    Emulator,
+    EmulatorBase,
+    PixelFormat,
+    RomInfo
+} from "./react/app";
+
+import {
+    Cartridge,
+    default as _wasm,
+    GameBoy,
+    PadKey,
+    PpuMode
+} from "./lib/boytacean.js";
+import info from "./package.json";
+
+declare const require: any;
+
+const LOGIC_HZ = 4194304;
+const VISUAL_HZ = 59.7275;
+const IDLE_HZ = 10;
+
+const SAMPLE_RATE = 2;
+
+const PALETTES = [
+    {
+        name: "basic",
+        colors: ["ffffff", "c0c0c0", "606060", "000000"]
+    },
+    {
+        name: "hogwards",
+        colors: ["b6a571", "8b7e56", "554d35", "201d13"]
+    },
+    {
+        name: "pacman",
+        colors: ["ffff00", "ffb897", "3732ff", "000000"]
+    },
+    {
+        name: "mariobros",
+        colors: ["f7cec3", "cc9e22", "923404", "000000"]
+    }
+];
+
+const KEYS_NAME: Record<string, number> = {
+    ArrowUp: PadKey.Up,
+    ArrowDown: PadKey.Down,
+    ArrowLeft: PadKey.Left,
+    ArrowRight: PadKey.Right,
+    Start: PadKey.Start,
+    Select: PadKey.Select,
+    A: PadKey.A,
+    B: PadKey.B
+};
+
+const ROM_PATH = require("../../res/roms/20y.gb");
+
+/**
+ * Top level class that controls the emulator behaviour
+ * and "joins" all the elements together to bring input/output
+ * of the associated machine.
+ */
+export class GameboyEmulator extends EmulatorBase implements Emulator {
+    /**
+     * The Game Boy engine (probably coming from WASM) that
+     * is going to be used for the emulation.
+     */
+    private gameBoy: GameBoy | null = null;
+
+    /**
+     * The descriptive name of the engine that is currently
+     * in use to emulate the system.
+     */
+    private _engine: string | null = null;
+
+    private logicFrequency: number = LOGIC_HZ;
+    private visualFrequency: number = VISUAL_HZ;
+    private idleFrequency: number = IDLE_HZ;
+
+    private paused: boolean = false;
+    private nextTickTime: number = 0;
+    private fps: number = 0;
+    private frameStart: number = new Date().getTime();
+    private frameCount: number = 0;
+    private paletteIndex: number = 0;
+
+    private romName: string | null = null;
+    private romData: Uint8Array | null = null;
+    private romSize: number = 0;
+    private cartridge: Cartridge | null = null;
+
+    async main({ romUrl }: { romUrl?: string }) {
+        // initializes the WASM module, this is required
+        // so that the global symbols become available
+        await wasm();
+
+        // boots the emulator subsystem with the initial
+        // ROM retrieved from a remote data source
+        await this.boot({ loadRom: true, romPath: romUrl ?? undefined });
+
+        // the counter that controls the overflowing cycles
+        // from tick to tick operation
+        let pending = 0;
+
+        // runs the sequence as an infinite loop, running
+        // the associated CPU cycles accordingly
+        while (true) {
+            // in case the machine is paused we must delay the execution
+            // a little bit until the paused state is recovered
+            if (this.paused) {
+                await new Promise((resolve) => {
+                    setTimeout(resolve, 1000 / this.idleFrequency);
+                });
+                continue;
+            }
+
+            // obtains the current time, this value is going
+            // to be used to compute the need for tick computation
+            let currentTime = new Date().getTime();
+
+            try {
+                pending = this.tick(
+                    currentTime,
+                    pending,
+                    Math.round(this.logicFrequency / this.visualFrequency)
+                );
+            } catch (err) {
+                // sets the default error message to be displayed
+                // to the user, this value may be overridden in case
+                // a better and more explicit message can be determined
+                let message = String(err);
+
+                // verifies if the current issue is a panic one
+                // and updates the message value if that's the case
+                const messageNormalized = (err as Error).message.toLowerCase();
+                const isPanic =
+                    messageNormalized.startsWith("unreachable") ||
+                    messageNormalized.startsWith("recursive use of an object");
+                if (isPanic) {
+                    message = "Unrecoverable error, restarting Game Boy";
+                }
+
+                // displays the error information to both the end-user
+                // and the developer (for diagnostics)
+                this.trigger("message", {
+                    text: message,
+                    error: true,
+                    timeout: 5000
+                });
+                console.error(err);
+
+                // pauses the machine, allowing the end-user to act
+                // on the error in a proper fashion
+                this.pause();
+
+                // if we're talking about a panic, proper action must be taken
+                // which in this case it means restarting both the WASM sub
+                // system and the machine state (to be able to recover)
+                // also sets the default color on screen to indicate the issue
+                if (isPanic) {
+                    await wasm();
+                    await this.boot({ restore: false });
+
+                    this.trigger("error");
+                }
+            }
+
+            // calculates the amount of time until the next draw operation
+            // this is the amount of time that is going to be pending
+            currentTime = new Date().getTime();
+            const pendingTime = Math.max(this.nextTickTime - currentTime, 0);
+
+            // waits a little bit for the next frame to be draw,
+            // this should control the flow of render
+            await new Promise((resolve) => {
+                setTimeout(resolve, pendingTime);
+            });
+        }
+    }
+
+    tick(currentTime: number, pending: number, cycles: number = 70224) {
+        // in case the time to draw the next frame has not been
+        // reached the flush of the "tick" logic is skipped
+        if (currentTime < this.nextTickTime) return pending;
+
+        // calculates the number of ticks that have elapsed since the
+        // last draw operation, this is critical to be able to properly
+        // operate the clock of the CPU in frame drop situations
+        if (this.nextTickTime === 0) this.nextTickTime = currentTime;
+        let ticks = Math.ceil(
+            (currentTime - this.nextTickTime) /
+                ((1 / this.visualFrequency) * 1000)
+        );
+        ticks = Math.max(ticks, 1);
+
+        // initializes the counter of cycles with the pending number
+        // of cycles coming from the previous tick
+        let counterCycles = pending;
+
+        let lastFrame = -1;
+
+        while (true) {
+            // limits the number of cycles to the provided
+            // cycle value passed as a parameter
+            if (counterCycles >= cycles) {
+                break;
+            }
+
+            // runs the Game Boy clock, this operations should
+            // include the advance of both the CPU and the PPU
+            counterCycles += this.gameBoy?.clock() ?? 0;
+
+            // in case the current PPU mode is VBlank and the
+            // frame is different from the previously rendered
+            // one then it's time to update the canvas
+            if (
+                this.gameBoy?.ppu_mode() === PpuMode.VBlank &&
+                this.gameBoy?.ppu_frame() !== lastFrame
+            ) {
+                lastFrame = this.gameBoy?.ppu_frame();
+
+                // triggers the frame event indicating that
+                // a new frame is now available for drawing
+                this.trigger("frame");
+            }
+        }
+
+        // increments the number of frames rendered in the current
+        // section, this value is going to be used to calculate FPS
+        this.frameCount += 1;
+
+        // in case the target number of frames for FPS control
+        // has been reached calculates the number of FPS and
+        // flushes the value to the screen
+        if (this.frameCount >= this.visualFrequency * SAMPLE_RATE) {
+            const currentTime = new Date().getTime();
+            const deltaTime = (currentTime - this.frameStart) / 1000;
+            const fps = Math.round(this.frameCount / deltaTime);
+            this.fps = fps;
+            this.frameCount = 0;
+            this.frameStart = currentTime;
+        }
+
+        // updates the next update time reference to the, so that it
+        // can be used to control the game loop
+        this.nextTickTime += (1000 / this.visualFrequency) * ticks;
+
+        // calculates the new number of pending (overflow) cycles
+        // that are going to be added to the next iteration
+        return counterCycles - cycles;
+    }
+
+    /**
+     * Starts the current machine, setting the internal structure in
+     * a proper state to start drawing and receiving input.
+     *
+     * This method can also be used to load a new ROM into the machine.
+     *
+     * @param options The options that are going to be used in the
+     * starting of the machine, includes information on the ROM and
+     * the emulator engine to use.
+     */
+    async boot({
+        engine = "neo",
+        restore = true,
+        loadRom = false,
+        romPath = ROM_PATH,
+        romName = null,
+        romData = null
+    }: {
+        engine?: string | null;
+        restore?: boolean;
+        loadRom?: boolean;
+        romPath?: string;
+        romName?: string | null;
+        romData?: Uint8Array | null;
+    } = {}) {
+        // in case a remote ROM loading operation has been
+        // requested then loads it from the remote origin
+        if (loadRom) {
+            ({ name: romName, data: romData } = await this.fetchRom(romPath));
+        } else if (romName === null || romData === null) {
+            [romName, romData] = [this.romName, this.romData];
+        }
+
+        // selects the proper engine for execution
+        // and builds a new instance of it
+        switch (engine) {
+            case "neo":
+                this.gameBoy = new GameBoy();
+                break;
+            default:
+                if (!this.gameBoy) {
+                    throw new Error("No engine requested");
+                }
+                break;
+        }
+
+        // runs the initial palette update operation
+        this.updatePalette();
+
+        // resets the Game Boy engine to restore it into
+        // a valid state ready to be used
+        this.gameBoy.reset();
+        this.gameBoy.load_boot_default();
+        const cartridge = this.gameBoy.load_rom_ws(romData!);
+
+        // updates the ROM name in case there's extra information
+        // coming from the cartridge
+        romName = cartridge.title() ? cartridge.title() : romName;
+
+        // updates the name of the currently selected engine
+        // to the one that has been provided (logic change)
+        if (engine) this._engine = engine;
+
+        // updates the complete set of global information that
+        // is going to be displayed
+        this.setRom(romName!, romData!, cartridge);
+
+        // in case the restore (state) flag is set
+        // then resumes the machine execution
+        if (restore) this.resume();
+
+        // triggers the booted event indicating that the
+        // emulator has finished the loading process
+        this.trigger("booted");
+    }
+
+    setRom(name: string, data: Uint8Array, cartridge: Cartridge) {
+        this.romName = name;
+        this.romData = data;
+        this.romSize = data.length;
+        this.cartridge = cartridge;
+    }
+
+    get name(): string {
+        return "Boytacean";
+    }
+
+    get device(): string {
+        return "Game Boy";
+    }
+
+    get deviceUrl(): string {
+        return "https://en.wikipedia.org/wiki/Game_Boy";
+    }
+
+    get engines() {
+        return ["neo"];
+    }
+
+    get engine() {
+        return this._engine;
+    }
+
+    get version(): string {
+        return info.version;
+    }
+
+    get versionUrl(): string {
+        return "https://gitlab.stage.hive.pt/joamag/boytacean/-/blob/master/CHANGELOG.md";
+    }
+
+    get romExts(): string[] {
+        return ["gb"];
+    }
+
+    get pixelFormat(): PixelFormat {
+        return PixelFormat.RGB;
+    }
+
+    /**
+     * Returns the array buffer that contains the complete set of
+     * pixel data that is going to be drawn.
+     *
+     * @returns The current pixel data for the emulator display.
+     */
+    get imageBuffer(): Uint8Array {
+        return this.gameBoy?.frame_buffer_eager() ?? new Uint8Array();
+    }
+
+    get romInfo(): RomInfo {
+        return {
+            name: this.romName ?? undefined,
+            data: this.romData ?? undefined,
+            size: this.romSize,
+            extra: {
+                romType: this.cartridge?.rom_type_s(),
+                romSize: this.cartridge?.rom_size_s(),
+                ramSize: this.cartridge?.ram_size_s()
+            }
+        };
+    }
+
+    get frequency(): number {
+        return this.logicFrequency;
+    }
+
+    set frequency(value: number) {
+        value = Math.max(value, 0);
+        this.logicFrequency = value;
+        this.trigger("frequency", value);
+    }
+
+    get frequencyDelta(): number | null {
+        return 400000;
+    }
+
+    get framerate(): number {
+        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();
+    }
+
+    toggleRunning() {
+        if (this.paused) {
+            this.resume();
+        } else {
+            this.pause();
+        }
+    }
+
+    pause() {
+        this.paused = true;
+    }
+
+    resume() {
+        this.paused = false;
+        this.nextTickTime = new Date().getTime();
+    }
+
+    reset() {
+        this.boot({ engine: null });
+    }
+
+    keyPress(key: string) {
+        const keyCode = KEYS_NAME[key];
+        if (keyCode === undefined) return;
+        this.gameBoy?.key_press(keyCode);
+    }
+
+    keyLift(key: string) {
+        const keyCode = KEYS_NAME[key];
+        if (keyCode === undefined) return;
+        this.gameBoy?.key_lift(keyCode);
+    }
+
+    updatePalette() {
+        const palette = PALETTES[this.paletteIndex];
+        this.gameBoy?.set_palette_colors_ws(palette.colors);
+        this.paletteIndex += 1;
+        this.paletteIndex %= PALETTES.length;
+    }
+
+    benchmark(count = 50000000) {
+        let cycles = 0;
+        this.pause();
+        try {
+            const initial = Date.now();
+            for (let i = 0; i < count; i++) {
+                cycles += this.gameBoy?.clock() ?? 0;
+            }
+            const delta = (Date.now() - initial) / 1000;
+            const frequency_mhz = cycles / delta / 1000 / 1000;
+            return {
+                delta: delta,
+                count: count,
+                cycles: cycles,
+                frequency_mhz: frequency_mhz
+            };
+        } finally {
+            this.resume();
+        }
+    }
+
+    private async fetchRom(
+        romPath: string
+    ): Promise<{ name: string; data: Uint8Array }> {
+        // extracts the name of the ROM from the provided
+        // path by splitting its structure
+        const romPathS = romPath.split(/\//g);
+        let romName = romPathS[romPathS.length - 1].split("?")[0];
+        const romNameS = romName.split(/\./g);
+        romName = `${romNameS[0]}.${romNameS[romNameS.length - 1]}`;
+
+        // loads the ROM data and converts it into the
+        // target byte array buffer (to be used by WASM)
+        const response = await fetch(romPath);
+        const blob = await response.blob();
+        const arrayBuffer = await blob.arrayBuffer();
+        const romData = new Uint8Array(arrayBuffer);
+
+        // returns both the name of the ROM and the data
+        // contents as a byte array
+        return {
+            name: romName,
+            data: romData
+        };
+    }
+}
+
+declare global {
+    interface Window {
+        panic: (message: string) => void;
+    }
+}
+
+window.panic = (message: string) => {
+    console.error(message);
+};
+
+const wasm = async () => {
+    await _wasm();
+    GameBoy.set_panic_hook_ws();
+};
diff --git a/examples/web/index.ts b/examples/web/index.ts
index 1e0810911d13b251069181df88402ca068412085..7b114fed0d75a82f8f61a612dea1a5d891bd989a 100644
--- a/examples/web/index.ts
+++ b/examples/web/index.ts
@@ -1,27 +1,5 @@
-import {
-    Emulator,
-    EmulatorBase,
-    PixelFormat,
-    RomInfo,
-    startApp
-} from "./react/app";
-
-import {
-    Cartridge,
-    default as _wasm,
-    GameBoy,
-    PadKey,
-    PpuMode
-} from "./lib/boytacean.js";
-import info from "./package.json";
-
-declare const require: any;
-
-const LOGIC_HZ = 4194304;
-const VISUAL_HZ = 59.7275;
-const IDLE_HZ = 10;
-
-const SAMPLE_RATE = 2;
+import { startApp } from "./react/app";
+import { GameboyEmulator } from "./gb";
 
 const BACKGROUNDS = [
     "264653",
@@ -33,521 +11,6 @@ const BACKGROUNDS = [
     "3a5a40"
 ];
 
-const PALETTES = [
-    {
-        name: "basic",
-        colors: ["ffffff", "c0c0c0", "606060", "000000"]
-    },
-    {
-        name: "hogwards",
-        colors: ["b6a571", "8b7e56", "554d35", "201d13"]
-    },
-    {
-        name: "pacman",
-        colors: ["ffff00", "ffb897", "3732ff", "000000"]
-    },
-    {
-        name: "mariobros",
-        colors: ["f7cec3", "cc9e22", "923404", "000000"]
-    }
-];
-
-const KEYS_NAME: Record<string, number> = {
-    ArrowUp: PadKey.Up,
-    ArrowDown: PadKey.Down,
-    ArrowLeft: PadKey.Left,
-    ArrowRight: PadKey.Right,
-    Start: PadKey.Start,
-    Select: PadKey.Select,
-    A: PadKey.A,
-    B: PadKey.B
-};
-
-const ROM_PATH = require("../../res/roms/20y.gb");
-
-/**
- * Top level class that controls the emulator behaviour
- * and "joins" all the elements together to bring input/output
- * of the associated machine.
- */
-class GameboyEmulator extends EmulatorBase implements Emulator {
-    /**
-     * The Game Boy engine (probably coming from WASM) that
-     * is going to be used for the emulation.
-     */
-    private gameBoy: GameBoy | null = null;
-
-    /**
-     * The descriptive name of the engine that is currently
-     * in use to emulate the system.
-     */
-    private _engine: string | null = null;
-
-    private logicFrequency: number = LOGIC_HZ;
-    private visualFrequency: number = VISUAL_HZ;
-    private idleFrequency: number = IDLE_HZ;
-
-    private paused: boolean = false;
-    private nextTickTime: number = 0;
-    private fps: number = 0;
-    private frameStart: number = new Date().getTime();
-    private frameCount: number = 0;
-    private paletteIndex: number = 0;
-
-    private romName: string | null = null;
-    private romData: Uint8Array | null = null;
-    private romSize: number = 0;
-    private cartridge: Cartridge | null = null;
-
-    async main({ romUrl }: { romUrl?: string }) {
-        // initializes the WASM module, this is required
-        // so that the global symbols become available
-        await wasm();
-
-        // boots the emulator subsystem with the initial
-        // ROM retrieved from a remote data source
-        await this.boot({ loadRom: true, romPath: romUrl ?? undefined });
-
-        // the counter that controls the overflowing cycles
-        // from tick to tick operation
-        let pending = 0;
-
-        // runs the sequence as an infinite loop, running
-        // the associated CPU cycles accordingly
-        while (true) {
-            // in case the machine is paused we must delay the execution
-            // a little bit until the paused state is recovered
-            if (this.paused) {
-                await new Promise((resolve) => {
-                    setTimeout(resolve, 1000 / this.idleFrequency);
-                });
-                continue;
-            }
-
-            // obtains the current time, this value is going
-            // to be used to compute the need for tick computation
-            let currentTime = new Date().getTime();
-
-            try {
-                pending = this.tick(
-                    currentTime,
-                    pending,
-                    Math.round(this.logicFrequency / this.visualFrequency)
-                );
-            } catch (err) {
-                // sets the default error message to be displayed
-                // to the user, this value may be overridden in case
-                // a better and more explicit message can be determined
-                let message = String(err);
-
-                // verifies if the current issue is a panic one
-                // and updates the message value if that's the case
-                const messageNormalized = (err as Error).message.toLowerCase();
-                const isPanic =
-                    messageNormalized.startsWith("unreachable") ||
-                    messageNormalized.startsWith("recursive use of an object");
-                if (isPanic) {
-                    message = "Unrecoverable error, restarting Game Boy";
-                }
-
-                // displays the error information to both the end-user
-                // and the developer (for diagnostics)
-                this.trigger("message", {
-                    text: message,
-                    error: true,
-                    timeout: 5000
-                });
-                console.error(err);
-
-                // pauses the machine, allowing the end-user to act
-                // on the error in a proper fashion
-                this.pause();
-
-                // if we're talking about a panic, proper action must be taken
-                // which in this case it means restarting both the WASM sub
-                // system and the machine state (to be able to recover)
-                // also sets the default color on screen to indicate the issue
-                if (isPanic) {
-                    await wasm();
-                    await this.boot({ restore: false });
-
-                    this.trigger("error");
-                }
-            }
-
-            // calculates the amount of time until the next draw operation
-            // this is the amount of time that is going to be pending
-            currentTime = new Date().getTime();
-            const pendingTime = Math.max(this.nextTickTime - currentTime, 0);
-
-            // waits a little bit for the next frame to be draw,
-            // this should control the flow of render
-            await new Promise((resolve) => {
-                setTimeout(resolve, pendingTime);
-            });
-        }
-    }
-
-    tick(currentTime: number, pending: number, cycles: number = 70224) {
-        // in case the time to draw the next frame has not been
-        // reached the flush of the "tick" logic is skipped
-        if (currentTime < this.nextTickTime) return pending;
-
-        // calculates the number of ticks that have elapsed since the
-        // last draw operation, this is critical to be able to properly
-        // operate the clock of the CPU in frame drop situations
-        if (this.nextTickTime === 0) this.nextTickTime = currentTime;
-        let ticks = Math.ceil(
-            (currentTime - this.nextTickTime) /
-                ((1 / this.visualFrequency) * 1000)
-        );
-        ticks = Math.max(ticks, 1);
-
-        // initializes the counter of cycles with the pending number
-        // of cycles coming from the previous tick
-        let counterCycles = pending;
-
-        let lastFrame = -1;
-
-        while (true) {
-            // limits the number of cycles to the provided
-            // cycle value passed as a parameter
-            if (counterCycles >= cycles) {
-                break;
-            }
-
-            // runs the Game Boy clock, this operations should
-            // include the advance of both the CPU and the PPU
-            counterCycles += this.gameBoy?.clock() ?? 0;
-
-            // in case the current PPU mode is VBlank and the
-            // frame is different from the previously rendered
-            // one then it's time to update the canvas
-            if (
-                this.gameBoy?.ppu_mode() === PpuMode.VBlank &&
-                this.gameBoy?.ppu_frame() !== lastFrame
-            ) {
-                lastFrame = this.gameBoy?.ppu_frame();
-
-                // triggers the frame event indicating that
-                // a new frame is now available for drawing
-                this.trigger("frame");
-            }
-        }
-
-        // increments the number of frames rendered in the current
-        // section, this value is going to be used to calculate FPS
-        this.frameCount += 1;
-
-        // in case the target number of frames for FPS control
-        // has been reached calculates the number of FPS and
-        // flushes the value to the screen
-        if (this.frameCount >= this.visualFrequency * SAMPLE_RATE) {
-            const currentTime = new Date().getTime();
-            const deltaTime = (currentTime - this.frameStart) / 1000;
-            const fps = Math.round(this.frameCount / deltaTime);
-            this.fps = fps;
-            this.frameCount = 0;
-            this.frameStart = currentTime;
-        }
-
-        // updates the next update time reference to the, so that it
-        // can be used to control the game loop
-        this.nextTickTime += (1000 / this.visualFrequency) * ticks;
-
-        // calculates the new number of pending (overflow) cycles
-        // that are going to be added to the next iteration
-        return counterCycles - cycles;
-    }
-
-    /**
-     * Starts the current machine, setting the internal structure in
-     * a proper state to start drawing and receiving input.
-     *
-     * This method can also be used to load a new ROM into the machine.
-     *
-     * @param options The options that are going to be used in the
-     * starting of the machine, includes information on the ROM and
-     * the emulator engine to use.
-     */
-    async boot({
-        engine = "neo",
-        restore = true,
-        loadRom = false,
-        romPath = ROM_PATH,
-        romName = null,
-        romData = null
-    }: {
-        engine?: string | null;
-        restore?: boolean;
-        loadRom?: boolean;
-        romPath?: string;
-        romName?: string | null;
-        romData?: Uint8Array | null;
-    } = {}) {
-        // in case a remote ROM loading operation has been
-        // requested then loads it from the remote origin
-        if (loadRom) {
-            ({ name: romName, data: romData } = await this.fetchRom(romPath));
-        } else if (romName === null || romData === null) {
-            [romName, romData] = [this.romName, this.romData];
-        }
-
-        // selects the proper engine for execution
-        // and builds a new instance of it
-        switch (engine) {
-            case "neo":
-                this.gameBoy = new GameBoy();
-                break;
-            default:
-                if (!this.gameBoy) {
-                    throw new Error("No engine requested");
-                }
-                break;
-        }
-
-        // runs the initial palette update operation
-        this.updatePalette();
-
-        // resets the Game Boy engine to restore it into
-        // a valid state ready to be used
-        this.gameBoy.reset();
-        this.gameBoy.load_boot_default();
-        const cartridge = this.gameBoy.load_rom_ws(romData!);
-
-        // updates the ROM name in case there's extra information
-        // coming from the cartridge
-        romName = cartridge.title() ? cartridge.title() : romName;
-
-        // updates the name of the currently selected engine
-        // to the one that has been provided (logic change)
-        if (engine) this._engine = engine;
-
-        // updates the complete set of global information that
-        // is going to be displayed
-        this.setRom(romName!, romData!, cartridge);
-
-        // in case the restore (state) flag is set
-        // then resumes the machine execution
-        if (restore) this.resume();
-
-        // triggers the booted event indicating that the
-        // emulator has finished the loading process
-        this.trigger("booted");
-    }
-
-    setRom(name: string, data: Uint8Array, cartridge: Cartridge) {
-        this.romName = name;
-        this.romData = data;
-        this.romSize = data.length;
-        this.cartridge = cartridge;
-    }
-
-    get name(): string {
-        return "Boytacean";
-    }
-
-    get device(): string {
-        return "Game Boy";
-    }
-
-    get deviceUrl(): string {
-        return "https://en.wikipedia.org/wiki/Game_Boy";
-    }
-
-    get engines() {
-        return ["neo"];
-    }
-
-    get engine() {
-        return this._engine;
-    }
-
-    get version(): string {
-        return info.version;
-    }
-
-    get versionUrl(): string {
-        return "https://gitlab.stage.hive.pt/joamag/boytacean/-/blob/master/CHANGELOG.md";
-    }
-
-    get romExts(): string[] {
-        return ["gb"];
-    }
-
-    get pixelFormat(): PixelFormat {
-        return PixelFormat.RGB;
-    }
-
-    /**
-     * Returns the array buffer that contains the complete set of
-     * pixel data that is going to be drawn.
-     *
-     * @returns The current pixel data for the emulator display.
-     */
-    get imageBuffer(): Uint8Array {
-        return this.gameBoy?.frame_buffer_eager() ?? new Uint8Array();
-    }
-
-    get romInfo(): RomInfo {
-        return {
-            name: this.romName ?? undefined,
-            data: this.romData ?? undefined,
-            size: this.romSize,
-            extra: {
-                romType: this.cartridge?.rom_type_s(),
-                romSize: this.cartridge?.rom_size_s(),
-                ramSize: this.cartridge?.ram_size_s()
-            }
-        };
-    }
-
-    get frequency(): number {
-        return this.logicFrequency;
-    }
-
-    set frequency(value: number) {
-        value = Math.max(value, 0);
-        this.logicFrequency = value;
-        this.trigger("frequency", value);
-    }
-
-    get frequencyDelta(): number | null {
-        return 400000;
-    }
-
-    get framerate(): number {
-        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();
-    }
-
-    toggleRunning() {
-        if (this.paused) {
-            this.resume();
-        } else {
-            this.pause();
-        }
-    }
-
-    pause() {
-        this.paused = true;
-    }
-
-    resume() {
-        this.paused = false;
-        this.nextTickTime = new Date().getTime();
-    }
-
-    reset() {
-        this.boot({ engine: null });
-    }
-
-    keyPress(key: string) {
-        const keyCode = KEYS_NAME[key];
-        if (keyCode === undefined) return;
-        this.gameBoy?.key_press(keyCode);
-    }
-
-    keyLift(key: string) {
-        const keyCode = KEYS_NAME[key];
-        if (keyCode === undefined) return;
-        this.gameBoy?.key_lift(keyCode);
-    }
-
-    updatePalette() {
-        const palette = PALETTES[this.paletteIndex];
-        this.gameBoy?.set_palette_colors_ws(palette.colors);
-        this.paletteIndex += 1;
-        this.paletteIndex %= PALETTES.length;
-    }
-
-    benchmark(count = 50000000) {
-        let cycles = 0;
-        this.pause();
-        try {
-            const initial = Date.now();
-            for (let i = 0; i < count; i++) {
-                cycles += this.gameBoy?.clock() ?? 0;
-            }
-            const delta = (Date.now() - initial) / 1000;
-            const frequency_mhz = cycles / delta / 1000 / 1000;
-            return {
-                delta: delta,
-                count: count,
-                cycles: cycles,
-                frequency_mhz: frequency_mhz
-            };
-        } finally {
-            this.resume();
-        }
-    }
-
-    private async fetchRom(
-        romPath: string
-    ): Promise<{ name: string; data: Uint8Array }> {
-        // extracts the name of the ROM from the provided
-        // path by splitting its structure
-        const romPathS = romPath.split(/\//g);
-        let romName = romPathS[romPathS.length - 1].split("?")[0];
-        const romNameS = romName.split(/\./g);
-        romName = `${romNameS[0]}.${romNameS[romNameS.length - 1]}`;
-
-        // loads the ROM data and converts it into the
-        // target byte array buffer (to be used by WASM)
-        const response = await fetch(romPath);
-        const blob = await response.blob();
-        const arrayBuffer = await blob.arrayBuffer();
-        const romData = new Uint8Array(arrayBuffer);
-
-        // returns both the name of the ROM and the data
-        // contents as a byte array
-        return {
-            name: romName,
-            data: romData
-        };
-    }
-}
-
-declare global {
-    interface Window {
-        panic: (message: string) => void;
-    }
-}
-
-window.panic = (message: string) => {
-    console.error(message);
-};
-
-const wasm = async () => {
-    await _wasm();
-    GameBoy.set_panic_hook_ws();
-};
-
 (async () => {
     // parses the current location URL as retrieves
     // some of the "relevant" GET parameters for logic