AI Skill Report Card

Creating Animated 2D Browser Games

A-88·May 23, 2026·Source: Web
15 / 15
HTML
<!DOCTYPE html> <html> <head> <title>Animated 2D Game</title> <style> canvas { border: 1px solid #000; display: block; margin: 0 auto; } body { margin: 0; padding: 20px; background: #222; } </style> </head> <body> <canvas id="gameCanvas" width="800" height="600"></canvas> <script> const canvas = document.getElementById('gameCanvas'); const ctx = canvas.getContext('2d'); // Game state const game = { player: { x: 100, y: 300, width: 32, height: 32, vx: 0, vy: 0 }, keys: {}, lastTime: 0 }; // Input handling window.addEventListener('keydown', (e) => game.keys[e.code] = true); window.addEventListener('keyup', (e) => game.keys[e.code] = false); function update(deltaTime) { // Player movement if (game.keys['ArrowLeft']) game.player.vx = -200; else if (game.keys['ArrowRight']) game.player.vx = 200; else game.player.vx *= 0.8; // Friction game.player.x += game.player.vx * deltaTime; game.player.x = Math.max(0, Math.min(canvas.width - game.player.width, game.player.x)); } function render() { ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = '#4CAF50'; ctx.fillRect(game.player.x, game.player.y, game.player.width, game.player.height); } function gameLoop(currentTime) { const deltaTime = (currentTime - game.lastTime) / 1000; game.lastTime = currentTime; update(deltaTime); render(); requestAnimationFrame(gameLoop); } requestAnimationFrame(gameLoop); </script> </body> </html>
Recommendation
Add audio implementation examples with Web Audio API usage patterns
14 / 15

Progress:

  • Set up HTML5 Canvas with proper viewport
  • Initialize game state and input handlers
  • Create sprite management system
  • Implement animation controller
  • Add collision detection
  • Build game scenes/states
  • Optimize performance and add audio
  • Test across browsers and devices

Core Game Architecture

JavaScript
class Game { constructor(canvasId) { this.canvas = document.getElementById(canvasId); this.ctx = this.canvas.getContext('2d'); this.scenes = new Map(); this.currentScene = null; this.assetManager = new AssetManager(); this.inputManager = new InputManager(); this.lastTime = 0; this.running = false; } async init() { await this.assetManager.loadAssets({ player: 'assets/player-spritesheet.png', background: 'assets/background.png', sounds: { jump: 'assets/jump.wav', music: 'assets/bgm.mp3' } }); this.start(); } start() { this.running = true; requestAnimationFrame((time) => this.gameLoop(time)); } gameLoop(currentTime) { if (!this.running) return; const deltaTime = Math.min((currentTime - this.lastTime) / 1000, 0.016); this.lastTime = currentTime; this.update(deltaTime); this.render(); requestAnimationFrame((time) => this.gameLoop(time)); } }

Sprite Animation System

JavaScript
class AnimatedSprite { constructor(image, frameWidth, frameHeight, animations) { this.image = image; this.frameWidth = frameWidth; this.frameHeight = frameHeight; this.animations = animations; this.currentAnimation = Object.keys(animations)[0]; this.frame = 0; this.frameTime = 0; this.frameRate = 12; // FPS } update(deltaTime) { this.frameTime += deltaTime; if (this.frameTime >= 1 / this.frameRate) { const anim = this.animations[this.currentAnimation]; this.frame = (this.frame + 1) % anim.length; this.frameTime = 0; } } render(ctx, x, y) { const anim = this.animations[this.currentAnimation]; const frameIndex = anim[this.frame]; const srcX = (frameIndex % (this.image.width / this.frameWidth)) * this.frameWidth; const srcY = Math.floor(frameIndex / (this.image.width / this.frameWidth)) * this.frameHeight; ctx.drawImage(this.image, srcX, srcY, this.frameWidth, this.frameHeight, x, y, this.frameWidth, this.frameHeight); } }
Recommendation
Include mobile touch controls and responsive design considerations in workflow
18 / 20

Example 1: Platform Character Controller Input: Arrow keys for movement, spacebar for jump

JavaScript
class Player extends Entity { constructor(x, y) { super(x, y, 32, 48); this.velocity = { x: 0, y: 0 }; this.onGround = false; this.speed = 150; this.jumpPower = -300; this.gravity = 800; } update(deltaTime, input) { // Horizontal movement if (input.isPressed('ArrowLeft')) { this.velocity.x = -this.speed; this.sprite.setAnimation('run'); this.flipX = true; } else if (input.isPressed('ArrowRight')) { this.velocity.x = this.speed; this.sprite.setAnimation('run'); this.flipX = false; } else { this.velocity.x *= 0.8; this.sprite.setAnimation('idle'); } // Jumping if (input.isJustPressed('Space') && this.onGround) { this.velocity.y = this.jumpPower; this.onGround = false; } // Gravity this.velocity.y += this.gravity * deltaTime; // Update position this.x += this.velocity.x * deltaTime; this.y += this.velocity.y * deltaTime; } }

Example 2: Particle System for Effects Input: Explosion at position (x, y)

JavaScript
class ParticleSystem { constructor() { this.particles = []; } createExplosion(x, y, count = 20) { for (let i = 0; i < count; i++) { this.particles.push({ x, y, vx: (Math.random() - 0.5) * 200, vy: (Math.random() - 0.5) * 200, life: 1.0, decay: Math.random() * 2 + 1, size: Math.random() * 4 + 2, color: `hsl(${Math.random() * 60 + 15}, 100%, 50%)` }); } } update(deltaTime) { this.particles = this.particles.filter(p => { p.x += p.vx * deltaTime; p.y += p.vy * deltaTime; p.life -= p.decay * deltaTime; return p.life > 0; }); } render(ctx) { this.particles.forEach(p => { ctx.save(); ctx.globalAlpha = p.life; ctx.fillStyle = p.color; ctx.fillRect(p.x - p.size/2, p.y - p.size/2, p.size, p.size); ctx.restore(); }); } }
Recommendation
Provide asset loading progress indicator code example for better UX

Performance Optimization:

  • Use object pooling for frequently created/destroyed objects
  • Implement spatial partitioning for collision detection (quadtree)
  • Batch draw calls and minimize canvas state changes
  • Use requestAnimationFrame with delta time for smooth 60fps
  • Preload all assets before game start

Asset Management:

  • Use spritesheets instead of individual images
  • Compress audio files (WebM/OGG for compatibility)
  • Implement progressive loading for large games
  • Cache frequently accessed sprites in memory

Code Structure:

  • Separate game logic from rendering
  • Use component-entity system for complex games
  • Implement scene management (menu, game, pause, gameover)
  • Create reusable systems (audio, input, collision)

Browser Compatibility:

  • Test on Chrome, Firefox, Safari, Edge
  • Handle different screen sizes with responsive canvas
  • Use feature detection for Web Audio API
  • Provide fallbacks for older browsers

Performance Issues:

  • Don't create new objects in game loop - use object pools
  • Avoid expensive operations like Math.sin/cos in tight loops
  • Don't clear entire canvas if only small areas changed
  • Never use setInterval - always use requestAnimationFrame

Animation Problems:

  • Don't tie animation speed to framerate - use delta time
  • Avoid frame-dependent physics calculations
  • Don't forget to reset animation states when switching
  • Handle edge cases in sprite sheet calculations

Input Handling:

  • Don't rely on keypress events for game controls
  • Handle focus loss gracefully (pause game)
  • Account for key repeat delays in movement
  • Test touch controls on mobile devices

Memory Leaks:

  • Remove event listeners when changing scenes
  • Clear intervals and timeouts properly
  • Dispose of audio contexts and image references
  • Use weak references for temporary game objects
0
Grade A-AI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
15/15
Workflow
14/15
Examples
18/20
Completeness
13/20
Format
15/15
Conciseness
13/15