oneshotlm
Compare all models on this prompt →

Top-down alien shooter · gpt-oss-20b

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.

⚠ no runnable output

This one-shot run produced no runnable artifact.

agent produced no index.html

The transcript below shows what the model did — read it to see why the one shot fell short.

transcript · 2 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
The user wants a single-page web app, index.html, that uses phaser.min.js (already present). They want a top-down alien shooter with WASD movement, aim and shoot. Procedurally generated graphics only. No external assets. Use only phaser and JS and CSS within index.html. We need to create an index.html file with full code. We need to load phaser.min.js via <script src="phaser.min.js"></script> then own script.

We'll need to build Phaser game config. Phaser global is available.

Game: top-down shooter: player ship, aliens spawn waves, move forward, maybe zigzag. We need health, score, increasing difficulty. Aim with mouse or maybe aim with direction? "Aim" implies aiming with mouse? We can allow mouse to target. Click to fire. Use arrow keys or WASD for movement.

Procedurally generated graphics: use Phaser graphics to draw shapes, generate texture from graphics.

We can create player ship as a polygon shape. Aliens as circles or squares.

Simplify: Use Phaser 3 or 2? We'll use Phaser 3 because phaser.min.js likely Phaser 3.

We'll create a canvas that fills window. Game size maybe 800x600.

Game structure:

- Preload: none or minimal
- Create: create player sprite as a graphics? We can create a graphics object, render to texture. Use this as player's sprite. But easier: use Phaser.Graphics to draw shapes at runtime. Or use Phaser.GameObjects.Sprite created from a texture. But we can create texture via texture manager: this.textures.generate. Example: graphics.createTexture('player'). We'll generate a simple shape.

Simplify: Use Phaser.GameObjects.Graphics to draw shapes as needed each frame? but we need physics. We can use an image texture.

Same for aliens and bullets.

We'll create textures in the create method:

We can create a 20x20 rectangle rotated (like arrow) as player.

Aliens: simple green squares.

Bullets: small red rectangle.

Use physics.arcade.

Game logic:

- Player movement: WASD or arrow keys, set velocity accordingly.
- Aiming: mouse pointer relative to player position. We calculate angle using Phaser.Math.Angle.Between.

- Shooting: on pointer down, fire bullet from player pos in direction of pointer. Bullet magnitude maybe a velocity constant.

- Aliens: spawn in waves: for each wave, spawn some number of aliens at random positions at top of screen, maybe moving downwards. With difficulty increased each wave: more aliens, larger speed.

- Aliens move toward player or downward. We'll just move downward.

- Collisions: bullet- alien: destroy alien, increase score. Player- alien: damage, reduce health - alien destroyed. When health 0 too show Game Over.

- Difficulty: timer triggers new wave after some time or after all aliens are destroyed.

- Display score, health.

- Score: integer.

- Keep bullet list, alien group.

- Use physics groups.

- Use no images.

- Procedurally generated texture: we can create simple textures:

Use this.make.graphics and generateTexture.

Example:

const graphics = this.add.graphics({});

graphics.fillStyle(0x00ff00, 1);

graphics.fillRect(0, 0, 20, 20);

graphics.generateTexture('alien', 20, 20);

Don't forget to destroy graphics.

Same for player:

graphics.clear(); graphics.fillStyle(0x0000ff, 1); graphics.fillTriangle(-10,10,10,10,0,-10);

Wait for angle? use the same orientation.

Perhaps we can use simple arrow shape.

And bullet: rectangle maybe 4x8.

The code must be all in one index.html.

We need to ensure that the script tags are inside <body> maybe after phaser.

Let's compose:

<!DOCTYPE html>

<html lang="en">

<head><meta charset="UTF-8"><title>Alien Shooter</title><style>/*some global style to center canvas*/</style></head>

<body>

<script src="phaser.min.js"></script>

