Replacing a Live MMORPG Client Without Replacing the World

How Fantasy Online 2's solo developer rebuilt a live browser and Steam client around an unchanged server and player world.

By Jeromy StrohGuest|Last updated: August 10, 2026|9 minutes read
community spotlightindie
Replacing a Live MMORPG Client Without Replacing the World
Fantasy Online 2 is a massively multiplayer online role-playing game you can play in a web browser or on Windows through Steam. It is a huge little pixel-art world of quests, crafting, rare loot, character builds, and adventures with other players Built around social progression and discovery. Players invest in one character across equipment, skills, factions, achievements, and cosmetics, then carry that adventure between supported platforms. That persistent world is also what made replacing the client hard. Replacing a game client sounds straightforward when the game is still a prototype. You can change the renderer, rebuild the interface, rewrite the input layer, and reset whatever data no longer fits. Fantasy Online 2 was already live when I began. Players had accounts, characters, inventories, quests, skills, guilds, market listings, achievements, and years of expectations about how the game behaved. The server and database had to stay online. The world could not be reset just because I wanted a better foundation for the client. That changed the job from an engine rewrite into a live-system migration. The first important decision was defining what would not change. The server remained authoritative. Existing accounts, character data, game content, and network messages were treated as contracts. PxEngine, the new TypeScript and WebGL2 client, had to understand the same world rather than invent a replacement for it. That boundary reduced the risk considerably. I was not rewriting the game server, moving the database, and replacing the client at the same time. I was rebuilding the presentation, input, rendering, audio, and interface layers around a backend that players already depended on.

Parity came before improvement

The most tempting mistake during a migration is redesigning every system while porting it. A new interface makes old choices look dated, and a new renderer makes it easy to imagine larger structural changes. Doing both at once makes failures much harder to diagnose. I made feature parity the first target. Login, character selection, zone loading, movement, combat, inventory, equipment, skills, quests, dialogue, shops, crafting, parties, guilds, banking, trading, settings, reconnect behavior, and error states all needed to survive the move. A system was not complete just because its main screen rendered. Inventory also had to handle pending operations, full bags, drag cancellation, server rejection, and updates arriving while another window was open. Skills had to cover casting from the book, hotbar assignment, cooldowns, targeting, and failure conditions. Mobile input had to block touches where desktop mouse input was already blocked. Those edge cases are where a port that looks finished begins to feel unreliable. The production game became the specification. Old behavior, server responses, player reports, and real content were more useful than a clean theoretical design document.

Building for the actual workload

PxEngine is a custom TypeScript and WebGL2 client built around Fantasy Online 2's real requirements. It renders tile maps, animated sprites, effects, particles, interface windows, and GPU text while handling mouse, keyboard, controller, and touch input. The engine uses retained buffers, pooling, and preallocated working storage wherever the game would otherwise create garbage every frame. Browser garbage collection is not automatically a problem, but repeated per-frame allocation can turn into visible pauses during movement or combat. Avoiding those allocations also makes performance more predictable across a wide range of devices. That does not mean every subsystem was generalized into a giant reusable framework. The useful approach was to build the smallest solid primitive that solved a production need, then expand it when another real feature required more. The tile renderer, sprite batching, text system, GUI batching, pathfinding, particles, and input stack all grew from scenes the game actually had to run. Keeping the server authoritative helped here too. The client could focus on responsive presentation and clear feedback while the server remained the source of truth for movement, combat, inventory, quests, and progression. That gave the migration a stable center even while the visual and interaction layers were changing quickly.

Ship the migration in slices

A live migration needs checkpoints that players can actually exercise. Waiting until every old feature has been recreated produces one enormous, difficult-to-debug launch. I brought the new client to the official website and CrazyGames first. That exposed the browser build to real accounts, real network conditions, and the full production data set. Android followed after the touch controls, storage behavior, layout constraints, and platform wrapper were ready for broader use. Steam has its own migration and testing path; it is not treated as a simple copy of the browser release. The core client stays shared, while platform-specific code remains around the edges. Authentication, local storage, storefront behavior, and wrappers differ, but movement, rendering, combat, interface systems, and game content do not need separate implementations for every platform. This staged rollout also made bug reports more useful. A setting that appeared correct in the browser could still fail when a platform supplied a different storage service. A window that blocked mouse input could still allow a mobile touch to pass through. A reconnect path could work during local testing and fail after a real network interruption. Shipping smaller slices made those boundaries visible.

The mistakes were usually at the seams

The hardest bugs were rarely in a single isolated renderer function. They appeared where systems met. A feature might display correctly but lose its state after a new session. A touch event might reach the world through an open interface window. A remote actor could receive the right server update while showing the wrong transient effect. A UI port finished too quickly could preserve the happy path but miss drag cancellation, selection state, or server rejection. The lesson was to test each feature as a state machine rather than as a screenshot. What happens before the action, while it is pending, after success, after rejection, after reconnecting, and after changing platforms? That checklist catches far more than asking whether a button works. It also helped to keep the distribution wrappers thin. The more game logic that moves into platform-specific code, the harder it becomes to know whether a bug belongs to the game, the engine, the wrapper, authentication, or storage.

What made the migration possible

The migration worked because it had a stable boundary. The server protocol, persistent data, and game rules remained the center of the system. PxEngine could replace the client one subsystem at a time without asking players to abandon their characters or asking the backend to become a different game simultaneously. For another small team considering the same kind of move, my main recommendations are:
  1. Decide which contracts must remain stable before writing the replacement.
  2. Build a complete parity checklist that includes failure and persistence states.
  3. Separate platform wrappers from shared game logic.
  4. Test against real production content as early as possible.
  5. Ship in controlled slices with a rollback path.
  6. Treat player reports as evidence about system boundaries, not just isolated bugs.
  7. Delay unrelated redesigns until the replacement is trustworthy.
The successful moment is not when the new renderer draws its first map. It is when a returning player logs in, sees the same character and world, and continues playing without needing to understand how much changed underneath.

Play Fantasy Online 2

Fantasy Online 2 can be played here: fantasyonline2.com

About the author

Jeromy Stroh is the solo developer of Fantasy Online 2 and the founder of Pixel Games LLC. He works primarily in TypeScript, WebGL2, Java, and MySQL while building and operating the game's custom client, persistent world, server, and player tools.

Share this article

Frequently asked questions

Newsletter

Stay in the Loop.

Subscribe to our newsletter to receive the latest news, updates, and special offers directly in your inbox. Don't miss out!