Generated banner for Game Engine
Generated logo for Game Engine

Game Engine

GitHubOfficialFirst committed by jhaugaDistributed by GitHub

  • Skill
  • Tool integration
  • Software development
  • MIT
  • No issues found

From the author

Expert skill for building web-based game engines and games using HTML5, Canvas, WebGL, and JavaScript. Use when asked to create games, build game engines, implement game physics, handle collision detection, set up game loops, manage sprites, add game controls, or work with 2D/3D rendering. Covers techniques for platformers, breakout-style games, maze games, tilemaps, audio, multiplayer via WebRTC, and publishing games.
— the description by jhauga

AutoGineer summary

Written by AutoGineer from the skill’s own files, not by the author

The Game Engine skill provides a structured approach to developing interactive 2D and 3D games for the web. It covers the entire lifecycle of game development, from core engine architecture—such as the game loop and rendering pipelines—to specific implementations of physics, collision detection, and input handling. The skill includes reference documentation on web APIs, algorithms, and 3D theory, alongside practical templates for common game types like platformers and maze games.

What it does

  • Provides workflows for building game engines and games from scratch.
  • Explains core concepts like game loops, rendering, and physics.
  • Offers reference material for WebGL, Canvas, and Web Audio APIs.
  • Includes starter templates for 2D platformers and breakout-style games.
  • Covers advanced topics like raycasting, collision detection, and multiplayer networking.
  • Guides users through asset optimization and game publishing.

When to use it

  • When you need to build a web-based game or a custom game engine.
  • When implementing game mechanics like physics, collision, or input controls.
  • When you need to understand 3D rendering pipelines or WebGL fundamentals.
  • When you are looking for starter code for 2D or 3D web game projects.
  • When you need to optimize game performance or handle cross-browser audio issues.

Advantages

  • Centralizes essential game development knowledge for the web.
  • Provides ready-to-use templates to accelerate project setup.
  • Offers clear, step-by-step procedures for common game development tasks.
  • Includes troubleshooting guides for common rendering and performance issues.
  • Supports both 2D (Canvas) and 3D (WebGL/Three.js) development workflows.

Usage examples

“How do I set up a basic game loop using requestAnimationFrame?”
The agent provides a code structure that initializes a canvas, defines an update function for game logic, and a render function for drawing, tied together by a recursive requestAnimationFrame call.
“Can you help me implement AABB collision detection for my 2D platformer?”
The agent explains the Axis-Aligned Bounding Box algorithm and provides a JavaScript function to check if two rectangular objects overlap.
“What are the steps to publish my web game?”
The agent outlines a checklist including asset compression, code minification, cross-browser testing, and choosing a distribution platform.

Install

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/github/game-engine/download
  2. Unzip it into the cross-client skills folder of your project:

    mkdir -p .agents/skills && unzip game-engine.zip -d .agents/skills

Documentation by jhauga

Game Engine Skill

Build web-based games and game engines using HTML5 Canvas, WebGL, and JavaScript. This skill includes starter templates, reference documentation, and step-by-step workflows for 2D and 3D game development with frameworks such as Phaser, Three.js, Babylon.js, and A-Frame.

When to Use This Skill

  • Building a game engine or game from scratch using web technologies

  • Implementing game loops, physics, collision detection, or rendering

  • Working with HTML5 Canvas, WebGL, or SVG for game graphics

  • Adding game controls (keyboard, mouse, touch, gamepad)

  • Creating 2D platformers, breakout-style games, maze games, or 3D experiences

  • Working with tilemaps, sprites, or animations

  • Adding audio to web games

  • Implementing multiplayer features with WebRTC or WebSockets

  • Optimizing game performance

  • Publishing and distributing web games

Prerequisites

  • Basic knowledge of HTML, CSS, and JavaScript

  • A modern web browser with Canvas/WebGL support

  • A text editor or IDE

  • Optional: Node.js for build tooling and local development servers

