screenshot

CHECK PASS: canvas #game exists and is sized CHECK PASS: no uncaught JS errors on load CHECK PASS: keyboard input does not throw score: 3/3 (100%)
/**
* Mini Platformer Game Logic
*/
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
// Game Configuration
const GRAVITY = 0.5;
const FRICTION = 0.8;
const PLAYER_SPEED = 5;
const JUMP_FORCE = -12;
// Game State
let score = 0;
const totalCoins = 5;
let gameActive = true;
let win = false;
// Input handling
const keys = {};
window.addEventListener('keydown', (e) => {
keys[e.code] = true;
});
window.addEventListener('keyup', (e) => {
keys[e.code] = false;
});
// Entities
class Player {
constructor(x, y) {
this.x = x;
this.y = y;
this.width = 30;
this.height = 30;
this.vx = 0;
this.vy = 0;
this.grounded = false;
this.color = '#FF5733'; // Reddish player
}
update() {
// Horizontal Movement
if (keys['ArrowLeft'] || keys['KeyA']) {
this.vx -= 1;
} else if (keys['ArrowRight'] || keys['KeyD']) {
this.vx += 1;
} else {
this.vx *= FRICTION;
}
// Limit speed
if (this.vx > PLAYER_SPEED) this.vx = PLAYER_SPEED;
if (this.vx < -PLAYER_SPEED) this.vx = -PLAYER_SPEED;
// Jump
if ((keys['ArrowUp'] || keys['KeyW'] || keys['Space']) && this.grounded) {
this.vy = JUMP_FORCE;
this.grounded = false;
}
// Apply Gravity
this.vy += GRAVITY;
// Apply Velocity
this.x += this.vx;
this.y += this.vy;
// Floor collision (absolute bottom)
if (this.y + this.height > canvas.height) {
this.y = canvas.height - this.height;
this.vy = 0;
this.grounded = true;
}
// Screen boundaries
if (this.x < 0) this.x = 0;
if (this.x + this.width > canvas.width) this.x = canvas.width - this.width;
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
// Eyes for the player
ctx.fillStyle = 'white';
ctx.fillRect(this.x + 5, this.y + 5, 5, 5);
ctx.fillRect(this.x + 20, this.y + 5, 5, 5);
}
}
class Platform {
constructor(x, y, width, height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = '#4B2C20'; // Brown platform
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
// Grass top
ctx.fillStyle = '#2E8B57';
ctx.fillRect(this.x, this.y, this.width, 5);
}
}
class Coin {
constructor(x, y) {
this.x = x;
this.y = y;
this.radius = 10;
this.collected = false;
this.floatY = 0;
}
update() {
this.floatY = Math.sin(Date.now() / 200) * 5;
}
draw() {
if (this.collected) return;
ctx.fillStyle = '#FFD700'; // Gold
ctx.beginPath();
ctx.arc(this.x, this.y + this.floatY, this.radius, 0, Math.PI * 2);
ctx.fill();
// Shine
ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
ctx.beginPath();
ctx.arc(this.x - 3, this.y + this.floatY - 3, 3, 0, Math.PI * 2);
ctx.fill();
}
}
// Initialize game objects
const player = new Player(50, 350);
const platforms = [
new Platform(0, 400, 800, 50), // Ground
new Platform(150, 300, 150, 20),
new Platform(400, 220, 150, 20),
new Platform(100, 150, 120, 20),
new Platform(550, 320, 100, 20),
new Platform(650, 150, 100, 20)
];
const coins = [
new Coin(225, 270),
new Coin(475, 180),
new Coin(160, 110),
new Coin(600, 280),
new Coin(700, 110)
];
function checkCollisions() {
player.grounded = false;
// Platform collision
platforms.forEach(platform => {
// Simple AABB collision for platform tops
if (player.vx >= 0 || player.vx < 0) { // Horizontal check not strictly needed for top-only landing
if (player.x < platform.x + platform.width &&
player.x + player.width > platform.x &&
player.y + player.height > platform.y &&
player.y + player.height < platform.y + platform.height + player.vy + 1 &&
player.vy >= 0) {
player.y = platform.y - player.height;
player.vy = 0;
player.grounded = true;
}
}
});
// Coin collision
coins.forEach(coin => {
if (!coin.collected) {
const dx = (player.x + player.width / 2) - coin.x;
const dy = (player.y + player.height / 2) - (coin.y + coin.floatY);
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance < player.width / 2 + coin.radius) {
coin.collected = true;
score++;
scoreElement.innerText = `Coins: ${score} / ${totalCoins}`;
if (score === totalCoins) {
win = true;
}
}
}
});
}
function update() {
if (!gameActive) return;
player.update();
coins.forEach(c => c.update());
checkCollisions();
}
function draw() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw background clouds/decorations (simple)
ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
ctx.beginPath(); ctx.arc(100, 100, 30, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(130, 100, 40, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(160, 100, 30, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(600, 80, 25, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(630, 80, 35, 0, Math.PI*2); ctx.fill();
// Draw world
platforms.forEach(p => p.draw());
coins.forEach(c => c.draw());
player.draw();
// Win overlay
if (win) {
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.font = '48px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('YOU WIN!', canvas.width / 2, canvas.height / 2);
ctx.font = '24px sans-serif';
ctx.fillText('Refresh to play again', canvas.width / 2, canvas.height / 2 + 40);
}
}
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
// Start the game
gameLoop();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mini Platformer</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #333;
color: white;
font-family: sans-serif;
}
canvas {
background-color: #87CEEB; /* Sky blue */
border: 4px solid #222;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
display: block;
}
.ui {
margin-top: 10px;
text-align: center;
}
</style>
</head>
<body>
<div class="ui">
<h1>Mini Platformer</h1>
<p>Use WASD or Arrow Keys to move & jump. Collect all coins!</p>
</div>
<canvas id="game" width="800" height="450"></canvas>
<div class="ui">
<div id="score">Coins: 0 / 5</div>
</div>
<script src="game.js"></script>
</body>
</html>
screenshot

CHECK PASS: canvas #game exists and is sized CHECK PASS: no uncaught JS errors on load CHECK PASS: keyboard input does not throw score: 3/3 (100%)
/**
* Super Mario Style Platformer - Game Engine
*/
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const scoreDisplay = document.getElementById('score-display');
const messageDisplay = document.getElementById('message-display');
// --- Game Configuration ---
const GRAVITY = 0.6;
const FRICTION = 0.8;
const PLAYER_SPEED = 5;
const PLAYER_JUMP = -12;
// --- Game State ---
let score = 0;
let gameOver = false;
let gameWon = false;
let cameraX = 0;
// --- Input Handling ---
const keys = {};
window.addEventListener('keydown', e => keys[e.code] = true);
window.addEventListener('keyup', e => keys[e.code] = false);
// --- Classes ---
class Entity {
constructor(x, y, width, height, color) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.color = color;
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x - cameraX, this.y, this.width, this.height);
}
}
class Player extends Entity {
constructor(x, y) {
super(x, y, 30, 40, '#ff4444'); // Red character
this.vx = 0;
this.vy = 0;
this.onGround = false;
}
update() {
// Horizontal Movement
if (keys['ArrowLeft'] || keys['KeyA']) {
this.vx = -PLAYER_SPEED;
} else if (keys['ArrowRight'] || keys['KeyD']) {
this.vx = PLAYER_SPEED;
} else {
this.vx *= FRICTION;
}
// Jump
if ((keys['Space'] || keys['ArrowUp'] || keys['KeyW']) && this.onGround) {
this.vy = PLAYER_JUMP;
this.onGround = false;
}
// Gravity
this.vy += GRAVITY;
// Apply Velocity
this.x += this.vx;
this.y += this.vy;
// Prevent moving off left edge (optional, but good for gameplay)
if (this.x < 0) this.x = 0;
// Screen Bounds (Bottom)
if (this.y + this.height > canvas.height + 100) {
this.reset();
}
}
reset() {
this.x = 50;
this.y = 100;
this.vx = 0;
this.vy = 0;
// We don't reset score here, just respawn
}
draw() {
// Draw body
ctx.fillStyle = this.color;
ctx.fillRect(this.x - cameraX, this.y, this.width, this.height);
// Draw "eyes" to show direction
ctx.fillStyle = 'white';
let eyeX = this.vx >= 0 ? this.x - cameraX + 20 : this.x - cameraX + 5;
ctx.fillRect(eyeX, this.y + 10, 5, 5);
}
}
class Platform extends Entity {
constructor(x, y, width, height, color = '#8B4513') {
super(x, y, width, height, color);
}
}
class Coin extends Entity {
constructor(x, y) {
super(x, y, 15, 15, '#ffd700');
this.collected = false;
}
draw() {
if (this.collected) return;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x - cameraX + this.width/2, this.y + this.height/2, this.width/2, 0, Math.PI * 2);
ctx.fill();
}
}
class Goal extends Entity {
constructor(x, y) {
super(x, y, 40, 100, '#00ff00');
}
draw() {
// Draw a flag pole
ctx.fillStyle = '#555';
ctx.fillRect(this.x - cameraX, this.y, 5, this.height);
// Draw flag
ctx.fillStyle = 'red';
ctx.fillRect(this.x - cameraX, this.y, 30, 20);
}
}
// --- World Data ---
const player = new Player(50, 100);
const platforms = [
new Platform(0, 400, 1000, 50), // Floor 1
new Platform(1100, 400, 800, 50), // Floor 2 (with a pit)
new Platform(1200, 300, 150, 20), // Platform 1
new Platform(1450, 200, 150, 20), // Platform 2
new Platform(1700, 300, 200, 20), // Platform 3
new Platform(2000, 400, 1000, 50), // Floor 3
new Platform(2200, 250, 100, 20), // High platform
new Platform(2500, 320, 150, 20), // High platform
new Platform(2800, 400, 500, 50), // Floor 4
];
const coins = [
new Coin(300, 350),
new Coin(1250, 250),
new Coin(1500, 150),
new Coin(1750, 250),
new Coin(2250, 200),
new Coin(2550, 270),
new Coin(2900, 350),
];
const goal = new Goal(3100, 300);
// --- Collision Logic ---
function checkCollisions() {
player.onGround = false;
// Platforms
for (const p of platforms) {
// Standard AABB collision
if (player.x < p.x + p.width &&
player.x + player.width > p.x &&
player.y < p.y + p.height &&
player.y + player.height > p.y) {
// Determine which side the collision happened on
const overlapTop = (player.y + player.height) - p.y;
const overlapBottom = (p.y + p.height) - player.y;
const overlapLeft = (player.x + player.width) - p.x;
const overlapRight = (p.x + p.width) - player.x;
const minOverlap = Math.min(overlapTop, overlapBottom, overlapLeft, overlapRight);
if (minOverlap === overlapTop && player.vy > 0) {
// Landing on top
player.y = p.y - player.height;
player.vy = 0;
player.onGround = true;
} else if (minOverlap === overlapBottom && player.vy < 0) {
// Hitting bottom
player.y = p.y + p.height;
player.vy = 0;
} else if (minOverlap === overlapLeft && player.vx > 0) {
// Hitting left
player.x = p.x - player.width;
player.vx = 0;
} else if (minOverlap === overlapRight && player.vx < 0) {
// Hitting right
player.x = p.x + p.width;
player.vx = 0;
}
}
}
// Coins
for (const c of coins) {
if (!c.collected &&
player.x < c.x + c.width &&
player.x + player.width > c.x &&
player.y < c.y + c.height &&
player.y + player.height > c.y) {
c.collected = true;
score += 10;
scoreDisplay.innerText = `Coins: ${score}`;
}
}
// Goal
if (player.x < goal.x + goal.width &&
player.x + player.width > goal.x &&
player.y < goal.y + goal.height &&
player.y + player.height > goal.y) {
gameWon = true;
}
}
// --- Game Loop ---
function update() {
if (gameOver || gameWon) return;
player.update();
checkCollisions();
// Camera follow
// Center the camera on player, but clamp to world boundaries (roughly)
// For simplicity, we'll just follow player X with some smoothing
const targetCameraX = player.x - canvas.width / 2;
cameraX += (targetCameraX - cameraX) * 0.1;
if (cameraX < 0) cameraX = 0;
}
function draw() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw sky/background (already handled by canvas background color)
// Draw platforms
for (const p of platforms) p.draw();
// Draw coins
for (const c of coins) c.draw();
// Draw goal
goal.draw();
// Draw player
player.draw();
// Draw Win Message
if (gameWon) {
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.font = '48px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('YOU WIN!', canvas.width / 2, canvas.height / 2);
ctx.font = '24px sans-serif';
ctx.fillText(`Final Score: ${score}`, canvas.width / 2, canvas.height / 2 + 50);
}
}
function loop() {
update();
draw();
requestAnimationFrame(loop);
}
// Start game
loop();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Super Mario Style Platformer</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
background-color: #333;
color: white;
font-family: sans-serif;
overflow: hidden;
}
canvas {
border: 4px solid #000;
background-color: #5c94fa; /* Classic Mario Sky Blue */
box-shadow: 0 0 20px rgba(0,0,0,0.5);
}
.ui {
margin-top: 10px;
text-align: center;
}
.controls {
font-size: 0.8em;
color: #ccc;
margin-top: 5px;
}
</style>
</head>
<body>
<div id="game-container">
<canvas id="game" width="800" height="450"></canvas>
</div>
<div class="ui">
<div id="score-display">Coins: 0</div>
<div id="message-display"></div>
<div class="controls">Use ARROWS or WASD to Move & Jump</div>
</div>
<script src="game.js"></script>
</body>
</html>
screenshot