<script>const config={type:Phaser.AUTO,width:800,height:600,physics:{default:"arcade", arcade:{debug:false}},scene:{preload,create,update}};var score=0;var health=3;var wave=1;var bullets,aliens,player,bulletSpeed=300,alienSpeed=100,shootTimer=null;var lastShot=0;var bulletCooldown=200;var keys;const game=new Phaser.Game(config);function preload(){// nothing; }function create(){player=this.physics.add.sprite(400,300, 'player');player.setCollideWorldBounds(true); bullets=this.physics.add.group(); aliens=this.physics.add.group();this.input.on('pointerdown',fireBullet);keys=this.input.keyboard.addKeys('W,A,S,D');this.cameras.main.startFollow(player, false, 0.07, 0.07 );spawnWave(); this.scoreText=this.add.text(10,10,'Score: 0', {font:'20px Arial', fill:'#FFF'}).setScrollFactor(0); this.healthText=this.add.text(10,40,'Health: 3', {font:'20px Arial', fill:'#FFF'}).setScrollFactor(0); this.waveText=this.add.text(10,70,'Wave: 1', {font:'20px Arial', fill:'#FFF'}).setScrollFactor(0); this.physics.add.overlap(bullets, aliens, bulletAlienCollision, null, this); this.physics.add.overlap(player, aliens, playerAlienCollision, null, this); }function update(){// movement
var vx=0,vy=0;
if(keys.W.isDown){vy=-1;}
if(keys.S.isDown){vy=1;}
if(keys.A.isDown){vx=-1;}
if(keys.D.isDown){vx=1;}
var speed=150;
player.setVelocity(vx*speed, vy*speed); // aim
var pointer=this.input.activePointer; var angle=Phaser.Math.Angle.Between(player.x,pointer.worldX, player.y,pointer.worldY); player.setRotation(angle); // spawn logic
if (aliens.countActive()===0 && Phaser.Time.now>lastWaveTime){
   wave++;
   spawnWave();
} }function spawnWave(){// spawn aliens at random top positions, count depends on wave
var count=5+wave*2;
var startY=-20;
for(var i=0;i<count;i++){
    var x=Phaser.Math.Between(0,800);
    var alien=this.physics.add.sprite(x,startY, 'alien');
    alien.setData('direction', 'down');
    alien.setVelocity(0, alienSpeed+wave*20); // center spawns
}
lastWaveTime=Phaser.Time.now+2000;
}function fireBullet(pointer){var now=Phaser.Time.now; if(now-lastShot<bulletCooldown) return; var bullet=this.physics.add.sprite(player.x,player.y, 'bullet'); var angle=Phaser.Math.Angle.Between(player.x,player.y,pointer.worldX,pointer.worldY); bullet.setRotation(angle); var vx=Math.cos(angle)*bulletSpeed; var vy=Math.sin(angle)*bulletSpeed; bullet.setVelocity(vx,vy); bullet.setCollideWorldBounds(true); bullet.setBounce(1);
bullet.setData('bounced',0);
bullets.add(bullet);
lastShot=now;
}function bulletAlienCollision(bullet,alien){ bullet.destroy(); alien.destroy(); score+=10; this.scoreText.setText('Score: '+score); }function playerAlienCollision(player,alien){ alien.destroy(); health--; this.healthText.setText('Health: '+health); if(health<=0){ gameOver(); } }function gameOver(){this.physics.pause(); this.add.text(400,300,'GAME OVER', {fontSize:'64px', color:'#f00'}).setOrigin(0.5); } // create textures before game start? Use a simple function to generate textures
function generateTextures(){var g= this.make.graphics({x:0,y:0,add:false}); // player as arrow
g.fillStyle(0x00aaff,1); g.beginPath(); g.moveTo(-10,10); g.lineTo(10,10); g.lineTo(0,-10); g.closePath(); g.fill(); g.generateTexture('player',20,20);
g.clear(); // alien as square
g.fillStyle(0x00ff00,1); g.fillRect(0,0,20,20); g.generateTexture('alien',20,20);
g.clear(); // bullet
g.fillStyle(0xff0000,1); g.fillRect(0,0,4,8); g.generateTexture('bullet',4,8);
g.destroy();
}
// need to call generateTextures after config? we can place inside create before using textures
Better: call generateTextures inside create before creation of player. We'll modify.

But we need to incorporate that in code.

Also stage: set background color.

We need to manage variable lastWaveTime.

Let's define lastWaveTime = 0 global.

Also inside create, after generating textures.

Let's build the script accordingly.

But need to support click to fire. Use pointerdown.

Also use physics Group for aliens and bullets; we need to specify bullet group static? we can just create group.

For simplicity, all bunch.

Also treat bullets bounce? We can have them dead when out of bounds via world bounds. Set setCollideWorldBounds(true) and setOutOfBoundsKill? In Arcade physics, we can set world bounds kill.

So bullet.setCollideWorldBounds(true); bullet.setBounce(1); bullet.setCollideWorldBounds(false) no.

I'll use bullet.setCollideWorldBounds with setOnWorldBounds? I can use bullet.setCollideWorldBounds(true); set knocked 0 perhaps.

Better: bullet.body.onWorldBounds = true; bullet.setCollideWorldBounds(false) no. According to docs: bullet.setCollideWorldBounds(true) will bounce? Actually bounce requires setBounce. We'll set bounce 1, so bullet will bounce. Not necessary; we can simply destroy bullet when it exits world. Use bullet.body.setCollideWorldBounds(true); bullet.body.onWorldBounds = true; But simplest: set bullet.setCollideWorldBounds(true) and set bullet.setBounce(1) maybe not needed. We'll use bullet.setCollideWorldBounds(true) and set bullet.setBounce(0).