Core Concepts

The following concepts form the foundation of every web-based game engine.

Game Loop

Every game engine revolves around the game loop -- a continuous cycle of:

  1. Process Input - Read keyboard, mouse, touch, or gamepad input

  2. Update State - Update game object positions, physics, AI, and logic

  3. Render - Draw the current game state to the screen

Use requestAnimationFrame for smooth, browser-optimized rendering.

Rendering
  • Canvas 2D - Best for 2D games, sprite-based rendering, and tilemaps

  • WebGL - Hardware-accelerated 3D and advanced 2D rendering

  • SVG - Vector-based graphics, good for UI elements

  • CSS - Useful for DOM-based game elements and transitions

Physics and Collision Detection
  • 2D Collision Detection - AABB, circle, and SAT-based collision

  • 3D Collision Detection - Bounding box, bounding sphere, and raycasting

  • Velocity and Acceleration - Basic Newtonian physics for movement

  • Gravity - Constant downward acceleration for platformers

Controls
  • Keyboard - Arrow keys, WASD, and custom key bindings

  • Mouse - Click, move, and pointer lock for FPS-style controls

  • Touch - Mobile touch events and virtual joysticks

  • Gamepad - Gamepad API for controller support

Audio
  • Web Audio API - Programmatic sound generation and spatial audio

  • HTML5 Audio - Simple audio playback for music and sound effects

Step-by-Step Workflows

Creating a Basic 2D Game
  1. Set up an HTML file with a <canvas> element

  2. Get the 2D rendering context

  3. Implement the game loop using requestAnimationFrame

  4. Create game objects with position, velocity, and size properties

  5. Handle keyboard/mouse input for player control

  6. Implement collision detection between game objects

  7. Add scoring, lives, and win/lose conditions

  8. Add sound effects and music

Building a 3D Game
  1. Choose a framework (Three.js, Babylon.js, A-Frame, or PlayCanvas)

  2. Set up the scene, camera, and renderer

  3. Load or create 3D models and textures

  4. Implement lighting and shaders

  5. Add physics and collision detection

  6. Implement player controls and camera movement

  7. Add audio and visual effects

Publishing a Game
  1. Optimize assets (compress images, minify code)

  2. Test across browsers and devices

  3. Choose distribution platform (web, app stores, game portals)

  4. Implement monetization if needed

  5. Promote through game communities and social media

Game Templates

Starter templates are available in the assets/ folder. Each template provides a complete, working example that can be used as a starting point for a new project.

TemplateDescription
paddle-game-template.md2D Breakout-style game with pure JavaScript
2d-maze-game.mdMaze game with device orientation controls
2d-platform-game.mdPlatformer game using Phaser framework
gameBase-template-repo.mdGame base template repository structure
simple-2d-engine.mdSimple 2D platformer engine with collisions

Reference Documentation

Detailed reference material is available in the references/ folder. Consult these files for in-depth coverage of specific topics.

ReferenceTopics Covered
basics.mdGame development introduction and anatomy
web-apis.mdCanvas, WebGL, Web Audio, Gamepad, and other web APIs
techniques.mdCollision detection, tilemaps, async scripts, audio
3d-web-games.md3D theory, frameworks, shaders, WebXR
game-control-mechanisms.mdTouch, keyboard, mouse, and gamepad controls
game-publishing.mdDistribution, promotion, and monetization
algorithms.mdRaycasting, collision, physics, vector math
terminology.mdGame development glossary
game-engine-core-principles.mdCore design principles for game engines

Troubleshooting

IssueSolution
Canvas is blankCheck that you are calling drawing methods after getting the context and inside the game loop
Game runs at different speedsUse delta time in update calculations instead of fixed values
Collision detection is inconsistentUse continuous collision detection or reduce time steps for fast-moving objects
Audio does not playBrowsers require user interaction before playing audio; trigger playback from a click handler
Performance is poorProfile with browser dev tools, reduce draw calls, use object pooling, and optimize asset sizes
Touch controls are unresponsivePrevent default touch behavior and handle touch events separately from mouse events
WebGL context lostHandle the webglcontextlost event and restore state on webglcontextrestored

