
Guide: Prompting AI for Game Code — A Practical Workflow
Getting AI to generate game code is easy. Getting it to generate working game code takes technique. After running 15+ models through identical game builds — Phaser.js and Three.js, 2D and 3D — we’ve converged on a repeatable workflow. This guide walks through it.
Why Prompt Structure Matters
AI models don’t know what you know. They don’t know what version of Phaser you’re using, whether your game needs physics or not, or that you meant “red circles chase the player” not “red circles move randomly.” The difference between a working game and a blank canvas is often one sentence in the prompt.
The benchmarks we’ve run on DeepSeek V4 Flash across Phaser.js and Three.js games show that structured prompts produce working code 80% of the time. Vague prompts produce working code about 30% of the time.
Prerequisites
- An AI model with code generation capability (this guide uses DeepSeek V4 Flash — available via OpenRouter or the DeepSeek API)
- A basic HTML editor or IDE
- A browser to test your game
No prior game development experience required. We’ll start from scratch.
The Two-Prompt Pattern
Every game in our benchmark pipeline follows the same pattern: two prompts, not one.
Prompt 1: Scaffold
The first prompt creates the complete game from scratch. A good scaffold prompt has four parts:
1. Framework and version. Phaser 3.60.0 and Three.js 0.128.0 are the CDN versions we’ve tested. Be explicit:
Create a Phaser.js 3.60 game as a single HTML file...
2. Player mechanics. What the player controls and how:
The player is a blue circle that moves with WASD or arrow keys. Movement speed is 300px/s.
3. Opponent behavior. What the enemies do:
Red circles spawn at random positions on the edges of the 680x480 canvas every 1.5 seconds. They chase the player at 100px/s with simple AI — they move toward the player's current position each frame.
4. Game state rules. Win/lose/score:
Score increases by 1 every second. Collision with any red circle ends the game. Press R to restart and reset the score.
Full scaffold prompt example:
Create a Phaser.js 3.60 game as a single HTML file. The player is a blue circle that moves with WASD or arrow keys at 300px/s. Red circles spawn at random screen edges every 1.5 seconds and chase the player at 100px/s. Score increases by 1 per second. Collision = game over. Press R to restart. Use
generateTexture()inpreload()to create textures from graphics. Do NOT usephysics.add.circle().
That last line is critical — it avoids the single most common model error.
Prompt 2: Polish
The second prompt adds features on top of the previous output. Feed the complete existing HTML back in with your modifications:
Here is the current game HTML. Add: enemy speed increases by 5% every 10 seconds. The player leaves a fading trail behind them (a circle that fades from 40% to 0% opacity over 1 second). Add a score display in the top-left corner. Return the complete updated HTML file.
This two-prompt pattern produces better results than trying to get everything in one shot. Each prompt has a focused goal. The model isn’t splitting its attention between scaffolding and polish.
Prompt Ingredients That Matter
Never Skip: The Version Pin
Without a version pin, models default to Phaser 2.x or Three.js r90 syntax — APIs that don’t match the CDN you’re actually loading. The result: Uncaught TypeError: this.add is not a function in Phaser 3.60 because Phaser.Class was removed.
WRONG: "Create a Phaser.js game..."
RIGHT: "Create a Phaser.js 3.60 game as a single HTML file using CDN https://cdn.jsdelivr.net/npm/[email protected]/dist/phaser.min.js"
Never Skip: The Output Format
WRONG: "Build a game"
RIGHT: "Return the complete, standalone HTML file. Include all CSS and JavaScript inline."
Without this, models may output pseudo-code, partial snippets, or instructions like “add the following to your index.html” instead of the actual file.
Always Include: Constraints That Prevent Common Errors
Add these lines to every game scaffold prompt:
Use generateTexture() in preload() to create graphics textures— preventsphysics.add.circle()which crashes on collisionBind keyboard keys once in create(), not in update()— prevents key binding accumulation every frameUse scene: [SceneName] array format, not scene: SceneName— prevents silent Phaser init failureSet physics body.setVelocity()— prevents wrong API calls on the sprite itselfInclude the <div id="game"> parent element in the HTML— models often omit it
The Cost Sweet Spot
Game code generation doesn’t need expensive models. Our benchmark data shows:
| Setting | Avg Output | Avg Time | Cost | Result |
|---|---|---|---|---|
| DeepSeek V4 Flash, temp 0.7, 8,192 max_tokens | 15,487 chars | 68s | $0.0012 | Working game + bot mode |
| DeepSeek V4 Flash, temp 0.4, 4,096 max_tokens | 7,376 chars | 14s | $0.0005 | Working game, no bot mode |
| DeepSeek V4 Pro, temp 0.7, 8,192 max_tokens | 1,424 chars | 101s | $0.0012 | Spent tokens on reasoning, minimal code |
The sweet spot: Lower temperature (0.4) and lower max_tokens (4,096) for the scaffold prompt — cheaper and faster. Then use higher temperature (0.7) with higher max_tokens (8,192) for the polish pass where you need creative feature additions.
Total cost for a complete game: under $0.002, both prompts combined.
Common Failure Modes (and Quick Fixes)
1. CDN URL Typo
Models frequently generate wrong CDN URLs. Verify before deploying:
curl -sI "https://cdn.jsdelivr.net/npm/[email protected]/dist/phaser.min.js" | head -1
# Expect: HTTP/2 200
Common wrong URLs: three@r128/build3 (wrong path), [email protected]/build/three.min.js (missing patch version), cdnjs.cloudflare.com (wrong host — it redirects).
2. Keyboard Events in update()
Model-generated code often checks keyboard input inside update(), which runs every frame. If the model uses createCursorKeys() inside update(), it creates new cursor key objects every frame — performance degrades immediately.
The fix: Move keyboard setup to create(). Reference the keys object in update().
3. Physics Colliders in update()
Same pattern: physics.add.overlap() or physics.add.collider() inside update() creates infinite new colliders. The game slows to a crawl within seconds.
The fix: Set colliders once in create().
4. Scene Not in Array Format
// WRONG — fails silently in Phaser 3.60
scene: GameScene
// RIGHT
scene: [GameScene]
Bare scene references fail silently. Always wrap in an array.
5. Missing Parent Div
The parent: 'game' config expects <div id="game"> in the HTML body. Models frequently omit this. Verify before testing.
6. No Texture Generation
Models default to physics.add.circle() for creating circular sprites. This creates an Image, not a Sprite — it has no body property and crashes on collision.
The fix: Add Use generateTexture() in preload() to create textures to every prompt.
Wrapping Up
The difference between a working game and a broken game is rarely the model’s capability — it’s the prompt structure. Version pinning, output format specification, and known-failure-mode prevention lines turn a 30% success rate into 80%+.
Three things to remember:
- Two prompts, not one. Scaffold first, then polish. Each prompt has one job.
- Pin versions explicitly. The model’s knowledge cutoff may predate the API version you’re using.
- Prevent known failure modes in your prompt. Add constraint lines that block the common errors models repeat.
Resources
- Phaser 3.60.0 CDN — the version we use for all 2D games
- Three.js 0.128.0 CDN — the version we use for 3D games
- OpenRouter — multi-model API access (DeepSeek, Mistral, Groq, and others)
- DeepSeek Platform — direct API access to DeepSeek models
- AI Dodge on GitHub — open-source game built with this workflow
- Phaser 3.60 Migration Guide — what changed and why