We can also use world bounds else bullet remains inside.

For bullet out-of-bounds kill, we can just add world bounds event.

Simplest: bullet.setCollideWorldBounds(true); bullet.setBounce(1) - bounce, but we want destroy after hitting edge. We can instead use setCollideWorldBounds(true); bullet.setVelocity(); bullet.setCollideWorldBounds(false)...

Better approach: we can destroy bullet when out of bounds by using:

this.physics.world.on('worldbounds',function(body){ if(body.gameObject.getData('isBullet')){ body.gameObject.destroy(); } });

Set bullet.setData('isBullet',true). Use world bounds event.

Simplify: add world bound event in create.

We need to set bullet.setCollideWorldBounds(true); bullet.body.onWorldBounds = true; bullet.setData('isBullet', true); attach worldbounds event.

Let's implement.

Also we need to adjust global spawn and counters.

Let's code properly.

We'll ensure Node environment, but for JS.

Given constraints, we can produce final index.html file with code inside.

But note we will need to create textures before using them in create. Actually we must generate textures before we add player sprite. We'll call generateTextures inside create before adding player.

Now create function.

Let's write final code:

- index.html: includes <style> to set body margin 0, background black, canvas center.

- The script includes game logic.

Let's craft code.

Implementation details:

Consider wave spawn logic: we'll spawn additionally each time aliens destroyed.

Simplify: spawn after all aliens are destroyed.

We'll track lastSpawnTime to avoid infinite loops; but if aliens count 0 after spawn, we spawn next wave. So no delay.

But to avoid quick spawn, set a delay of 3 seconds between waves. Use lastWaveTime variable.

Implementation: inside create, set lastWaveTime = 0;

In update:

if (aliens.countActive()===0 && Phaser.Time.now > lastWaveTime + 3000) { wave++; spawnWave(); }

But we need lastWaveTime updated after spawn: lastWaveTime = Phaser.Time.now;

Let's do that.

Also spawnWave uses the current wave number to determine number and speed.

Okay.

Edge Cases: Player cannot go out of bounds, set collisions.

Add camera follow? We might skip camera, but set world bounds to game width, height.

Better: not use camera, use static world.

But Player at center.

Given the resources, we can display score in top left.

Ok.

Now produce final code:

index.html content.

Let's write final answer.