Files

assets/simple-2d-engine.md

Simple 2D Platformer Engine Template

A grid-based 2D platformer engine tutorial by Sebastien Benard (deepnight), the lead developer behind Dead Cells. This template covers the fundamental architecture for a performant platformer: a dual-coordinate positioning system that blends integer grid cells with sub-pixel precision, velocity and friction mechanics, gravity, and a robust collision detection and response system. The approach is language-agnostic but examples use Haxe.

Source references:

Author: Sebastien Benard / deepnight


Engine Architecture Overview

The engine is built around a grid-based world where each cell has a fixed pixel size (e.g., 16x16). Entities exist within this grid using a dual-coordinate system: integer cell coordinates for coarse position and floating-point ratios for sub-pixel precision within each cell. This design enables pixel-perfect collision detection against the grid while maintaining smooth, fluid movement.

Core Principles
  1. Grid is truth: The world is a 2D grid of cells. Collision data lives in the grid.

  2. Entities straddle cells: An entity's position is defined by which cell it occupies (cx, cy) plus how far into that cell it is (xr, yr).

  3. Velocity is in grid-ratio units: Movement deltas (dx, dy) represent fractions of a cell per step, not raw pixels.

  4. Collisions are grid lookups: Instead of testing sprite bounds against geometry, the engine checks the grid cells an entity is about to enter.


Part 1: Basics

The Grid

The level is a 2D array where each cell is either empty or solid. A constant defines the cell size in pixels:

static inline var GRID = 16;

Collision data is stored as a simple 2D boolean or integer map:

// Check if a grid cell is solid
function hasCollision(cx:Int, cy:Int):Bool {
  // Look up cell value in the level data
  return level.getCollision(cx, cy) != 0;
}
Entity Positioning: Dual Coordinates

Every entity tracks its position using four values:

VariableTypeDescription
cxIntCell X coordinate (which column the entity is in)
cyIntCell Y coordinate (which row the entity is in)
xrFloatX ratio within the cell, range 0.0 to 1.0
yrFloatY ratio within the cell, range 0.0 to 1.0

An entity at cx=5, cy=3, xr=0.5, yr=1.0 is horizontally centered in cell (5,3) and sitting on the bottom edge.

Converting to Pixel Coordinates

To render the entity, convert grid coordinates to pixel positions:

// Pixel position for rendering
var pixelX : Float = (cx + xr) * GRID;
var pixelY : Float = (cy + yr) * GRID;

This produces smooth, sub-pixel-precise positions for rendering even though the collision system operates on discrete grid cells.

Velocity and Movement

Velocity is expressed in cell-ratio units per fixed-step (not pixels per frame):

var dx : Float = 0; // Horizontal velocity (cells per step)
var dy : Float = 0; // Vertical velocity (cells per step)

Each fixed-step update, velocity is added to the ratio:

// Apply horizontal movement
xr += dx;

// Apply vertical movement
yr += dy;
Cell Overflow

When the ratio exceeds the 0..1 range, the entity has moved into an adjacent cell:

// X overflow
while (xr > 1) { xr--; cx++; }
while (xr < 0) { xr++; cx--; }

// Y overflow
while (yr > 1) { yr--; cy++; }
while (yr < 0) { yr++; cy--; }
Friction

Friction is applied as a multiplier each step, decaying velocity toward zero:

var frictX : Float = 0.82; // Horizontal friction (0 = instant stop, 1 = no friction)
var frictY : Float = 0.82; // Vertical friction

// Applied each step after movement
dx *= frictX;
dy *= frictY;

// Clamp very small values to zero
if (Math.abs(dx) < 0.0005) dx = 0;
if (Math.abs(dy) < 0.0005) dy = 0;