CHECK PASS: canvas #game exists and is sized CHECK PASS: no uncaught JS errors on load CHECK PASS: keyboard input does not throw score: 3/3 (100%)
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
const messageElement = document.getElementById('message');
// Game Constants
const GRAVITY = 0.6;
const FRICTION = 0.8;
const PLAYER_SPEED = 5;
const JUMP_FORCE = -12;
const WORLD_WIDTH = 2000; // The level is wider than the canvas
// Game State
let score = 0;
let gameOver = false;
let win = false;
// Input handling
const keys = {};
window.addEventListener('keydown', (e) => keys[e.code] = true);
window.addEventListener('keyup', (e) => keys[e.code] = false);
// Entities
class Player {
constructor() {
this.width = 30;
this.height = 40;
this.x = 50;
this.y = 300;
this.vx = 0;
this.vy = 0;
this.grounded = false;
this.color = '#e74c3c'; // Red character
}
update() {
// Input
if (keys['ArrowLeft'] || keys['KeyA']) {
if (this.vx > -PLAYER_SPEED) this.vx--;
}
if (keys['ArrowRight'] || keys['KeyD']) {
if (this.vx < PLAYER_SPEED) this.vx++;
}
if ((keys['ArrowUp'] || keys['KeyW'] || keys['Space']) && this.grounded) {
this.vy = JUMP_FORCE;
this.grounded = false;
}
// Physics
this.vx *= FRICTION;
this.vy += GRAVITY;
this.x += this.vx;
this.y += this.vy;
// Boundary checks (left/right)
if (this.x < 0) this.x = 0;
if (this.x + this.width > WORLD_WIDTH) this.x = WORLD_WIDTH - this.width;
// Ground/Platform collision is handled in the main loop
}
draw(cameraX) {
ctx.fillStyle = this.color;
ctx.fillRect(this.x - cameraX, this.y, this.width, this.height);
// Draw a little "eye" to show direction
ctx.fillStyle = 'white';
let eyeOffset = this.vx >= 0 ? 18 : 5;
ctx.fillRect(this.x - cameraX + eyeOffset, this.y + 8, 7, 7);
}
}
class Platform {
constructor(x, y, w, h, color = '#2ecc71') {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.color = color;
}
draw(cameraX) {
ctx.fillStyle = this.color;
ctx.fillRect(this.x - cameraX, this.y, this.w, this.h);
// Add a little detail
ctx.strokeStyle = 'rgba(0,0,0,0.1)';
ctx.strokeRect(this.x - cameraX, this.y, this.w, this.h);
}
}
class Coin {
constructor(x, y) {
this.x = x;
this.y = y;
this.radius = 10;
this.collected = false;
this.angle = 0;
}
update() {
this.angle += 0.1;
}
draw(cameraX) {
if (this.collected) return;
ctx.fillStyle = '#f1c40f';
ctx.beginPath();
let wobble = Math.sin(this.angle) * 3;
ctx.arc(this.x - cameraX, this.y + wobble, this.radius, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = '#d4ac0d';
ctx.lineWidth = 2;
ctx.stroke();
}
}
class Goal {
constructor(x, y) {
this.x = x;
this.y = y;
this.w = 40;
this.h = 100;
}
draw(cameraX) {
// Flag pole
ctx.fillStyle = '#7f8c8d';
ctx.fillRect(this.x - cameraX, this.y, 5, this.h);
// Flag
ctx.fillStyle = '#f1c40f';
ctx.fillRect(this.x - cameraX + 5, this.y, 30, 20);
}
}
// Initialize Game Objects
const player = new Player();
const platforms = [
new Platform(0, 400, 1000, 50), // Ground 1
new Platform(1100, 400, 900, 50), // Ground 2 (gap in between)
new Platform(200, 300, 150, 20), // Platform 1
new Platform(450, 220, 150, 20), // Platform 2
new Platform(700, 300, 150, 20), // Platform 3
new Platform(1200, 300, 200, 20), // Platform 4
new Platform(1500, 200, 100, 20), // Platform 5
];
const coins = [
new Coin(250, 250),
new Coin(500, 180),
new Coin(750, 250),
new Coin(1250, 250),
new Coin(1550, 150),
new Coin(1800, 350),
];
const goal = new Goal(1900, 300);
let cameraX = 0;
function checkCollisions() {
player.grounded = false;
for (let p of platforms) {
// AABB collision
if (player.x < p.x + p.w &&
player.x + player.width > p.x &&
player.y < p.y + p.h &&
player.y + player.height > p.y) {
// Calculate overlap depths to find collision direction
let overlapX = Math.min(player.x + player.width - p.x, p.x + p.w - player.x);
let overlapY = Math.min(player.y + player.height - p.y, p.y + p.h - player.y);
if (overlapX < overlapY) {
// Collision on X axis
if (player.x < p.x) {
player.x = p.x - player.width;
} else {
player.x = p.x + p.w;
}
player.vx = 0;
} else {
// Collision on Y axis
if (player.y + player.height / 2 < p.y + player.h / 2) {
// Falling onto platform
player.y = p.y - player.height;
player.vy = 0;
player.grounded = true;
} else {
// Hitting head on platform
player.y = p.y + p.h;
player.vy = 0;
}
}
}
}
// Coin collection
for (let coin of coins) {
if (!coin.collected) {
let dx = (player.x + player.width/2) - coin.x;
let dy = (player.y + player.height/2) - coin.y;
let distance = Math.sqrt(dx * dx + dy * dy);
if (distance < player.width/2 + coin.radius) {
coin.collected = true;
score += 100;
scoreElement.innerText = score;
}
}
}
// Goal check
if (player.x + player.width > goal.x && player.x < goal.x + goal.w &&
player.y + player.height > goal.y && player.y < goal.y + goal.h) {
win = true;
messageElement.innerText = "YOU WIN!";
messageElement.style.color = "#2ecc71";
messageElement.style.display = "block";
gameOver = true;
}
// Fall off world
if (player.y > canvas.height) {
gameOver = true;
messageElement.innerText = "GAME OVER";
messageElement.style.color = "#e74c3c";
messageElement.style.display = "block";
}
}
function update() {
if (gameOver) return;
player.update();
for (let coin of coins) {
coin.update();
}
checkCollisions();
// Camera follows player
// Center camera on player
let targetCameraX = player.x - canvas.width / 2;
// Clamp camera
if (targetCameraX < 0) targetCameraX = 0;
if (targetCameraX > WORLD_WIDTH - canvas.width) targetCameraX = WORLD_WIDTH - canvas.width;
// Smooth camera
cameraX += (targetCameraX - cameraX) * 0.1;
}
function draw() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Draw background clouds/decoration (static simple shapes)
ctx.fillStyle = 'rgba(255, 255, 255, 0.3)';
ctx.beginPath(); ctx.arc(100 - cameraX * 0.2, 100, 40, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(140 - cameraX * 0.2, 110, 50, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(500 - cameraX * 0.2, 80, 30, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(800 - cameraX * 0.2, 150, 60, 0, Math.PI*2); ctx.fill();
// Draw platforms
for (let p of platforms) {
p.draw(cameraX);
}
// Draw coins
for (let coin of coins) {
coin.draw(cameraX);
}
// Draw goal
goal.draw(cameraX);
// Draw player
player.draw(cameraX);
}
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
// Start the game
gameLoop();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Super Mario Style Platformer</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #333;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
color: white;
overflow: hidden;
}
#game-container {
position: relative;
box-shadow: 0 0 50px rgba(0,0,0,0.5);
}
canvas {
background-color: #5c94fc; /* Sky blue */
display: block;
}
#ui {
position: absolute;
top: 10px;
left: 10px;
pointer-events: none;
text-shadow: 2px 2px 2px rgba(0,0,0,0.5);
font-size: 24px;
}
#message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 48px;
text-align: center;
display: none;
text-shadow: 3px 3px 5px rgba(0,0,0,0.8);
}
</style>
</head>
<body>
<div id="game-container">
<canvas id="game" width="800" height="450"></canvas>
<div id="ui">Coins: <span id="score">0</span></div>
<div id="message"></div>
</div>
<script src="game.js"></script>
</body>
</html>
screenshot

