Context
I have multiple small and large games written in PixiJS. Originally, these games were rendered on specific pages using an iframe approach. The page was able to handle multiple games at the same time until it reached its memory limits.
For example, after 6 games were loaded, the 7th game would have memory issues, and the iframe would show a "memory limit exceeded" message. The important point is that this behavior was consistent: on some devices, the issue appeared after 4 games, on others after 5 or 6 games. This behavior was acceptable.
However, due to some feature requirements and limitations of our iframe solution, we decided to move the games into Shadow DOM. We migrated all games to this approach.
Everything seemed fine on desktop devices. We tested on different devices, including older Android devices and newer iPhones, and everything worked as expected.
We inject around 50% of the games into the Shadow Root using Module Federation, and the other half are injected using a custom script-based build process.
The games injected using the custom script follow a specific structure: there is one root class that contains child components, and only the root class is injected into the Shadow Root. The rest of the game structure is created inside that root element, building the complete component tree dynamically.
After some testing time, we discovered a few small but very difficult-to-debug issues.
I will describe the problems below along with my questions.
Problem 1
Two separate games had:
const lang = "en";
defined in their code.
This caused one of the games to fail during loading. I do not remember the exact error message, but the idea was that the second game could not write to the lang constant because it was already defined.
The issue was fixed immediately after renaming one of the variables to something like:
const lang2 = "en";
I knew that Shadow DOM can isolate and affect CSS, but is it also possible for one Shadow Root component to break the JavaScript of another Shadow Root component, or the JavaScript of the main page?
If yes, I would like to understand this behavior in more detail. The information I found so far is not deep enough, and I would like to read a more detailed explanation of how JavaScript isolation works with Shadow DOM.
Problem 2
This issue is still not solved.
On iPhone devices, even a single game forces the entire page to refresh.
It does not matter whether the game is injected using Module Federation or using our custom script-based approach.
I have analyzed the whole flow and could not find anything suspicious. I used Safari developer tools and the debugger, but I do not see any memory spikes or anything indicating that the game is consuming too many resources.
For example, we created a very simple Pixi canvas with only a spinning circle animation, and even that component caused the entire page to refresh.
We tried multiple fixes suggested by AI tools, but they did not seem relevant. I believe the issue is deeper than a simple configuration problem, but I do not know where else to investigate.
Additional Information
All games use:
pixi.Assetsloader for loading assetsHowler.js for audio
GSAP for animations
Initially, after discovering Problem 1, I thought the issue might be related to static fields shared between games. I removed all possible static fields from the game implementations.
Currently, everything is part of an object instance. The only remaining static-like usages are related to:
GSAP animations
Howler audio instances
Pixi asset loading
Reusing assets loaded by Pixi
I believe library versions might be important. The most relevant one is probably PixiJS, and we are currently using PixiJS v8.
If other library versions are also important, I can provide them.
Additional Observation: Issue Resolved After Device Restart
One thing fixed the issue completely on an iOS device.
One of our coworkers had more than 140 pages open in Chrome on their device. We closed all of them, but the issue was still happening.
After that, we restarted the phone following a recommendation from some documentation, and the issue was completely fixed. The problem has never happened again on that device.
However, we cannot ask our clients to perform these steps every time this issue occurs.