No description
  • JavaScript 43.4%
  • TypeScript 16%
  • Python 15.3%
  • CSS 8.5%
  • C 6.2%
  • Other 10.6%
Find a file
2026-10-31 20:16:22 -04:00
.agents/skills/beads bd init: initialize beads issue tracking 2026-10-27 00:54:15 -04:00
.beads bd init: initialize beads issue tracking 2026-10-27 00:54:15 -04:00
.claude bd init: initialize beads issue tracking 2026-10-27 00:54:15 -04:00
.codex bd init: initialize beads issue tracking 2026-10-27 00:54:15 -04:00
client Multiplayer validation: load test, robustness, session end, connection loss. tools/mp_load_test.mjs drives a local server with simulated players (crowds of 10 to 100 on one map, 10 to 100 simultaneous link sessions, connect/disconnect churn); results are in docs/MULTIPLAYER_PLAN.md 7i. The server survives garbage of every kind (a Go test). A session now ends as lost when the page's own connection closes or the player disconnects, instead of waiting for ever (tested by stopping the server mid-battle). The end-of-session rules are stricter: the game must have been away from the field (its main callback another one for 30 frames) before 'back in the field' counts, which a first-visit scene or a slow transition no longer fakes; a battle that never began ends as lost after a minute. The master's pipeline margin is 0 (a deeper pipeline adds to the delay the game sees), and the slave and master keep the earlier, tested behaviour. Jitter is the open weakness, tracked as pokemon-c5e with an opt-in test. 2026-10-31 20:16:21 -04:00
config Multiplayer validation: load test, robustness, session end, connection loss. tools/mp_load_test.mjs drives a local server with simulated players (crowds of 10 to 100 on one map, 10 to 100 simultaneous link sessions, connect/disconnect churn); results are in docs/MULTIPLAYER_PLAN.md 7i. The server survives garbage of every kind (a Go test). A session now ends as lost when the page's own connection closes or the player disconnects, instead of waiting for ever (tested by stopping the server mid-battle). The end-of-session rules are stricter: the game must have been away from the field (its main callback another one for 30 frames) before 'back in the field' counts, which a first-visit scene or a slow transition no longer fakes; a battle that never began ends as lost after a minute. The master's pipeline margin is 0 (a deeper pipeline adds to the delay the game sees), and the slave and master keep the earlier, tested behaviour. Jitter is the open weakness, tracked as pokemon-c5e with an opt-in test. 2026-10-31 20:16:21 -04:00
dist Game build output with the validation fixes. Generated from the committed sources and the supplied ROMs. 2026-10-31 20:16:22 -04:00
docs Multiplayer validation: load test, robustness, session end, connection loss. tools/mp_load_test.mjs drives a local server with simulated players (crowds of 10 to 100 on one map, 10 to 100 simultaneous link sessions, connect/disconnect churn); results are in docs/MULTIPLAYER_PLAN.md 7i. The server survives garbage of every kind (a Go test). A session now ends as lost when the page's own connection closes or the player disconnects, instead of waiting for ever (tested by stopping the server mid-battle). The end-of-session rules are stricter: the game must have been away from the field (its main callback another one for 30 frames) before 'back in the field' counts, which a first-visit scene or a slow transition no longer fakes; a battle that never began ends as lost after a minute. The master's pipeline margin is 0 (a deeper pipeline adds to the delay the game sees), and the slave and master keep the earlier, tested behaviour. Jitter is the open weakness, tracked as pokemon-c5e with an opt-in test. 2026-10-31 20:16:21 -04:00
fire-red Build native FireRed traversal preview and ROM-to-dist pipeline 2026-10-26 23:31:39 -04:00
patches/pret Multiplayer: no sessions in cable clubs, saves recovered from link rooms, a crash in the cable club fixed; your own chat in its own colour; Fly indoors. A session started while standing in a Pokemon Center's cable club room left both players saved inside the rooms, one of them in the colosseum with no way out, and the attendants' battle options stopped the game: the save prompt opened by a script clears a Start menu window that does not exist (window 0xFF, out of bounds in the browser, patch 0046, a fix for everyone). Now sessions do not start in a cable club room, the Union Room or a link room (the buttons say so, invitations are declined), and Continue sends a save made inside a link room to the last Pokemon Center (patch 0047), which recovers saves made that way. Your own chat lines are shown in a colour of their own. Enhanced mode: Fly can be used in buildings and underground, never in a link room (patch 0048). Tests for each, and the plan, bug list and enhancement notes record them. 2026-10-31 09:45:34 -04:00
server Multiplayer validation: load test, robustness, session end, connection loss. tools/mp_load_test.mjs drives a local server with simulated players (crowds of 10 to 100 on one map, 10 to 100 simultaneous link sessions, connect/disconnect churn); results are in docs/MULTIPLAYER_PLAN.md 7i. The server survives garbage of every kind (a Go test). A session now ends as lost when the page's own connection closes or the player disconnects, instead of waiting for ever (tested by stopping the server mid-battle). The end-of-session rules are stricter: the game must have been away from the field (its main callback another one for 30 frames) before 'back in the field' counts, which a first-visit scene or a slow transition no longer fakes; a battle that never began ends as lost after a minute. The master's pipeline margin is 0 (a deeper pipeline adds to the delay the game sees), and the slave and master keep the earlier, tested behaviour. Jitter is the open weakness, tracked as pokemon-c5e with an opt-in test. 2026-10-31 20:16:21 -04:00
sim Multiplayer validation: load test, robustness, session end, connection loss. tools/mp_load_test.mjs drives a local server with simulated players (crowds of 10 to 100 on one map, 10 to 100 simultaneous link sessions, connect/disconnect churn); results are in docs/MULTIPLAYER_PLAN.md 7i. The server survives garbage of every kind (a Go test). A session now ends as lost when the page's own connection closes or the player disconnects, instead of waiting for ever (tested by stopping the server mid-battle). The end-of-session rules are stricter: the game must have been away from the field (its main callback another one for 30 frames) before 'back in the field' counts, which a first-visit scene or a slow transition no longer fakes; a battle that never began ends as lost after a minute. The master's pipeline margin is 0 (a deeper pipeline adds to the delay the game sees), and the slave and master keep the earlier, tested behaviour. Jitter is the open weakness, tracked as pokemon-c5e with an opt-in test. 2026-10-31 20:16:21 -04:00
tests Multiplayer validation: load test, robustness, session end, connection loss. tools/mp_load_test.mjs drives a local server with simulated players (crowds of 10 to 100 on one map, 10 to 100 simultaneous link sessions, connect/disconnect churn); results are in docs/MULTIPLAYER_PLAN.md 7i. The server survives garbage of every kind (a Go test). A session now ends as lost when the page's own connection closes or the player disconnects, instead of waiting for ever (tested by stopping the server mid-battle). The end-of-session rules are stricter: the game must have been away from the field (its main callback another one for 30 frames) before 'back in the field' counts, which a first-visit scene or a slow transition no longer fakes; a battle that never began ends as lost after a minute. The master's pipeline margin is 0 (a deeper pipeline adds to the delay the game sees), and the slave and master keep the earlier, tested behaviour. Jitter is the open weakness, tracked as pokemon-c5e with an opt-in test. 2026-10-31 20:16:21 -04:00
tools Multiplayer validation: load test, robustness, session end, connection loss. tools/mp_load_test.mjs drives a local server with simulated players (crowds of 10 to 100 on one map, 10 to 100 simultaneous link sessions, connect/disconnect churn); results are in docs/MULTIPLAYER_PLAN.md 7i. The server survives garbage of every kind (a Go test). A session now ends as lost when the page's own connection closes or the player disconnects, instead of waiting for ever (tested by stopping the server mid-battle). The end-of-session rules are stricter: the game must have been away from the field (its main callback another one for 30 frames) before 'back in the field' counts, which a first-visit scene or a slow transition no longer fakes; a battle that never began ends as lost after a minute. The master's pipeline margin is 0 (a deeper pipeline adds to the delay the game sees), and the slave and master keep the earlier, tested behaviour. Jitter is the open weakness, tracked as pokemon-c5e with an opt-in test. 2026-10-31 20:16:21 -04:00
website Group sidebar links and place area references under towns and routes 2026-10-30 23:34:21 -04:00
.gitignore Multiplayer server skeleton in Go. Invite tokens (the file holds names and SHA-256 hashes, the token is shown once), who's online, joined and left, presence with the map and an opt-in party summary, global chat with history and per-map chat, rate limits, a 200-character limit, control characters stripped, one connection per player, a slow client dropped instead of slowing the rest. No game data and no saves. Tests cover the tokens, joining, duplicate logins, both chat channels, the rate limit and the party visibility, and pass under the race detector; the README has the build, the token command, the protocol and the nginx location for the site owner to add. 2026-10-30 23:16:43 -04:00
AGENTS.md Time the battle intro by the game's own state; the slow species are the intro abilities 2026-10-28 07:03:00 -04:00
CLAUDE.md Add rule: write files with native tools, never heredocs or cat 2026-10-27 01:08:34 -04:00
DEPLOYMENT.md Rewrite the project documents to describe the project as it is 2026-10-29 20:55:41 -04:00
package-lock.json Add original dialogue, persistent field events and ROM reference captures 2026-10-27 00:14:08 -04:00
package.json Get out of the house: fix the player's movement range, and play through Oak's lab to the rival battle 2026-10-27 13:31:12 -04:00
PLAN.md Rewrite the project documents to describe the project as it is 2026-10-29 20:55:41 -04:00
playwright.config.mjs Multiplayer client: the Multiplayer button and panel in the player's Options. A name (kept in the browser) with a secret the browser makes, an optional game key with a Make a key button, an advanced server address, Connect and Disconnect, a status line and the list of players in the room (name, party size and top level). The address is the one in ?mp=, else the one typed in the panel, else the build's multiplayer.json (config/multiplayer.json, empty by default), else the site's own /pokemon-mp/ws. It never connects by itself; presence (the map and a party summary) follows the game's snapshot; the page also posts a multiplayer message to an embedding page. Keys typed in a text field are not game input. The server loses its show-my-party switch: the room sees each player's party. Playwright tests run the real Go server: two players see each other in the public room, a key makes a private room, a name that belongs to someone else is refused, the typed address and a dead one. 2026-10-30 23:57:55 -04:00
playwright.live.config.mjs Double battle: two players, two Pokemon each. A new session mode (relay, client, the players list and the click menu get a Double button, the invitation reads 'wants to have a double battle'); the game's own link-up and battle-start task already make a double link battle from the kind of battle in gSpecialVar_0x8004, so the core only gained the game's own party check (two Pokemon that can fight) as sim_mp_can_double, asked on both sides: the inviter's button says why it cannot, and an invited player who cannot is declined with a note. Tested: the party check, two sims in the harness with the cable, packet and 5-frame-delay models (double and link flags, a turn resolves, no link error), through the relay with 70 ms each way fought to its end (both sessions done, link closed, back in the field), and two real pages (the refusal, the invitation and its wording, the battle's start). The plan records it. 2026-10-31 16:02:06 -04:00
README.md Add a volume slider to the player and record the decision to keep cut trees out of the save 2026-10-30 05:18:23 -04:00
RESEARCH.md Rewrite the project documents to describe the project as it is 2026-10-29 20:55:41 -04:00
STATUS.md Multiplayer: click a drawn player for Trade, Battle or Mute, and mute a player. A drawn player can be clicked for a small menu with the same choices as the list; a muted name (kept in this browser) never reaches the page: no chat line, bubble or sound, and invitations from it are declined without a prompt. Pressing A next to a player was left out because other players are not game objects. STATUS.md and the plan describe what multiplayer is and is not tested. Two real pages test the menu, the invitation from it and the mute. 2026-10-31 02:06:31 -04:00
THIRD_PARTY.md Rewrite the project documents to describe the project as it is 2026-10-29 20:55:41 -04:00
tsconfig.json Build native FireRed traversal preview and ROM-to-dist pipeline 2026-10-26 23:31:39 -04:00