CHECK PASS: canvas #game exists and is sized CHECK PASS: no uncaught JS errors on load CHECK PASS: keyboard input does not throw score: 3/3 (100%)
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
const scoreElement = document.getElementById('score');
// Game Constants
const GRAVITY = 0.6;
const FRICTION = 0.8;
const PLAYER_SPEED = 5;
const JUMP_FORCE = -12;
const CANVAS_WIDTH = 800;
const CANVAS_HEIGHT = 450;
// Game State
let score = 0;
let gameActive = true;
let gameOverReason = "";
// Input state
const keys = {};
// Entity Classes
class Player {
constructor() {
this.width = 30;
this.height = 40;
this.x = 50;
this.y = CANVAS_HEIGHT - 100;
this.vx = 0;
this.vy = 0;
this.grounded = false;
this.color = "#ff4444";
}
update() {
if (keys['ArrowLeft'] || keys['KeyA']) {
if (this.vx > -PLAYER_SPEED) this.vx--;
}
if (keys['ArrowRight'] || keys['KeyD']) {
if (this.vx < PLAYER_SPEED) this.vx++;
}
if ((keys['ArrowUp'] || keys['KeyW'] || keys['Space']) && this.grounded) {
this.vy = JUMP_FORCE;
this.grounded = false;
}
this.vx *= FRICTION;
this.vy += GRAVITY;
this.x += this.vx;
this.y += this.vy;
// Collision with screen bounds
if (this.x < 0) this.x = 0;
if (this.x + this.width > CANVAS_WIDTH) this.x = CANVAS_WIDTH - this.width;
// Basic floor collision (hardcoded floor at bottom for safety)
if (this.y + this.height > CANVAS_HEIGHT - 20) {
this.y = CANVAS_HEIGHT - 20 - this.height;
this.vy = 0;
this.grounded = true;
} else {
this.grounded = false;
}
// Check if fell off map (if we had a real camera, but here it's fixed)
if (this.y > CANVAS_HEIGHT) {
gameOver("You fell!");
}
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.width, this.height);
// Eyes to show direction
ctx.fillStyle = "white";
let eyeOffset = this.vx >= 0 ? 20 : 5;
ctx.fillRect(this.x + eyeOffset, this.y + 10, 5, 5);
}
}
class Platform {
constructor(x, y, w, h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
this.color = "#8B4513";
}
draw() {
ctx.fillStyle = this.color;
ctx.fillRect(this.x, this.y, this.w, this.h);
// Grass top
ctx.fillStyle = "#228B22";
ctx.fillRect(this.x, this.y, this.w, 5);
}
}
class Coin {
constructor(x, y) {
this.x = x;
this.y = y;
this.radius = 10;
this.collected = false;
this.floatY = 0;
}
update() {
this.floatY = Math.sin(Date.now() / 200) * 5;
}
draw() {
if (this.collected) return;
ctx.beginPath();
ctx.arc(this.x, this.y + this.floatY, this.radius, 0, Math.PI * 2);
ctx.fillStyle = "#FFD700";
ctx.fill();
ctx.strokeStyle = "#DAA520";
ctx.lineWidth = 2;
ctx.stroke();
ctx.closePath();
}
}
class Goal {
constructor(x, y) {
this.x = x;
this.y = y;
this.w = 30;
this.h = 60;
}
draw() {
// Flag pole
ctx.fillStyle = "#ddd";
ctx.fillRect(this.x, this.y, 5, this.h);
// Flag
ctx.fillStyle = "red";
ctx.beginPath();
ctx.moveTo(this.x + 5, this.y);
ctx.lineTo(this.x + 30, this.y + 15);
ctx.lineTo(this.x + 5, this.y + 30);
ctx.fill();
}
}
// Game instance
let player;
let platforms = [];
let coins = [];
let goal;
function init() {
player = new Player();
platforms = [
new Platform(0, CANVAS_HEIGHT - 20, CANVAS_WIDTH, 20), // Floor
new Platform(150, 300, 150, 20),
new Platform(400, 220, 150, 20),
new Platform(100, 150, 100, 20),
new Platform(600, 300, 150, 20),
];
coins = [
new Coin(225, 270),
new Coin(475, 190),
new Coin(150, 120),
new Coin(675, 270),
];
goal = new Goal(750, CANVAS_HEIGHT - 80);
score = 0;
scoreElement.innerText = score;
gameActive = true;
gameOverReason = "";
}
function checkCollisions() {
// Platform collisions
player.grounded = false;
// We check against the ground-floor which is already handled in player.update,
// but we need to handle the other platforms.
// Re-check grounding based on all platforms
platforms.forEach(p => {
// Collision detection (AABB)
if (player.x < p.x + p.w &&
player.x + player.width > p.x &&
player.y < p.y + p.h &&
player.y + player.height > p.y) {
// Determine collision side
// To keep it simple for a platformer:
// If player is falling and above the platform, land on top.
if (player.vy > 0 && player.y + player.height - player.vy <= p.y) {
player.y = p.y - player.height;
player.vy = 0;
player.grounded = true;
}
}
});
// Coin collisions
coins.forEach(c => {
if (!c.collected) {
let dx = player.x + player.width/2 - c.x;
let dy = player.y + player.height/2 - (c.y + c.floatY);
let distance = Math.sqrt(dx*dx + dy*dy);
if (distance < player.width/2 + c.radius) {
c.collected = true;
score += 10;
scoreElement.innerText = score;
}
}
});
// Goal collision
if (player.x < goal.x + goal.w &&
player.x + player.width > goal.x &&
player.y < goal.y + goal.h &&
player.y + player.height > goal.y) {
gameOver("YOU WIN!");
}
}
function gameOver(reason) {
gameActive = false;
gameOverReason = reason;
}
function update() {
if (!gameActive) return;
player.update();
coins.forEach(c => c.update());
checkCollisions();
}
function draw() {
// Clear canvas
ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
// Draw Background (Clouds/Sun simple shapes)
ctx.fillStyle = "white";
ctx.beginPath(); ctx.arc(100, 80, 30, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(130, 80, 40, 0, Math.PI*2); ctx.fill();
ctx.beginPath(); ctx.arc(160, 80, 30, 0, Math.PI*2); ctx.fill();
ctx.fillStyle = "#FFD700";
ctx.beginPath(); ctx.arc(700, 60, 40, 0, Math.PI*2); ctx.fill();
// Draw entities
platforms.forEach(p => p.draw());
coins.forEach(c => c.draw());
goal.draw();
player.draw();
if (!gameActive) {
ctx.fillStyle = "rgba(0, 0, 0, 0.5)";
ctx.fillRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
ctx.fillStyle = "white";
ctx.font = "48px Arial";
ctx.textAlign = "center";
ctx.fillText(gameOverReason, CANVAS_WIDTH/2, CANVAS_HEIGHT/2);
ctx.font = "24px Arial";
ctx.fillText("Press 'R' to restart", CANVAS_WIDTH/2, CANVAS_HEIGHT/2 + 50);
}
}
function loop() {
update();
draw();
requestAnimationFrame(loop);
}
// Event Listeners
window.addEventListener('keydown', e => {
keys[e.code] = true;
if (e.code === 'KeyR' && !gameActive) {
init();
}
});
window.addEventListener('keyup', e => {
keys[e.code] = false;
});
// Start game
init();
loop();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Super Mario Style Platformer</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #333;
font-family: Arial, sans-serif;
overflow: hidden;
}
canvas {
background-color: #5c94fc;
border: 4px solid #000;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
}
#ui {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
color: white;
font-size: 24px;
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
pointer-events: none;
}
</style>
</head>
<body>
<div id="ui">Coins: <span id="score">0</span></div>
<canvas id="game" width="800" height="450"></canvas>
<script src="game.js"></script>
</body>
</html>
screenshot

