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.Claim — You take the lease and the latest savestate loads into your emulator.
  2. 2.Play — A heartbeat every 30s keeps your 2-minute lease alive; spectators’ inputs are disabled.
  3. 3.Save — Your progress is captured on an interval, and again whenever you leave or the tab is hidden.
  4. 4.Hand off — Stop 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