**index.html** (create this file in the current directory) ```html <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Top‑Down Alien Shooter</title> <style> *{margin:0;padding:0;overflow:hidden;} body{background:#111;color:#fff;font-family:monospace;} #game{display:block;margin:auto;} canvas{display:block;} </style> </head> <body> <script src="phaser.min.js"></script> <script> (() => { const config = { type: Phaser.AUTO, width: 800, height: 600, backgroundColor: 0x000000, physics: { default: 'arcade', arcade: { debug: false } }, scene: { preload, create, update } }; let score = 0, health = 3, wave = 1; let lastWaveTime = 0, lastShot = 0; const bulletCooldown = 200; // ms const bulletSpeed = 400; const baseAlienSpeed = 50; // will increase with wave let player, bullets, aliens, keys; const game = new Phaser.Game(config); function preload() { /* nothing to load here */ } function create() { /* ------------------------------------------------------ */ /* generate procedural textures (no external assets) */ /* ------------------------------------------------------ */ const g = this.make.graphics({ x: 0, y: 0, add: false }); // Player – a simple blue arrow g.lineStyle(1, 0x00aaff, 1); g.fillStyle(0x00aaff, 1); g.beginPath(); g.moveTo(-10, 10); g.lineTo(10, 10); g.lineTo(0, -10); g.closePath(); g.fillPath(); g.generateTexture('player', 20, 20); // Alien – green square g.clear(); g.fillStyle(0x00ff00, 1); g.fillRect(0, 0, 20, 20); g.generateTexture('alien', 20, 20); // Bullet – red rectangle g.clear(); g.fillStyle(0xff0000, 1); g.fillRect(0, 0, 4, 8); g.generateTexture('bullet', 4, 8); g.destroy(); /* ------------------------------------------------------ */ /* game objects */ /* ------------------------------------------------------ */ player = this.physics.add.sprite(400, 300, 'player'); player.setCollideWorldBounds(true); bullets = this.physics.add.group(); aliens = this.physics.add.group(); /* mouse click => fire */ this.input.on('pointerdown', fireBullet); /* keyboard keys */ keys = this.input.keyboard.addKeys('W,A,S,D'); /* text UI */ this.scoreText = this.add.text(10, 10, 'Score: 0', { font: '18px Arial', fill: '#fff' }) .setScrollFactor(0); this.healthText = this.add.text(10, 30, 'Health: 3', { font: '18px Arial', fill: '#fff' }) .setScrollFactor(0); this.waveText = this.add.text(10, 50, 'Wave: 1', { font: '18px Arial', fill: '#fff' }) .setScrollFactor(0); /* collisions */ this.physics.add.overlap(bullets, aliens, bulletAlienCollision, null, this); this.physics.add.overlap(player, aliens, playerAlienCollision, null, this); /* world‑bounds kill for bullets */ this.physics.world.on('worldbounds', (body) => { if (body.gameObject.getData('isBullet')) body.gameObject.destroy(); }); /* initially spawn first wave */ spawnWave(); } function update(time, delta) { /* ------------------------------------------------------ */ /* movement */ /* ------------------------------------------------------ */ let vx = 0, vy = 0; if (keys.W.isDown) vy = -1; if (keys.S.isDown) vy = 1; if (keys.A.isDown) vx = -1; if (keys.D.isDown) vx = 1; const speed = 150; player.setVelocity(vx * speed, vy * speed); /* rotation to aim cursor */ const pointer = this.input.activePointer; const angle = Phaser.Math.Angle.Between(player.x, player.y, pointer.worldX, pointer.worldY); player.setRotation(angle); /* wave spawn logic */ if (aliens.countActive() === 0 && time > lastWaveTime + 3000) { wave++; spawnWave(); } } /* ---------------------------------------------------------- */ /* helper: spawn a new wave of aliens */ /* ---------------------------------------------------------- */ function spawnWave() { const count = 5 + wave * 2; const speed = baseAlienSpeed + wave * 20; const startY = -20; for (let i = 0; i < count; i++) { const x = Phaser.Math.Between(0, 800); const alien = this.physics.add.sprite(x, startY, 'alien'); alien.setVelocity(0, speed); } lastWaveTime = this.time.now; this.waveText.setText(`Wave: ${wave}`); } /* ---------------------------------------------------------- */ /* helper: fire a bullet */ /* ---------------------------------------------------------- */ function fireBullet(pointer) { const now = this.time.now; if (now - lastShot < bulletCooldown) return; const bullet = this.physics.add.sprite(player.x, player.y, 'bullet'); bullet.setData('isBullet', true); const ang = Phaser.Math.Angle.Between(player.x, player.y, pointer.worldX, pointer.worldY); bullet.setRotation(ang); const vx = Math.cos(ang) * bulletSpeed; const vy = Math.sin(ang) * bulletSpeed; bullet.setVelocity(vx, vy); bullet.setCollideWorldBounds(true); bullet.body.onWorldBounds = true; // will trigger worldbounds event bullets.add(bullet); lastShot = now; } /* ---------------------------------------------------------- */ /* collision handlers */ /* ---------------------------------------------------------- */ function bulletAlienCollision(bullet, alien) { bullet.destroy(); alien.destroy(); score += 10; this.scoreText.setText(`Score: ${score}`); } function playerAlienCollision(player, alien) { alien.destroy(); health--; this.healthText.setText(`Health: ${health}`); if (health <= 0) gameOver.call(this); } /* ---------------------------------------------------------- */ /* game over */ /* ---------------------------------------------------------- */ function gameOver() { this.physics.pause(); this.add.text(400, 300, 'GAME OVER', { fontSize: '64px', fill: '#f33' }) .setOrigin(0.5); this.time.addEvent({ delay: 2000, callback: () => { this.scene.restart(); health = 3; score = 0; wave = 1; }, callbackScope: this }); } })(); </script> </body> </html> ``` **How it works** 1. **Textures** – All graphics are created procedurally with `Phaser.GameObjects.Graphics` and converted to textures (`player`, `alien`, `bullet`). No external images, sounds or libraries are used. 2. **Controls** – *W/A/S/D* move the ship. Mouse cursor is used for aiming; click to shoot. 3. **Gameplay** – * Bullets travel in the aimed direction. * Aliens spawn in waves; each wave has more and faster enemies. * Collision logic handles damage, score and game‑over. 4. **UI** – Score, health, and current wave are displayed in the corner. The whole application fits inside one self‑contained `index.html`. Just open the file in a browser to play.

TAKE IT TO CODE

This run produced no artifact — reuse the prompt with a different model or your own pi-agent run.

RUN

modelgpt-oss-20b
vendorOpenAI
harnesspi-agent
generated2026-07-22

METRICS

runtime
tokens in49
tokens out4.5k
cached1.2k
cost / run$0.0006