CHECK PASS: canvas #game exists and is sized CHECK PASS: no uncaught JS errors on load CHECK PASS: keyboard input does not throw score: 3/3 (100%)
const canvas = document.getElementById('game');
const ctx = canvas.getContext('2d');
// Game constants
const GRAVITY = 0.6;
const FRICTION = 0.8;
const PLAYER_SPEED = 5;
const JUMP_FORCE = -12;
const GROUND_HEIGHT = 40;
// Game state
let score = 0;
let gameActive = true;
let winState = false;
// Input state
const keys = {};
// Player object
const player = {
x: 50,
y: 300,
width: 30,
height: 30,
velX: 0,
velY: 0,
color: '#ff4444',
onGround: false
};
// Platforms
const platforms = [
{ x: 0, y: 410, width: 2000, height: 40, color: '#8B4513' }, // Main ground
{ x: 200, y: 300, width: 150, height: 20, color: '#555' },
{ x: 450, y: 220, width: 150, height: 20, color: '#555' },
{ x: 150, y: 150, width: 100, height: 20, color: '#555' },
{ x: 700, y: 320, width: 100, height: 20, color: '#555' }
];
// Collectibles (Coins)
const coins = [
{ x: 250, y: 260, width: 15, height: 15, collected: false },
{ x: 480, y: 180, width: 15, height: 15, collected: false },
{ x: 180, y: 110, width: 15, height: 15, collected: false },
{ x: 720, y: 280, width: 15, height: 15, collected: false },
{ x: 1000, y: 370, width: 15, height: 15, collected: false }
];
// Goal
const goal = {
x: 1200,
y: 310,
width: 40,
height: 100,
color: '#ffd700' // Gold flag pole
};
// Camera offset
let cameraX = 0;
// Event listeners
window.addEventListener('keydown', (e) => {
keys[e.code] = true;
});
window.addEventListener('keyup', (e) => {
keys[e.code] = false;
});
function update() {
if (!gameActive) return;
// Movement
if (keys['ArrowRight'] || keys['KeyD']) {
if (player.velX < PLAYER_SPEED) player.velX++;
}
if (keys['ArrowLeft'] || keys['KeyA']) {
if (player.velX > -PLAYER_SPEED) player.velX--;
}
if ((keys['Space'] || keys['ArrowUp'] || keys['KeyW']) && player.onGround) {
player.velY = JUMP_FORCE;
player.onGround = false;
}
// Apply physics
player.velX *= FRICTION;
player.velY += GRAVITY;
player.x += player.velX;
player.y += player.velY;
// Platform collisions
player.onGround = false;
for (let plat of platforms) {
// Check if player is within the platform bounds horizontally
if (player.x < plat.x + plat.width &&
player.x + player.width > plat.x &&
player.y < plat.y + plat.height &&
player.y + player.height > plat.y) {
// Check collision from top (landing)
if (player.velY > 0 && player.y + player.height - player.velY <= plat.y) {
player.y = plat.y - player.height;
player.velY = 0;
player.onGround = true;
}
// Check collision from bottom (hitting head)
else if (player.velY < 0 && player.y - player.velY >= plat.y + plat.height) {
player.y = plat.y + plat.height;
player.velY = 0;
}
// Check collision from sides
else {
// Simple side collision: push out
if (player.x + player.width/2 < plat.x + plat.width/2) {
player.x = plat.x - player.width;
} else {
player.x = plat.x + plat.width;
}
player.velX = 0;
}
}
}
// Collectibles collision
for (let coin of coins) {
if (!coin.collected &&
player.x < coin.x + coin.width &&
player.x + player.width > coin.x &&
player.y < coin.y + coin.height &&
player.y + player.height > coin.y) {
coin.collected = true;
score += 10;
}
}
// Goal collision
if (player.x < goal.x + goal.width &&
player.x + player.width > goal.x &&
player.y < goal.y + goal.height &&
player.y + player.height > goal.y) {
winState = true;
gameActive = false;
}
// Screen bounds (fall off map)
if (player.y > canvas.height) {
// Reset player
player.x = 50;
player.y = 300;
player.velX = 0;
player.velY = 0;
}
// Camera follow
// We center the camera on the player but keep it within reasonable bounds
cameraX = player.x - canvas.width / 4;
if (cameraX < 0) cameraX = 0;
// Check if all coins collected for a bonus win or just to check?
// Let's just say reaching the flag is the win state.
}
function draw() {
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
ctx.translate(-cameraX, 0);
// Draw platforms
for (let plat of platforms) {
ctx.fillStyle = plat.color;
ctx.fillRect(plat.x, plat.y, plat.width, plat.height);
}
// Draw coins
ctx.fillStyle = '#ffd700';
for (let coin of coins) {
if (!coin.collected) {
ctx.beginPath();
ctx.arc(coin.x + coin.width/2, coin.y + coin.height/2, coin.width/2, 0, Math.PI * 2);
ctx.fill();
}
}
// Draw goal
ctx.fillStyle = goal.color;
ctx.fillRect(goal.x, goal.y, goal.width, goal.height);
// Add a small flag at top
ctx.fillStyle = '#ff0000';
ctx.fillRect(goal.x - 10, goal.y, 20, 15);
// Draw player
ctx.fillStyle = player.color;
ctx.fillRect(player.x, player.y, player.width, player.height);
ctx.restore();
// UI (Score)
ctx.fillStyle = 'white';
ctx.font = '24px Arial';
ctx.fillText(`Score: ${score}`, 20, 40);
if (winState) {
ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'white';
ctx.font = '48px Arial';
ctx.textAlign = 'center';
ctx.fillText('YOU WIN!', canvas.width / 2, canvas.height / 2);
ctx.font = '24px Arial';
ctx.fillText('Press F5 to play again', canvas.width / 2, canvas.height / 2 + 40);
}
}
function gameLoop() {
update();
draw();
requestAnimationFrame(gameLoop);
}
// Start game
gameLoop();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Super Mario Style Platformer</title>
<style>
body {
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background-color: #333;
color: white;
font-family: sans-serif;
flex-direction: column;
}
canvas {
background-color: #5c94fc; /* Sky blue */
border: 4px solid #000;
box-shadow: 0 0 20px rgba(0,0,0,0.5);
display: block;
}
.instructions {
margin-top: 20px;
text-align: center;
}
</style>
</head>
<body>
<canvas id="game" width="800" height="450"></canvas>
<div class="instructions">
<p>Use <b>WASD</b> or <b>Arrow Keys</b> to move and jump!</p>
<p>Collect all coins to reach the goal!</p>
</div>
<script src="game.js"></script>
</body>
</html>