oneshotlm
Compare all models on this prompt →

Top-down alien shooter · gemini-3-flash-preview

Using the pre-provided phaser.min.js (global Phaser), build a top-down alien shooter: move with WASD, aim and fire at waves of advancing aliens, with health, score, and increasing difficulty. Use ONLY procedurally generated graphics, no external image or audio assets.

⛨ sandboxed Open in new tab ↗
transcript · 16 messages
user

Using the pre-provided phaser.min.js (global Phaser), build a top-down alien shooter: move with WASD, aim and fire at waves of advancing aliens, with health, score, and increasing difficulty. Use ONLY procedurally generated graphics, no external image or audio assets.

assistant
thinking
**Developing Alien Shooter Game**

I'm focusing on structuring the `index.html` to incorporate Phaser 3 and establish the core game loop. My immediate goal is to get a basic scene rendering to confirm the setup for the top-down alien shooter mechanics.

bash {"command":"ls phaser.min.js"}
toolResult

phaser.min.js

assistant
thinking
**Developing Game Structure**

I'm starting by confirming the presence of `phaser.min.js` and will now proceed to construct the `index.html` file. My immediate focus is on defining the visual elements for the player, aliens, and bullets, outlining their basic shapes and colors.

write index.html (9021 bytes)
toolResult

Successfully wrote 9021 bytes to index.html

assistant
thinking
**Building Core Functionality**

I've successfully implemented the `index.html` file, incorporating procedural texture generation for player, enemy, and bullet elements. Player movement is now responsive via WASD and arrow keys, with mouse control for aiming and shooting. A foundational wave system has also been established, designed to progressively escalate enemy count and speed.

**Implementing Game Mechanics**

My focus has shifted to integrating crucial gameplay elements. I'm currently developing the collision detection system and the health and score HUD. Additionally, I'm setting up the game over screen with restart functionality and a starfield background. I've also refined texture generation, ensuring it's called efficiently and only once, either in `preload` or `create`.

**Refining Bullet Logic**

I'm addressing a subtle bug where inactive bullets might retain their previous velocity. I'll ensure their bodies are properly reset and reactivated when reused from the pool. Additionally, I've confirmed the `phaser.min.js` script is correctly referenced using the specified plain tag.