Typical friction values:

  • 0.82 -- Standard ground friction (responsive, quick stop)

  • 0.94 -- Ice or slippery surface (slow deceleration)

  • 0.96 -- Air friction (very slow horizontal deceleration)

Gravity

Gravity is a constant added to dy each step:

static inline var GRAVITY = 0.05; // In cell-ratio units per step^2

// In fixedUpdate:
dy += GRAVITY;

Since dy accumulates and friction is applied, the entity reaches a natural terminal velocity.

Rendering / Sprite Sync

After the physics step, the sprite is placed at the computed pixel position:

// In postUpdate, after physics is done:
sprite.x = (cx + xr) * GRID;
sprite.y = (cy + yr) * GRID;

For a platformer character, the anchor point is typically at the bottom-center of the sprite. With yr = 1.0 representing the bottom of the current cell, the sprite's feet align with the floor.

Basic Entity Template
class Entity {
  // Grid coordinates
  var cx : Int = 0;
  var cy : Int = 0;
  var xr : Float = 0.5;
  var yr : Float = 1.0;

  // Velocity
  var dx : Float = 0;
  var dy : Float = 0;

  // Friction
  var frictX : Float = 0.82;
  var frictY : Float = 0.82;

  // Gravity
  static inline var GRAVITY = 0.05;

  // Grid size
  static inline var GRID = 16;

  // Pixel position (computed)
  public var attachX(get, never) : Float;
  inline function get_attachX() return (cx + xr) * GRID;

  public var attachY(get, never) : Float;
  inline function get_attachY() return (cy + yr) * GRID;

  public function fixedUpdate() {
    // Gravity
    dy += GRAVITY;

    // Apply velocity
    xr += dx;
    yr += dy;

    // Apply friction
    dx *= frictX;
    dy *= frictY;

    // Clamp small values
    if (Math.abs(dx) < 0.0005) dx = 0;
    if (Math.abs(dy) < 0.0005) dy = 0;

    // Cell overflow
    while (xr > 1) { xr--; cx++; }
    while (xr < 0) { xr++; cx--; }
    while (yr > 1) { yr--; cy++; }
    while (yr < 0) { yr++; cy--; }
  }

  public function postUpdate() {
    sprite.x = attachX;
    sprite.y = attachY;
  }
}

Part 2: Collisions

Collision Philosophy

Instead of using bounding-box-to-bounding-box collision detection (which becomes complex with slopes, one-way platforms, and edge cases), this engine checks grid cells directly. Since the entity's position is already expressed in grid terms, collision detection becomes a series of simple integer lookups.

The Core Idea

Before allowing the entity to move into a neighboring cell, check if that cell is solid. If it is, clamp the entity's ratio and zero out its velocity on that axis.

Axis Separation

Collisions are handled per axis -- first X, then Y (or vice versa). This simplifies the logic and avoids corner-case tunneling issues.

X-Axis Collision

After applying dx to xr, before doing the cell-overflow step, check for collisions:

// Apply X movement
xr += dx;

// Check collision to the RIGHT
if (dx > 0 && hasCollision(cx + 1, cy) && xr >= 0.7) {
  xr = 0.7;   // Clamp: stop before entering the solid cell
  dx = 0;     // Kill horizontal velocity
}

// Check collision to the LEFT
if (dx < 0 && hasCollision(cx - 1, cy) && xr <= 0.3) {
  xr = 0.3;   // Clamp: stop before entering the solid cell
  dx = 0;     // Kill horizontal velocity
}

// Cell overflow (after collision check)
while (xr > 1) { xr--; cx++; }
while (xr < 0) { xr++; cx--; }

Why 0.7 and 0.3? These thresholds represent the entity's collision radius within a cell. An entity centered at xr = 0.5 with a half-width of 0.3 cells would collide at xr = 0.7 on the right side and xr = 0.3 on the left side. Adjust these values based on entity width.

