Building Galaxy Guardian: Fixing Our AI Loading Problem
We wanted AI-generated ships and enemies. Load times hit 60 seconds. Here's how we fixed it using Gemini AI at build time instead of runtime.
The Problem: 60-Second Load Times
When we started building Galaxy Guardian, we had this idea: every player gets their own AI-generated ship, enemies, and backgrounds. No two games would look the same. Sounds cool, right?
The reality was different. Generating images on the fly meant users were staring at a loading screen for a full minute. In 2025, that's not acceptable for a browser game. People close the tab after 3 seconds.
We had to figure out a way to keep the AI-generated look without the wait. That's what this post is about.
1. Moving AI to Build Time
The fix was simple once we thought about it: why generate assets when the player is waiting? We could generate them beforehand.
We wrote a Node.js script that runs during our build process. It calls Gemini to generate all the ships, enemies, and backgrounds, then compresses them into optimized PNGs. When a player loads the game, they're downloading pre-made images, not waiting for AI inference.
The Numbers
Time-to-interactive dropped from 60 seconds to under 2 seconds. Same AI art style, but the generation happens on our servers during deployment, not on the user's device.
With the asset problem solved, we could focus on the actual game. The next question was: what tech stack should we use?
2. React 19 for UI, Canvas for Rendering
We went with a hybrid setup. React 19 handles the menus, score display, and state management. For the actual game rendering—ships moving, bullets flying—we use a raw 2D Canvas.
This split makes sense for shooters. React is great for UI components, but running game logic through the React render cycle would be too slow. The Canvas runs at 60 FPS independently, and React just updates the HUD when scores change.
React 19's concurrent rendering helped with one specific thing: the weapon upgrade menu. It's a complex overlay that needs to animate smoothly without freezing the game behind it.
// Canvas handles rendering, React handles UI state
const gameLoop = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
renderBackground(ctx);
entities.forEach(e => {
e.update(deltaTime);
e.draw(ctx, assets);
});
requestAnimationFrame(gameLoop);
};
With the tech stack figured out, we moved on to the part that actually makes a shooter fun: the weapons.
3. The Weapon System
A space shooter lives or dies by its weapons. We built a 7-level progression: you start with a single blaster, and by level 7, you're firing a screen-wide plasma beam.
Each weapon level has its own particle effects and sound. Getting this right took some iteration. Early versions felt too weak at low levels and too overpowered at high levels. We used the AI to suggest damage curves, then tweaked them based on playtest feedback.
The goal was to make the game feel fair for someone on a coffee break but still challenging for players who want to chase high scores.
What We Learned
The original plan—real-time AI everything—sounded impressive but didn't work. Moving asset generation to build time was a simple fix that made the game actually playable.
Sometimes the boring solution is the right one. We still get the AI-generated art style, just without making users wait for it.
If you want to try the game, it loads in about 2 seconds now.