FireRed in the browser

A private, in-development port of Pokémon FireRed (GBA) to the browser. The game's own code, from pret/pokefirered, is compiled to WebAssembly and runs against a software model of the GBA's hardware. There is no ARM emulator, no BIOS image and no emulator wrapper at runtime. A TypeScript client draws the screen, plays the sound, reads input and stores saves. All game content (graphics, maps, text, music, data) is read from your own cartridge image when you build; nothing from the game is in this repository's source.

There are two sites built from this repository:

  • The game (dist/): a player page, play.html. It also still contains the first traversal pilot (index.html), a separate small TypeScript engine kept for comparison. See STATUS.md.
  • The field guide and companion (website/): a static Astro site with a Pokédex, items, moves, areas and a story guide. The player is embedded in its home page, and a live companion panel (party, bag, Pokédex, Journey, a map of where you are) reads the running game.

Do not read this as a claim of original-game parity. The whole story has been played through automatically, but comparison against the original is still partial. STATUS.md lists what is checked and what is not.

Build once, then serve dist/

Requirements: Python 3.11+ with venv, Node.js 22+, npm, and for the engine build GNU patch, make, g++ and git. The build needs internet for dependencies and for fetching pret at its pinned commit; the finished game does not.

python3 -m venv .venv
.venv/bin/python -m pip install -r tools/requirements.txt
npm ci
.venv/bin/python tools/build_from_rom.py "/path/to/FireRed.gba"
python3 -m http.server 8080 --directory dist

