diff --git a/CHANGELOG.md b/CHANGELOG.md
index fc4e4e9413917f44ce4203828cbca155191fb7c3..913906d133d00c6ac97f57340df9ddb54ba9f37a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 * Support for true fullscreen at a browser level
 * Support for more flexible palette colors
 * Support for setting palette colors using WASM
+* Local storage usage for saving battery backed RAM
 
 ### Changed
 
diff --git a/examples/web/gb.ts b/examples/web/gb.ts
index a34c1e6bd57bc7c9d6a8e8b15591d9bab5bfafba..edbf4dcd0ed0ca0834ea441e426963271d3b9a04 100644
--- a/examples/web/gb.ts
+++ b/examples/web/gb.ts
@@ -15,13 +15,22 @@ import {
     PpuMode
 } from "./lib/boytacean.js";
 import info from "./package.json";
+import { base64ToBuffer, bufferToBase64 } from "./util";
 
 declare const require: any;
 
 const LOGIC_HZ = 4194304;
+
 const VISUAL_HZ = 59.7275;
 const IDLE_HZ = 10;
 
+/**
+ * The rate at which the local storage RAM state flush
+ * operation is going to be performed, this value is the
+ * number of seconds in between flush operations (eg: 5 seconds).
+ */
+const STORE_RATE = 5;
+
 const SAMPLE_RATE = 2;
 
 const KEYS_NAME: Record<string, number> = {
@@ -65,6 +74,7 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
     private frameStart: number = new Date().getTime();
     private frameCount: number = 0;
     private paletteIndex: number = 0;
+    private storeCycles: number = LOGIC_HZ * STORE_RATE;
 
     private romName: string | null = null;
     private romData: Uint8Array | null = null;
@@ -190,7 +200,8 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
 
             // runs the Game Boy clock, this operations should
             // include the advance of both the CPU and the PPU
-            counterCycles += this.gameBoy?.clock() ?? 0;
+            const tickCycles = this.gameBoy?.clock() ?? 0;
+            counterCycles += tickCycles;
 
             // in case the current PPU mode is VBlank and the
             // frame is different from the previously rendered
@@ -206,13 +217,16 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
                 // triggers the frame event indicating that
                 // a new frame is now available for drawing
                 this.trigger("frame");
+            }
 
-                // @todo this has to be structureed in a better way
-                if (this.cartridge && this.cartridge.has_battery()) {
-                    const ramData = this.cartridge.ram_data_eager();
-                    const decoder = new TextDecoder("utf8");
-                    const ramDataB64 = btoa(decoder.decode(ramData));
-                    localStorage.setItem(this.cartridge.title(), ramDataB64)
+            // in case the current cartridge is battery backed
+            // then we need to check if a RAM flush to local
+            // storage operation is required
+            if (this.cartridge && this.cartridge.has_battery()) {
+                this.storeCycles -= tickCycles;
+                if (this.storeCycles <= 0) {
+                    this.storeRam();
+                    this.storeCycles = this.logicFrequency * STORE_RATE;
                 }
             }
         }
@@ -299,22 +313,22 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
         this.gameBoy.load_boot_default();
         const cartridge = this.gameBoy.load_rom_ws(romData!);
 
-        // in case there's a battery involved tries to obtain
-        if (cartridge.has_battery()) {
-        }
+        // updates the name of the currently selected engine
+        // to the one that has been provided (logic change)
+        if (engine) this._engine = engine;
 
         // 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 there's a battery involved tries to load the
+        // current RAM from the local storage
+        if (cartridge.has_battery()) this.loadRam();
+
         // in case the restore (state) flag is set
         // then resumes the machine execution
         if (restore) this.resume();
@@ -504,6 +518,22 @@ export class GameboyEmulator extends EmulatorBase implements Emulator {
         }
     }
 
+    private loadRam() {
+        if (!this.gameBoy || !this.cartridge) return;
+        const ramDataB64 = localStorage.getItem(this.cartridge.title());
+        if (!ramDataB64) return;
+        const ramData = base64ToBuffer(ramDataB64);
+        this.gameBoy.set_ram_data(ramData);
+    }
+
+    private storeRam() {
+        if (!this.gameBoy || !this.cartridge) return;
+        const title = this.cartridge.title();
+        const ramData = this.gameBoy.ram_data_eager();
+        const ramDataB64 = bufferToBase64(ramData);
+        localStorage.setItem(title, ramDataB64);
+    }
+
     private setPalette(index?: number) {
         index ??= this.paletteIndex;
         const palette = PALETTES[index];
diff --git a/examples/web/util.ts b/examples/web/util.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d52196c025576237af8c49dd7a1f98a18f56ec4f
--- /dev/null
+++ b/examples/web/util.ts
@@ -0,0 +1,18 @@
+export const bufferToBase64 = (buffer: Uint8Array) => {
+    const array = Array(buffer.length)
+        .fill("")
+        .map((_, i) => String.fromCharCode(buffer[i]))
+        .join("");
+    const base64 = btoa(array);
+    return base64;
+};
+
+export const base64ToBuffer = (base64: string) => {
+    const data = window.atob(base64);
+    const length = data.length;
+    const buffer = new Uint8Array(length);
+    for (let i = 0; i < length; i++) {
+        buffer[i] = data.charCodeAt(i);
+    }
+    return buffer;
+};