GBA Emulator

Pick up where the last visitor left off
AI Agent — last session
No signal
Episode
Steps
Reward
ε
Badges
Loading GBA Emulator...

How this works

One shared emulator and a live AI trainer, wired together through a single Firestore database.

Shared emulator

Everyone sees the same game. One visitor holds control via a 2-minute lease; the rest spectate. Your progress is snapshotted to the cloud, so the next person picks up exactly where you left off — same map, same party, same RAM.

Live AI trainer

A reinforcement-learning agent trains on Pokémon Emerald on a Kaggle GPU, reading the game's memory and learning by reward. Every 500 steps it streams its screen, stats, and current thought into the panel above — what you see is a real run.

Data flow

Browserclient

EmulatorJS runs the game; polls control + training state

Next.js API/api/gba/*

Server routes authenticate and read/write Firestore via the Admin SDK

Firestoresource of truth

Two docs: gba/session (control + savestate), gba/training (live metrics)

Kagglegpu trainer

The RL notebook writes training metrics straight into Firestore

The browser never touches the database directly — every read and write goes through the server. The trainer is the one exception, writing in with a service-account key.

Taking turns, without losing progress

  1. 1.ClaimYou take the lease and the latest savestate loads into your emulator.
  2. 2.PlayA heartbeat every 30s keeps your 2-minute lease alive; spectators’ inputs are disabled.
  3. 3.SaveYour progress is captured on an interval, and again whenever you leave or the tab is hidden.
  4. 4.Hand offStop heartbeating (or release) and the lease frees — the next visitor resumes your exact save.

Stack: Next.js (App Router) · Firestore · EmulatorJS (mGBA core) · a DQN agent trained with mGBA + TensorFlow on Kaggle.

How the AI learns Pokémon

Reinforcement learning: reward the good, penalize the bad, repeat millions of times.

1. What it sees — the state vector

The AI doesn't process pixels. It reads 4 numbers directly from the GBA's RAM — enough to capture survival, progress, and wealth. Emerald XOR-encrypts money and shuffles save blocks in RAM, so the agent dereferences the SaveBlock pointers at runtime to find them.

RAM addressWhat it readsNormalized toExample
0x020244EC + 0x56/0x58Lead Pokémon HP / Max HPhp / maxhp0.85
SaveBlock2 flags 0x867–0x86EBadges earned (0–8)badges / 80.25
SaveBlock1 + 0x0490 ^ encKeyMoney (XOR-decrypted)min(money / 100k, 1)0.12
0x020244E9Party size (1–6)count / 60.50
Network input[0.85, 0.25, 0.12, 0.50]

2. What it can do — 7 actions

AConfirm / Attack
BCancel / Run
STARTOpen menu
Move right
Move left
Move up
Move down

Each action is held for 8 frames then released for 8 frames before the next decision — 16 frames per step, ~60fps equivalent.

3. Rewards — what it's optimizing for

+50
Per badge earned
The primary progress signal — pushes the agent toward beating gyms. Sparse but massive.
±5×
HP% change per step
Positive when healing or winning battles, negative when taking hits. Keeps it alive.
−0.01
Per step (time tax)
A tiny penalty every step. Forces the agent to act — standing still slowly kills its score.

4. The neural network (DQN)

A Deep Q-Network maps the 4-number state to a Q-value per action — the estimated future reward of pressing that button right now. The action with the highest Q-value wins.

Input
hp%
badges%
money%
party%
Dense 64 · ReLU
· · ·
Dense 64 · ReLU
· · ·
Q-values out
A
B
ST

Two identical networks run in parallel. The live network trains every step; a frozen target network provides stable Q-value targets and syncs every 1,000 steps. Without this split, the agent chases a moving target and diverges.

5. Explore vs. exploit — ε-greedy

Early on the agent acts randomly to discover the game. Over time it trusts its own predictions more. ε (epsilon) controls the tradeoff — at each step, with probability ε the agent picks a random button; otherwise it picks the highest Q-value.

ε = 1.0 100% random (step 0)ε = 0.05 5% random (floor)

Decays by ×0.9995 per step. The panel above shows the live epsilon — watch it drop as training progresses.

6. Training mechanics

Replay Buffer
10,000experiences

Every (state, action, reward, next state) tuple is stored. Training samples 64 at random — breaking the correlation of sequential steps that would otherwise destabilize learning.

Batch Update
64samples / step

MSE loss between predicted Q-values and Bellman targets, minimized by Adam (lr=1e-3, γ=0.95). Runs every step once the buffer has 64 entries.

Target Sync
1,000steps

The target network's weights are hard-copied from the live network every 1,000 steps. This prevents feedback loops where the network trains against its own rapidly changing outputs.

PortfolioProjectsGamesGBAAICharity🌱Garden
Dust
Hire Me