Open http://localhost:8080/play.html. On Windows use the virtual environment's Scripts/python.exe.

The build accepts English FireRed USA/Europe v1.0, SHA-1 41cb23d8dccc8ebd7c649cd8fbb58eeace6e2fdc. Rev 1 and LeafGreen are not supported. The ROM is read, never changed, and is needed only while building: dist/ holds the compiled core (core.wasm) and a content pack cut from the ROM, but not the ROM or any BIOS. The build is staged and verified before it replaces dist/; a failed build leaves the previous one.

Serve over HTTP (not file://) with application/wasm for .wasm. Cross-origin isolation is not required. Because the content pack is game data, keep a built dist/ private.

Play

The defaults below can be changed with the player's Options ▸ Controls (keyboard and controller, two keys or buttons per action).

Action Default keys
Move Arrow keys or W A S D
A / B Z / X
Start / Select Enter / Backspace
L / R Q / E
Fast-forward Space (hold), or the Speed button
Sound on / off M (the Volume slider beside the Sound button sets the level)

On phones the player shows touch buttons. An Xbox or similar controller works; A confirms and B goes back.

Progress is saved the way the game saves (the real cartridge format) into browser storage. Options has Quick Load (skip the title and go straight to CONTINUE), Intro skip, Export save and Import save. An exported .sav is a real cartridge save, and a cartridge .sav can be imported. Only one player tab can be open at a time so two copies never overwrite each other's save.

For testing, play.html?save=NAME starts from pokemon-saves/NAME.sav beside the page without touching the stored save (see docs/MANUAL_CHECKS.md).

The website

python3 tools/export_tables.py "/path/to/FireRed.gba"
python3 tools/build_website_maps.py "/path/to/FireRed.gba"
cd website && npm ci && npm run import-export && npm run import-areas && npm run build
python3 tools/build_website.py          # package website/dist with checks

See website/README.md and DEPLOYMENT.md.

Verify

Build first, then:

npm run check                                  # TypeScript
npm test                                       # core, PPU, audio, save and new-game tests
node --test tests/*.test.mjs                   # everything, including the story tests (slow)
.venv/bin/python -m unittest discover -s tests -p 'test_*.py' -v
npx playwright install chromium
npm run test:browser                           # player, embed and single-player specs
.venv/bin/python tools/verify_dist.py dist
.venv/bin/python tools/coverage.py --check     # no unsupported stub in ordinary play
npm --prefix website test                      # website specs

The story tests play the game headless: a new game, Route 1, every gym, the Rocket scenes, Silph Co, the Safari Zone, the Sevii gifts and the Elite Four, using a level-100 helper party where noted. The oracle comparisons against mGBA need the development-only setup in docs/ORACLE.md.

Layout

Path Purpose
sim/port/ Our C: the hardware model (frame driver, DMA, BIOS functions, sound chip, flash save, companion snapshot) around pret's code. See sim/port/README.md.
patches/pret/ Small unified diffs applied to the fetched pret tree for wasm-versus-ARM differences.
client/ TypeScript player: PPU renderer, audio, input, controls, saves, companion messages. Also the old traversal pilot.
tools/ ROM to dist build, exporters, pret port, deployment and development checks. See docs/tools.md.
config/ Supported ROM identity, unit list for the engine build, stub list, ROM data layout.
tests/ Node and Playwright tests (core, story, save, audio, player).
website/ The field guide and companion. Has its own README, build and tests.
docs/ Architecture, decisions, plans, oracle, references.
dist/ The built game; tracked in this private repository.

Documents

STATUS.md what works and what is unverified. PLAN.md the contract and milestones. docs/ARCHITECTURE.md the sim and client boundary. docs/decisions.md decisions with evidence. docs/COVERAGE.md which code is pret's, which is ours and what is stubbed (generated). docs/ENHANCEMENTS_PLAN.md and docs/MULTIPLAYER_PLAN.md plans only, nothing built. docs/REFERENCES.md community tools. THIRD_PARTY.md provenance. RESEARCH.md the original feasibility research (historical). Work items are tracked with Beads (bd).

This is the private working repository. It tracks the supplied cartridge inputs under fire-red/ and the generated dist/ on purpose. A public version will be produced later as a separate reviewed export that contains no ROM, no pret source, no game data and no built game.