Professor Basil's Lab
Demo
Live Demo
Live preview of Professor Basil's Lab
Problem
A full Pokemon platform with 5 AI difficulty tiers, online PvP via PeerJS, ELO ranked ladder with global leaderboards (Vercel KV), full replay system, 8 Kanto gym leaders, Elite Four, Battle Tower/Factory, tournaments, breeding system, Safari Zone, and Nuzlocke mode. GBA emulator with ARM7TDMI + PPU emulation, NDS emulator in progress, and Rust/WASM acceleration across 10 crates. PWA-capable offline-first. 70 commits.
Constraints
- -Zero backend. Must run entirely client-side with no server costs
- -GBA emulator requires SharedArrayBuffer threading (restricted by COOP/COEP headers)
- -Gen 3 save format is XOR-encrypted with 24 sub-structure permutations per Pokemon
- -Battle damage formula must match the actual games to be credible
- -1025 Pokemon with full stat/move/ability data, which is a large dataset for client-side
Architecture
Hook-based architecture: useTeam for team management, useBattle (useReducer state machine) for the battle engine, useWildEncounter for the catch system, useGBAEmulator for WebAssembly integration. Online PvP via PeerJS with ELO ranked ladder backed by Vercel KV. Data layer: React Context for the Pokedex, TanStack Query v5 for PokeAPI with aggressive caching. Persistence: IndexedDB for emulator SRAM blobs, localStorage for teams. Visualization: Recharts 3 for stat radar charts, type coverage matrices, and damage distributions. The battle engine is a pure function, deterministic, replay-capable, and reused for AI lookahead evaluation across 5 difficulty tiers. Full replay system records and replays battles frame by frame. GBA emulator implements ARM7TDMI CPU and PPU emulation compiled to WASM.
Tech Choices & Why
useReducer
Battle engine needs pure state transitions for determinism and replay, not a global store
IndexedDB
Binary SRAM blobs exceed localStorage's 5MB limit
TanStack Query
Caching and deduplication for 1025 Pokemon worth of API calls
WebAssembly
Only way to run mGBA at playable speed in a browser
Challenges & Solutions
Problem
Gen 3 save files use XOR encryption (PID ^ OTID key), 24 sub-structure permutations (PID % 24), and bit-packed IV values (6 stats in one 32-bit integer).
Solution
Custom binary parser using ArrayBuffer/DataView. Decrypts each 80-byte block, determines sub-structure order from PID, extracts IVs by shifting and masking 5 bits per stat. The >>> 0 unsigned coercion was critical because JavaScript's signed 32-bit bitwise ops silently corrupt XOR results without it.
Problem
WebAssembly requires SharedArrayBuffer for threading, which needs COOP/COEP headers. These headers break CDN fonts, analytics, and third-party resources across the entire app.
Solution
Scoped COOP/COEP headers to just the /emulator route. Served WASM binaries from public/ to bypass Turbopack bundling. Save states persisted to IndexedDB survive page reloads.
Problem
The damage formula accounts for STAB, type effectiveness (18x18 matrix), crits, weather, terrain, items, abilities, and three generational mechanics (Mega, Tera, Dynamax). Nested Math.floor placement matters because wrong rounding cascades into incorrect damage ranges.
Solution
Gen V+ formula as a pure function with modifiers composed as a multiplier chain. Each mechanic is a state transformation in the reducer. Verified against online damage calculators for dozens of edge cases.
Trade-offs
- ~Initially shipped without tests for speed. Now has 63 Vitest tests covering battle engine, save parser, and catch formula. Deterministic architecture made retrofitting tests straightforward.
- ~Client-side only means no multiplayer without a complete rewrite.
- ~TanStack Query cache means first load is slow; subsequent loads are instant.
- ~WASM binary is ~5MB, which is acceptable for a SPA but adds to initial page weight.
Impact
Live at professor-basils-lab.vercel.app. 70 commits, 10 Rust crates. Demonstrates binary parsing, WebAssembly integration, state machine architecture, deterministic systems design, online PvP, ELO ranking, and full replay systems. PWA-capable offline-first.
What I'd Improve
- +Complete NDS emulator integration (DeSmuME WASM port in progress)
- +Extract the battle engine into a standalone publishable package
- +Expand tournament system with bracket visualization
- +Add spectator mode for live PvP battles
This project was reviewed by Luka (Principal Research Consultant) and Nala (UI/UX Compliance Auditor). They had no comments, which we interpret as approval.