Y-Axis Collision

Similarly, after applying dy to yr:

// Apply Y movement
yr += dy;

// Check collision BELOW (floor)
if (dy > 0 && hasCollision(cx, cy + 1) && yr >= 1.0) {
  yr = 1.0;   // Clamp: land on top of the solid cell
  dy = 0;     // Kill vertical velocity
}

// Check collision ABOVE (ceiling)
if (dy < 0 && hasCollision(cx, cy - 1) && yr <= 0.3) {
  yr = 0.3;   // Clamp: stop before entering ceiling cell
  dy = 0;     // Kill vertical velocity
}

// Cell overflow
while (yr > 1) { yr--; cy++; }
while (yr < 0) { yr++; cy--; }

For floor collisions, yr = 1.0 means the entity sits exactly on the bottom edge of its current cell, which is the top edge of the cell below it. This is the natural "standing on ground" position.

On-Ground Detection

To determine if the entity is standing on solid ground (for jump logic, animations, etc.):

function isOnGround() : Bool {
  return hasCollision(cx, cy + 1) && yr >= 0.98;
}

The threshold 0.98 instead of 1.0 allows for minor floating-point imprecision.

Complete Entity with Collisions
class Entity {
  var cx : Int = 0;
  var cy : Int = 0;
  var xr : Float = 0.5;
  var yr : Float = 1.0;
  var dx : Float = 0;
  var dy : Float = 0;
  var frictX : Float = 0.82;
  var frictY : Float = 0.82;

  static inline var GRID = 16;
  static inline var GRAVITY = 0.05;

  // Collision radius (half-width in cell-ratio units)
  var collRadius : Float = 0.3;

  function hasCollision(testCx:Int, testCy:Int):Bool {
    return level.isCollision(testCx, testCy);
  }

  function isOnGround():Bool {
    return hasCollision(cx, cy + 1) && yr >= 0.98;
  }

  public function fixedUpdate() {
    // --- Gravity ---
    dy += GRAVITY;

    // --- X Axis ---
    xr += dx;

    // Right collision
    if (dx > 0 && hasCollision(cx + 1, cy) && xr >= 1.0 - collRadius) {
      xr = 1.0 - collRadius;
      dx = 0;
    }

    // Left collision
    if (dx < 0 && hasCollision(cx - 1, cy) && xr <= collRadius) {
      xr = collRadius;
      dx = 0;
    }

    // X cell overflow
    while (xr > 1) { xr--; cx++; }
    while (xr < 0) { xr++; cx--; }

    // --- Y Axis ---
    yr += dy;

    // Floor collision
    if (dy > 0 && hasCollision(cx, cy + 1) && yr >= 1.0) {
      yr = 1.0;
      dy = 0;
    }

    // Ceiling collision
    if (dy < 0 && hasCollision(cx, cy - 1) && yr <= collRadius) {
      yr = collRadius;
      dy = 0;
    }

    // Y cell overflow
    while (yr > 1) { yr--; cy++; }
    while (yr < 0) { yr++; cy--; }

    // --- Friction ---
    dx *= frictX;
    dy *= frictY;

    if (Math.abs(dx) < 0.0005) dx = 0;
    if (Math.abs(dy) < 0.0005) dy = 0;
  }

  public function postUpdate() {
    sprite.x = (cx + xr) * GRID;
    sprite.y = (cy + yr) * GRID;
  }
}

Collision Edge Cases and Solutions

Diagonal Movement / Corner Clipping

Because collisions are checked per-axis in sequence, an entity moving diagonally into a corner naturally resolves against one axis first. This prevents the entity from getting stuck in corners and eliminates the need for complex diagonal collision logic.

High-Speed Tunneling

