vibe-shooter: Gemini as the Game Master

A React and Canvas arcade shooter where Gemini generates structured anomalies, the frame loop stays deterministic, and the fallback path is just as important as the AI path.

8 min read • View on GitHub • More from StarKnightt

A top-down arcade ship crosses a sparse field while a sealed anomaly card descends from above like a machine-issued decree. The scene explains that the match stays deterministic in motion, but AI can still inject a rule-changing event into the loop.
The point is not that AI is everywhere. The point is that it only gets to touch a narrow but powerful part of the game.
Key Takeaways

The AI is not the narrator. It is the referee.

Most AI games use the model for flavor. vibe-shooter gives Gemini a job that actually changes the match. It does not write lore on the side. It generates anomalies that alter health, scrap, and momentum inside a loop that still obeys fixed combat rules.

That distinction is the entire trick. If the model disappears, the game does not collapse into a dead demo. It falls back to a static interference event, keeps the player moving, and preserves the arcade rhythm.

What an anomaly actually is

The anomaly system is small on paper and powerful in practice. Player state, especially level and current scrap, is sent into a Gemini request. The model returns structured output, the game checks that structure, and only then does the event become playable.

type AnomalyEvent = {\n  title: string\n  safeChoice: { label: string; delta: number }\n  riskyChoice: { label: string; delta: number }\n  fallback?: boolean\n}\n\nconst event = await generateAnomaly({\n  level: player.level,\n  currentScrap: player.scrap,\n})

A single pipeline makes the article's main claim visible: Gemini proposes a bounded event, validation decides whether it enters the loop, and fallback logic keeps the shooter playable if the model path fails.

That pipeline matters because it turns the model into a constrained mechanic. The AI can shape risk and reward, but it cannot invent arbitrary game state. Schema checks and fallback logic are the fence.

A close-up of a stamped JSON packet sliding through a narrow mechanical gate. On the left, many possible outputs splinter into chaos, while on the right only one clean anomaly card survives, showing how the model is fenced in by validation.
The model can be surprising, but the game only accepts outputs that survive the gate.

How the game stays smooth while AI is thinking

This is where the repo earns its stripes. React owns the shell, the HUD, and the anomaly modal. The moving parts live elsewhere: bullets, enemies, collisions, and frame updates run through Canvas with mutable refs and requestAnimationFrame, so the app does not ask React to reconcile every tiny motion.

That split is the enabling architecture. The game can wait on AI without stalling the combat loop, because the loop is already insulated from the slower UI layer. In other words, the model sits beside the engine, not inside it.

A close-up of a phone screen split into left and right touch zones. One thumb steers movement with a circular vector ring while the other drags aim toward a small ship icon, showing how the game keeps its control scheme practical on mobile.
The repo is not just a desktop toy. Its touch model makes the same loop usable on a phone.

The code is vibe-coded, but the boundaries are disciplined

The stack is practical, not performative. TypeScript keeps the game entities honest, Canvas handles the render loop, and Web Audio synthesizes effects without loading a single asset file. That makes the project feel lighter than its concept should allow.

The same discipline shows up in the input model. Mobile control zones, entity arrays, and audio triggers are all explicit rather than magical. You can feel the shortcuts of vibe coding, but you can also see the constraints that keep the repo legible.

Why this is different from a normal shooter, and from a normal AI toy

DimensionClassic arcade shooterChat-first AI toyvibe-shooter
Core loopMove, shoot, survivePrompt, wait, read outputMove, shoot, and absorb anomaly events
Role of AINoneOwns the experienceTouches only the bounded event layer
Moment-to-moment changeDriven by level design and player skillDriven by model output and conversationDriven by deterministic combat plus AI-generated risk or reward
Failure modeHard game overHallucination or driftFallback interference event keeps the game playable
Player trustHigh, because rules stay fixedLow, because the model may wanderHigher, because the model is fenced in by schema and validation
Performance modelPure real-time loopUsually asynchronous and looseCanvas and refs keep the loop fast while React handles the shell
Why it mattersProves craftsmanshipDemonstrates noveltyShows how AI can be a mechanic instead of a mascot

Origin and lineage: a template that became a thesis

The surrounding ecosystem matters because it shows how prompt-native repos spread. The upstream README frames the project as a vibe-coded game built against a codebase that is meant to be extended by prompting, not by hand-editing every feature. That is useful context, but it is not the main story here. The main story is that this fork-like lineage uses the same prompt-friendly posture to put Gemini inside the runtime, not just inside the workflow.

Vibe Shooter is a fully vibe-coded Next.js web game powered by the AI-optimized thirdweb API which handles authentication, wallet management, gasless transactions, and more.

thirdweb-example/vibe-shooter README, Project Documentation · thirdweb-example/vibe-shooter

Build features by prompting against your codebase + `llms.txt`. The thirdweb API covers user authentication, wallet management, gasless transactions, and more out of the box.

thirdweb-example/vibe-shooter README, Project Documentation · thirdweb-example/vibe-shooter

That lineage makes the repo more interesting, not less. It shows how a prompt-first template can become a runtime thesis when the AI stops being a helper and starts being a bounded game master.