How can you translate a functioning game from Flash to HTML5 without rewriting the client from scratch and without disrupting the players' familiar experience? In a column for App2Top, SuperCity development director Kirill Ivanov discusses the gradual modernization of the project, the reasons for retaining old tools, and process automation that helped reduce certain tasks from several days to a few hours.
Theoretically, modernizing an application with outdated code seems simple: identify the problematic technology, replace it with a modern stack, remove old dependencies, and continue development. In practice, it is more complicated. We encountered this while modernizing SuperCity—a game that has been running for 15 years.
SuperCity was launched in 2011. Today, its client is built on TypeScript and PixiJS, while the backend still uses PHP. Yet, certain parts of the system retain solutions from the Flash era. Artists continue to work in Adobe Animate, content must remain compatible with existing production processes, and part of the browser infrastructure must support interaction interfaces originally designed for Flash Player.
Therefore, for us, modernization never meant replacing every old component. Our goal was different: to gradually move away from technology the industry no longer supports, while preserving the game familiar to our audience.
The first migration solved players' issues but not development ones
The first major step away from Flash came when browsers began to stop supporting it. SuperCity had to continue operating without Flash Player, but we opted not to rewrite the entire client immediately.
Instead, we employed a conversion layer that transformed existing ActionScript to JavaScript for the working version of the game. Simultaneously, we adapted PixiJS to emulate the behavior the game expected from Flash Player. For users, the problem was solved: SuperCity continued to work in the browser without the old plugin.
However, Flash did not disappear from the development process. We still wrote in ActionScript and converted the code during build. Making changes meant considering an additional layer that complicated debugging and support.
Over time, another issue became increasingly evident: Flash was becoming an isolated technology. Fewer engineers were willing to work with it, new tools were sparse, the ecosystem ceased to evolve, and the external expertise available to us diminished.
At some point, we realized that realistic migration alternatives were running out. Without technological advancement around SuperCity, supporting the game itself would become increasingly challenging. However, that still did not make a complete rewrite the right decision.
A successful live game is more than just code
For a mature game, rewriting the client poses a risk that many ordinary software products do not have. Players interact with the interface not for a few minutes just to complete a task. They may return to the same product for years. They know where everything is. They understand how systems work. They have practically learned visual patterns and interaction rules subconsciously. These accumulated insights become part of the product itself. A completely new client may replicate the same mechanics and data but still feel like a different game.
I consider this distinction particularly important for games.
In a utilitarian application, an interface change may be inconvenient, but a user is usually able to learn a new path to the same result. In a game, the interface, feedback, rhythm, and accumulated feel of a familiar product are all part of the experience. Changing all this simultaneously risks forcing longtime players to relearn what they never asked to be changed. There was also a more traditional engineering problem. Over years, SuperCity accumulated numerous integrations and platform dependencies. Recreating all of these from scratch, revalidating them, and performing a complete regression run of the entire product would require significant investment. Essentially, the team would be taking a working game, rebuilding significant parts of it, and then paying to prove the new version works as the old one did. Meanwhile, there were no guarantees that the result would generate comparable business value.
Therefore, we opted for gradual changes: measuring the effect of each step and not altering working parts of the system without a clear reason.
Preserve the contract, change what lies beneath it
One of our key architectural decisions was maintaining the interaction interface on which the game relied. This allowed us not to rewrite all dependent systems at once.
Flash Player effectively acted as a contract: through known calls, the game could ask it to render an object, play or modify a sound, perform another operation. After abandoning Flash, we recreated this contract. The implementation underneath changed, but the calling code did not need to know details of the new environment.
If the game asks to display an animation, that part of the code shouldn’t need to care which technology ends up rendering it—Flash Player or Pixi.
The new layer receives a familiar command and ensures the expected behavior. This way, we replaced a critically important part of the stack without rebuilding all related systems simultaneously.
This approach has a cost: the compatibility layer retains technical debt and requires additional adaptation over time. But rewriting all dependent systems would have been significantly more expensive and would have created more risks.
The compatibility layer might differ from the architecture we would choose for a new game. However, when modernizing an existing product, you must consider everything that already works.
The transition to TypeScript required migration of an entire ecosystem
Our next major step was ditching ActionScript directly in development and transitioning to TypeScript. It concluded years after the initial browser migration.
One reason was prioritization. While engineers update the technological foundation, a live game continues to release content. Events must be launched, servers must handle changes in load, external platforms introduce mandatory requirements, and product opportunities appear and disappear. At different times, these tasks turned out to be more urgent than internal migration.
Another reason was the scale of dependencies. Over the years, we created utilities for asset conversion, build processes, internal applications, and tools for various specialists around SuperCity. The dependency on Flash was not limited to the client.
If we had translated only the client to TypeScript, leaving everything else unchanged, the bottleneck would have merely shifted to another part of the production process.
The migration involved not just what players launch in the browser. We also had to restructure many internal processes around the game.
This less visible part of the transition became a major engineering challenge itself.
Old doesn’t mean wrong
The art pipeline demonstrates why we don't see modernization as a reason to eliminate everything associated with Flash. Over the years, SuperCity has accumulated about 80 GB of graphic assets: buildings, decorations, characters, and other content. Artists still use Adobe Animate.
From a technological perspective, the tool may seem an obvious candidate for replacement. But from a production standpoint, the calculation is different: a new tool must provide at least the same quality and speed of operation.
If asset quality diminishes, the product suffers. If it remains constant but each release takes more time, production suffers. We have explored alternatives but have not yet found a replacement whose advantages would justify restructuring the entire process.
For us, the age of technology itself is not a sufficient reason for its replacement. A component becomes a priority for modernization when it begins to impose significant limitations.
Sometimes it’s better to modernize the processes around a tool instead of the tool itself
Maintaining Adobe Animate doesn’t mean that working with graphics has remained the same. Previously, an artist often couldn’t accurately see how an asset would behave in the game until developers prepared a working build. This lengthened the feedback cycle.
Art is often created long before a new feature is released. If a conversion issue, rendering artifact, or other visual discrepancy is only discovered during test build preparation, it is too late to correct them.
We created a browser-based preview tool—a simplified version of the game. An artist can upload an asset and immediately see how it appears in the game environment. Color changes, pixel distortion, and other conversion issues become apparent immediately.
This also changed the distribution of responsibility. Previously, developers collected assets, prepared builds, and helped resolve discovered issues. Now, the asset's author can independently check the result and prepare it for the next stage.
Quality control is maintained, but engineers no longer need to manually connect each stage of production. The legacy tool remains, but the processes around it have changed.
The broader goal is to remove developers from work that doesn’t require developers
Over time, this principle extended beyond graphics work. Over the past few years, we have restructured a significant part of our internal tools into a system I would describe as internal ERP for the game.
Historically, engineers were involved in nearly every stage of release preparation. Translations, art, game and promo configurations, and other materials eventually converged on developers before a working version could be assembled. The engineering team became a bottleneck.
New tools divided areas of responsibility. A specialist responsible for game configuration can publish it independently, and the system knows how to compile the final result. A game designer can change the balance or prepare a prototype of a new feature, prepare the change and test it without asking a developer to manually pass each asset through the pipeline.
Processes that used to require several days, coordination meetings, and engineer involvement can now take just a few hours. In some cases, the interaction is reduced to just a few actions in the internal tool.
The main result is not that everyone became technical specialists. Quite the opposite. A game designer no longer has to delve into Git branches to test a balance change. A narrative specialist no longer needs to understand the internal workings of the pipeline to update content. An artist doesn’t need a developer just to see how the asset will look in the game.
Canvas requires a unique approach to automated testing
Switching to HTML5 did not create an automated browser testing problem from scratch. Flash was already poorly suited to standard tools. But after the migration, we lost familiar Flash debugging capabilities, and HTML5 canvas brought its own limitations.
For browser automation, the canvas can appear as a black rectangle. It lacks convenient DOM elements corresponding to each building or UI component.
Therefore, visual checks require different approaches. We have internal debugging tools for examining rendering layers. Some checks can be performed using images and screenshot comparison. However, many scenarios are still harder to automate than in a regular web interface.
We have already automated basic end-to-end scenarios: you can deploy a build, launch the game, bring it to the desired state, and check, for example, building placement or resource collection.
A more complex problem is related to the state of the live game. Two users may see different windows, events, offers, and interfaces depending on platform, progress, cohort, and time. A test that worked yesterday might encounter a different set of events or offers from LiveOps tomorrow.
Automation must account not only for where a button is pressed but also for why the player’s state differs from the expected. For some scenarios, fully solving this task today would be more expensive than manual testing.
We evaluate automation by whether it removes a real constraint. Increased automation processes are not a goal in themselves for us.
Sometimes testing must occur in a production environment
In long-lived games, situations arise where external platform constraints don’t allow replicating conditions in full before release. During a recent mandatory migration of one platform, we faced exactly this: specific behavior could only be accurately tested in the production environment.
This approach was dictated by platform conditions. We gradually enabled changes for a small portion of users, observed the system, and expanded coverage as confidence in the results grew.
This example shows that gradual modernization helps manage risks. In large migrations, unknowns are inevitable. The ability to incrementally implement, measure, adjust, or rollback changes allows not placing the entire audience at risk at once. For a live game, the update deployment method also becomes part of modernization.
The primary advantage is not TypeScript itself
Moving away from Flash changed the development language, but one of the main improvements I see is the expanded pool of specialists able to work on SuperCity.
Finding an engineer who knows Flash, wants to continue writing on it, is interested in the gaming industry, and is ready to work on a mature product was becoming increasingly difficult. TypeScript largely eliminated this limitation.
An equally important result was reducing engineering involvement in daily production. Developers no longer need to manually oversee each stage of content preparation. Artists can independently check more assets, and game designers can create prototypes and adjust game systems.
Internal tools compile materials that previously required a whole chain of people and technical operations. Some processes have been reduced from several days to several hours. For us, this is a more substantial modernization metric than the complete absence of obsolete components in the codebase.
Our aim was to make the game easier to continue developing.
Legacy modernization never truly ends
SuperCity still contains old code, systems we would like to improve, and technologies that might be replaced over time. We also reassess even relatively recent parts of the game.
Our approach focuses on metrics, not the technology’s age. If something loads slowly, we investigate the issue. After addressing the most significant bottleneck, the next one becomes apparent—and the work continues.
The results impact both sides of the product. Players experience faster loading times, fewer crashes, and quicker responses. Meanwhile, the infrastructure may require fewer resources to maintain the same quality of work.
We cannot simply declare the architecture modern and stop thinking about it. For a long-lived product, modernization is a constant effort to maintain the ability to evolve.
Start with boundaries, understanding what absolutely cannot be broken
If I were advising another team on a successful product aged 10–15 years, I would start by defining boundaries, not by selecting a new engine. What does the team want to achieve? What can it change given the people, technology, and resources? What cannot afford to be broken in the process?
For a mature live game, the last question pertains to both infrastructure and the audience. Technically elegant migration could fail if it disrupts the familiar player experience.
I also remain skeptical about changing technology for the sake of imitation. Another studio’s shift to Unity, Cocos, or another popular stack doesn’t say anything about SuperCity’s needs. Each mature product has its own constraints.
My advice: first, understand what you want, what you can do, and what you cannot afford to break. Everything else should be filtered through common sense.
We gradually eliminated constraints around the existing game. Flash vanished from the client’s day-to-day development. Compatibility layers protected operational systems while their implementations evolved. Internal tools allowed specialists to perform tasks independently that were previously reliant on engineers.
We implemented automation where it made a tangible impact and retained manual testing where it remained economically justified. Such an approach allows changing technology beneath a working game without forcing the audience or team to start from scratch.