If dx or dy is large enough to skip an entire cell in one step, the entity could "tunnel" through walls. Solutions:

  1. Cap velocity: Clamp dx and dy to a maximum of 0.5 (half a cell per step)

  2. Subdivide steps: If velocity exceeds the threshold, run the collision check in smaller increments

  3. Ray-march the grid: Check every cell along the movement path

// Simple velocity cap
if (dx > 0.5) dx = 0.5;
if (dx < -0.5) dx = -0.5;
if (dy > 0.5) dy = 0.5;
if (dy < -0.5) dy = -0.5;
One-Way Platforms

Platforms the entity can jump up through but land on from above:

// In Y collision, check for one-way platform
if (dy > 0 && isOneWayPlatform(cx, cy + 1) && yr >= 1.0 && prevYr < 1.0) {
  yr = 1.0;
  dy = 0;
}

Key: Only collide when the entity is moving downward (dy > 0) and was previously above the platform (prevYr < 1.0).

Slopes

For basic slope support, instead of a binary collision check, query the slope height at the entity's x-position within the cell:

// Pseudocode for slope collision
var slopeHeight = getSlopeHeight(cx, cy + 1, xr);
if (yr >= slopeHeight) {
  yr = slopeHeight;
  dy = 0;
}

Jumping

Jumping is simply a negative dy impulse:

function jump() {
  if (isOnGround()) {
    dy = -0.5; // Jump impulse (in cell-ratio units)
  }
}

Gravity naturally decelerates the upward motion, creating a parabolic arc. To allow variable-height jumps (holding the button longer = higher jump):

// On jump button release, reduce upward velocity
function onJumpRelease() {
  if (dy < 0) {
    dy *= 0.5; // Cut remaining upward velocity
  }
}

Coordinate System Diagram

  Cell (cx, cy)           Next Cell (cx+1, cy)
  +-------------------+   +-------------------+
  |                   |   |                   |
  |  xr=0.0    xr=1.0 --> |  xr=0.0           |
  |                   |   |                   |
  |         *         |   |                   |
  |     (xr=0.5,      |   |                   |
  |      yr=0.5)      |   |                   |
  |                   |   |                   |
  +-------------------+   +-------------------+
  yr=0.0      yr=1.0 = top of cell below

  Pixel position = (cx + xr) * GRID, (cy + yr) * GRID

Update Order Summary

fixedUpdate():
  1. Apply gravity          dy += GRAVITY
  2. Apply X velocity       xr += dx
  3. Check X collisions     Clamp xr, zero dx if colliding
  4. Handle X cell overflow cx/xr normalization
  5. Apply Y velocity       yr += dy
  6. Check Y collisions     Clamp yr, zero dy if colliding
  7. Handle Y cell overflow cy/yr normalization
  8. Apply friction         dx *= frictX, dy *= frictY
  9. Zero out tiny values   Threshold check

postUpdate():
  1. Sync sprite position   sprite.x/y = pixel coords
  2. Update animation       Based on state/velocity
  3. Camera follow          Track entity

Design Advantages

FeatureBenefit
Grid-based collisionO(1) lookup per check, no broad-phase needed
Dual coordinatesSub-pixel smooth rendering with integer collision
Per-axis collisionSimple logic, naturally handles corners
Ratio-based velocityResolution-independent movement
Friction multiplierTunable feel per surface type
Cell overflow while-loopsHandles multi-cell movement safely

Requirements and compatibility

Written for: GitHub Copilot

Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta

“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.

Security

No issues found by our static scan· No issues found

A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.

Attribution and licence

First committed by
GitHub avatar of jhaugajhauga
Publisher
GitHub (Official)
Distributed by
GitHub
Source
github/awesome-copilot · skills/game-engine · commit 797337f
Licence
MIT License (redistribution allowed)
  • LICENSE (added by AutoGineer from LICENSE)
  • ATTRIBUTION.md (written by AutoGineer)
Synced
Contributors
jhauga

AutoGineer is not affiliated with or endorsed by jhauga or GitHub.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download game-engine.zip