Brick breaker · gpt-oss-120b
Using the pre-provided phaser.min.js (global Phaser), build a playable brick-breaker game with a paddle (arrow keys or mouse), a bouncing ball, and a wall of breakable bricks with a score. Use ONLY procedurally generated graphics via the Phaser Graphics API or generated textures — no external image or audio assets whatsoever.
transcript · 4 messages
Using the pre-provided phaser.min.js (global Phaser), build a playable brick-breaker game with a paddle (arrow keys or mouse), a bouncing ball, and a wall of breakable bricks with a score. Use ONLY procedurally generated graphics via the Phaser Graphics API or generated textures — no external image or audio assets whatsoever.
thinking
We need to create index.html with Phaser loaded from phaser.min.js. The phaser.min.js is present in the directory. We'll need to write index.html containing HTML skeleton, <script src="phaser.min.js"></script>. Then script that sets up Phaser game: config with width/height, physics arcade. Create a scene with preload (maybe none), create (draw paddle, ball, bricks), add controls, collision handling, score text.
We need to generate graphics procedurally: use graphics objects to create textures for paddle, ball, bricks. We'll create them in the scene's create function using this.add.graphics() and generate a texture with generateTexture.
Implement simple brick layout: rows and columns.
Implement controls: paddle moves with arrow keys or pointer move.
Score text.
Let's write index.html.
Plan:
<!DOCTYPE html><html><head><meta charset="utf-8"><title>Brick Breaker</title><style>body{margin:0;}</style></head><body><script src="phaser.min.js"></script><script> ... </script></body></html>
In script:
class BrickBreaker extends Phaser.Scene {
constructor(){ super({key:'Game'}); }
preload() {} // generate textures in create.
create() {
const width = this.sys.game.config.width;
const height = this.sys.game.config.height;
// paddle texture
const paddleWidth=104, paddleHeight=20;
const paddleGraphics = this.add.graphics();
paddleGraphics.fillStyle(0x00ff00,1);
paddleGraphics.fillRoundedRect(0,0,paddleWidth,paddleHeight,5);
paddleGraphics.generateTexture('paddle',paddleWidth,paddleHeight);
paddleGraphics.destroy();
// ball texture
const ballRadius=10;
const ballGraphics = this.add.graphics();
ballGraphics.fillStyle(0xffff00,1);
ballGraphics.fillCircle(ballRadius,ballRadius,ballRadius);
ballGraphics.generateTexture('ball',ballRadius*2,ballRadius*2);
ballGraphics.destroy();
// brick texture
const brickWidth=50, brickHeight=20;
const brickGraphics = this.add.graphics();
brickGraphics.fillStyle(0xff0000,1);
brickGraphics.fillRect(0,0,brickWidth,brickHeight);
brickGraphics.generateTexture('brick',brickWidth,brickHeight);
brickGraphics.destroy();
// create paddle
this.paddle = this.physics.add.sprite(width/2, height - 50, 'paddle').setImmovable(true);
this.paddle.body.allowGravity = false;
this.paddle.setCollideWorldBounds(true);
// create ball
this.ball = this.physics.add.sprite(width/2, height - 70, 'ball');
this.ball.setCollideWorldBounds(true);
this.ball.setBounce(1);
this.ball.setVelocity(150, -150);
this.ball.setData('onPaddle', false);
this.ball.setData('startX', width/2);
// bricks group
this.bricks = this.physics.add.staticGroup();
const rows=5, cols=12;
const offsetX= (width - (cols*brickWidth)) / 2 + brickWidth/2;
const offsetY=60;
for(let row=0; row<rows; row++){
for(let col=0; col<cols; col++){
const x = offsetX + col*brickWidth;
const y = offsetY + row*brickHeight;
const brick = this.bricks.create(x,y,'brick');
brick.setOrigin(0.5);
}
}
// collisions
this.physics.add.collider(this.ball, this.paddle, this.hitPaddle, null, this);
this.physics.add.collider(this.ball, this.bricks, this.hitBrick, null, this);
// input
this.cursors = this.input.keyboard.createCursorKeys();
this.input.on('pointermove', pointer => {
this.paddle.x = Phaser.Math.Clamp(pointer.x, this.paddle.width/2, width - this.paddle.width/2);
});
// score
this.score = 0;
this.scoreText = this.add.text(10,10,'Score: 0',{fontSize:'20px', fill:'#fff'});
}
hitPaddle(ball, paddle){
// adjust ball velocity based on where it hits paddle
const diff = ball.x - paddle.x;
ball.setVelocityX(10 * diff);
}
hitBrick(ball, brick){
brick.disableBody(true,true);
this.score += 10;
this.scoreText.setText('Score: '+this.score);
// win condition
if(this.bricks.countActive()===0){
this.scene.restart();
}
}
update(){
const width = this.sys.game.config.width;
const paddleSpeed = 400;
if(this.cursors.left.isDown){
this.paddle.setVelocityX(-paddleSpeed);
} else if(this.cursors.right.isDown){
this.paddle.setVelocityX(paddleSpeed);
} else {
this.paddle.setVelocityX(0);
}
// Keep paddle within bounds already done by world bounds/ setCollideWorldBounds
// check if ball fell below
if(this.ball.y > this.sys.game.config.height){
// reset
this.ball.setPosition(this.paddle.x, this.paddle.y - 30);
this.ball.setVelocity(150, -150);
}
}
}
const config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#222',
physics: {
default: 'arcade',
arcade: {
debug: false
}
},
scene: BrickBreaker
};
const game = new Phaser.Game(config);
</script></body></html>
Make sure we include phaser.min.js script before our script.
Now we need to create index.html. Use write tool.
index.html (3999 bytes)Successfully wrote 3999 bytes to index.html
Implemented a complete, self‑contained Brick Breaker game using the provided **phaser.min.js**. The `index.html` includes: - Inline CSS for full‑screen canvas. - Procedurally generated paddle, ball, and brick textures using Phaser’s Graphics API. - Arcade physics for realistic bouncing. - Paddle control via arrow keys or mouse movement. - Score tracking and win/restart handling. - All assets generated at runtime, no external resources required. The game runs entirely offline and is ready in the `index.html` file. Enjoy playing!
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
EVALUATION
Functional brick-breaker with paddle, bouncing ball, breakable red bricks, and increasing score across inputs.
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.