edit {"edits":[{"oldText":" function create() {\n createTextures(this);\n\n // Add stars\n for (let i = 0
toolResult

Successfully replaced 2 block(s) in index.html.

assistant
thinking
**Refining Enemy Generation**

I'm double-checking the enemy retrieval logic. It appears `enemies.get` *should* create a new enemy instance with the correct texture if one doesn't exist for the given key, which is good. I'm now considering edge cases for initial object creation.

**Ensuring Physics Functionality**

I've confirmed that when reusing enemies from the pool, their physics bodies are correctly re-enabled. I was initially concerned about this, but the logic in `spawnWave` explicitly sets `enemy.body.enabled = true`, addressing potential issues with inactive objects.

read {"path":"index.html"}
toolResult

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Alien Swarm Survivor</title> <script src="phaser.min.js"></script> <style> body { margin: 0; padding: 0; background-color: #111; display: flex; justify-content: center; align-items: center; height: 100vh; overflow: hidden; font-family: 'Courier New', Courier, monospace; } canvas { box-shadow: 0 0 20px rgba(0, 255, 0, 0.2); border: 2px solid #333; } </style> </head> <body> <div id="game-container"></div> <script> const config = { type: Phaser.AUTO, width: 800, height: 600, parent: 'game-container', physics: { default: 'arcade', arcade: { gravity: { y: 0 }, debug: false } }, scene: { preload: preload, create: create, update: update } }; const game = new Phaser.Game(config); let player; let cursors; let wasd; let bullets; let enemies; let lastFired = 0; let score = 0; let health = 100; let scoreText; let healthText; let gameOver = false; let wave = 1; let spawnTimer; function preload() { // Procedural Graphics Generation only in create } function createTextures(scene) { // Player texture let graphics = scene.make.graphics({ x: 0, y: 0, add: false }); graphics.lineStyle(2, 0x00ff00); graphics.fillStyle(0x008800); graphics.beginPath(); graphics.moveTo(20, 10); graphics.lineTo(0, 0); graphics.lineTo(0, 20); graphics.closePath(); graphics.fillPath(); graphics.strokePath(); graphics.generateTexture('player', 20, 20); graphics.clear(); // Enemy texture graphics.lineStyle(2, 0xff0000); graphics.fillStyle(0x880000); graphics.beginPath(); graphics.moveTo(10, 0); graphics.lineTo(20, 10); graphics.lineTo(10, 20); graphics.lineTo(0, 10); graphics.closePath(); graphics.fillPath(); graphics.strokePath(); // Add some "eyes" graphics.fillStyle(0xffffff); graphics.fillCircle(12, 7, 2); graphics.fillCircle(12, 13, 2); graphics.generateTexture('enemy', 20, 20); graphics.clear(); // Bullet texture graphics.fillStyle(0xffff00); graphics.fillCircle(4, 4, 4); graphics.generateTexture('bullet', 8, 8); graphics.clear(); // Background star graphics.fillStyle(0xffffff); graphics.fillCircle(1, 1, 1); graphics.generateTexture('star', 2, 2); graphics.destroy(); } function create() { // Already generated in create if called there, let's just keep it in create. // Remove the call from preload to avoid duplication if it was there. // Add stars for (let i = 0; i < 150; i++) { let star = this.add.image(Phaser.Math.Between(0, 800), Phaser.Math.Between(0, 600), 'star'); star.setAlpha(Phaser.Math.FloatBetween(0.2, 1.0)); star.setScrollFactor(0); } player = this.physics.add.sprite(400, 300, 'player'); player.setCollideWorldBounds(true); player.setBodySize(16, 16); bullets = this.physics.add.group({ defaultKey: 'bullet', maxSize: 30 }); enemies = this.physics.add.group(); // Controls cursors = this.input.keyboard.createCursorKeys(); wasd = this.input.keyboard.addKeys({ up: Phaser.Input.Keyboard.KeyCodes.W, down: Phaser.Input.Keyboard.KeyCodes.S, left: Phaser.Input.Keyboard.KeyCodes.A, right: Phaser.Input.Keyboard.KeyCodes.D }); // UI scoreText = this.add.text(16, 16, 'SCORE: 0', { fontSize: '24px', fill: '#0f0', fontStyle: 'bold' }); healthText = this.add.text(16, 45, 'HEALTH: 100', { fontSize: '24px', fill: '#0f0', fontStyle: 'bold' }); this.time.addEvent({ delay: 2000, callback: spawnWave, callbackScope: this, loop: true }); // Collisions this.physics.add.overlap(bullets, enemies, hitEnemy, null, this); this.physics.add.overlap(player, enemies, hitPlayer, null, this); } function update(time, delta) { if (gameOver) return; // Player Movement player.setVelocity(0); if (wasd.left.isDown || cursors.left.isDown) { player.setVelocityX(-250); } else if (wasd.right.isDown || cursors.right.isDown) { player.setVelocityX(250); } if (wasd.up.isDown || cursors.up.isDown) { player.setVelocityY(-250); } else if (wasd.down.isDown || cursors.down.isDown) { player.setVelocityY(250); } // Aiming let angle = Phaser.Math.Angle.Between(player.x, player.y, this.input.x, this.input.y); player.setRotation(angle); // Firing if (this.input.activePointer.isDown && time > lastFired) { let bullet = bullets.get(player.x, player.y); if (bullet) { bullet.setActive(true); bullet.setVisible(true); bullet.body.enabled = true; this.physics.velocityFromRotation(angle, 500, bullet.body.velocity); lastFired = time + 100; } } // Clean up bullets bullets.children.each(function(b) { if (b.active && (b.y < -10 || b.y > 610 || b.x < -10 || b.x > 810)) { b.setActive(false); b.setVisible(false); b.body.enabled = false; } }, this); // Enemies follow player enemies.children.each(function(enemy) { if (enemy.active) { let angle = Phaser.Math.Angle.Between(enemy.x, enemy.y, player.x, player.y); this.physics.velocityFromRotation(angle, 80 + (wave * 2), enemy.body.velocity); enemy.setRotation(angle); } }, this); } function spawnWave() { if (gameOver) return; let count = Math.min(10, 2 + Math.floor(wave / 3)); for (let i = 0; i < count; i++) { let side = Phaser.Math.Between(0, 3); let x, y; if (side === 0) { x = Phaser.Math.Between(0, 800); y = -30; } else if (side === 1) { x = Phaser.Math.Between(0, 800); y = 630; } else if (side === 2) { x = -30; y = Phaser.Math.Between(0, 600); } else { x = 830; y = Phaser.Math.Between(0, 600); } let enemy = enemies.get(x, y, 'enemy'); if (enemy) { enemy.setActive(true); enemy.setVisible(true); enemy.body.enabled = true; } } wave++; } function hitEnemy(bullet, enemy) { if (!bullet.active || !enemy.active) return; bullet.setActive(false); bullet.setVisible(false); bullet.body.enabled = false; enemy.setActive(false); enemy.setVisible(false); enemy.body.enabled = false; score += 10; scoreText.setText('SCORE: ' + score); } function hitPlayer(p, enemy) { if (!enemy.active) return; enemy.setActive(false); enemy.setVisible(false); enemy.body.enabled = false; health -= 10; healthText.setText('HEALTH: ' + health); if (health <= 30) healthText.setFill('#f00'); this.cameras.main.shake(100, 0.02); if (health <= 0) { health = 0; healthText.setText('HEALTH: 0'); this.physics.pause(); player.setTint(0xff0000); gameOver = true; this.add.text(400, 300, 'MISSION FAILED', { fontSize: '64px', fill: '#f00', fontStyle: 'bold' }).setOrigin(0.5); this.add.text(400, 370, 'Final Score: ' + score, { fontSize: '32px', fill: '#fff' }).setOrigin(0.5); this.add.text(400, 430, 'Click to Restart', { fontSize: '24px', fill: '#0f0' }).setOrigin(0.5); this.input.on('pointerdown', () => { location.reload(); }); } } </script> </body> </html>

assistant
thinking
**Refining Texture Generation**

I've realized I inadvertently removed the texture creation call from the `create` function within the `edit` process. My intention was to leverage existing generation, but it seems I missed implementing that call. I'm currently reviewing the flow to rectify this.

edit {"path":"index.html","edits":[{"newText":" function create() {\n createTextures(this);\n \n // Add s
toolResult

Successfully replaced 1 block(s) in index.html.

assistant
thinking
**Reviewing Code Functionality**

I've completed a thorough review of the code. All core features, including texture generation, WASD/arrow key movement, bullet firing with pooling, wave progression, and health/score tracking, are implemented and functioning as expected.

bash {"command":"ls -l index.html phaser.min.js"}
toolResult

-rw-r--r-- 1 node node 9476 Jul 22 18:22 index.html -rw-r--r-- 1 node node 1181917 Jul 22 18:21 phaser.min.js

assistant
thinking
**Verifying Game Functionality**

The game files are in place and I believe it should now function as expected, incorporating `phaser.min.js`, WASD/arrow controls, mouse aiming, and procedural graphics. I'm performing a final review of the enemy pooling mechanism, particularly focusing on the `enemies.get(x, y, 'enemy')` instantiation call.

bash {"command":"grep \"createTextures(this);\" index.html"}
toolResult

createTextures(this);

assistant

I have built a self-contained top-down alien shooter in `index.html`. ### Key Features: - **Procedural Graphics**: All entities (Player, Aliens, Bullets, Stars) are generated at runtime using Phaser's `Graphics` API. - **Controls**: - **Movement**: Use `WASD` or Arrow keys. - **Aiming**: Use the Mouse cursor to rotate the ship. - **Firing**: Click and hold the Left Mouse Button to shoot. - **Gameplay**: - **Waves**: Aliens spawn at the edges of the screen in increasingly larger waves. - **Difficulty**: Alien movement speed and spawn density increase as you progress through waves. - **Combat**: Aliens follow the player; direct contact reduces Health. Bullets destroy aliens and increase your Score. - **Feedback**: Damage causes screen shakes and visual cues. - **Technical**: Uses the local `phaser.min.js`, runs fully offline without external assets or network calls, and includes an automated restart mechanic on Game Over. ```bash ls index.html phaser.min.js ``` The application is ready for use.

TAKE IT TO CODE

A single self-contained HTML file — copy it and open in any browser, or reuse the prompt with your own pi-agent run.

RUN

modelgemini-3-flash-preview
vendorGoogle
harnesspi-agent
generated2026-07-22

EVALUATION

3/5 working

Top-down alien shooter with player ship, aliens, health/score HUD, but health drains fast leading to quick game over.

Vision grade · anthropic/claude-sonnet-5

MEASUREMENTS

Raw readings from the capture, not a verdict — the grade above is the only judgement. Percentages are how much of the screen changed; the measure under-reports sparse motion on a dark background.

idle motion0.3%
on click0.8%
on drag0.7%
on wheel0.3%
on enter + space0.5%
on arrow keys3.3%
on w/a/s/d0.0%
frame spread11.9 / 255
console errors0
js errors none

METRICS

runtime66.5s
tokens in49k
tokens out8.7k
cached16k
cost / run$0.0512