Build a Browser-Based Endless Runner Game in TypeScript: Step-by-Step Guide for Beginners

{ Download Full Source code of GAME | Play this Interactive GAME Online }

Key Takeaways

Beginners following this tutorial will learn:

How to set up a TypeScript project and compile it for browser-based games.
Structuring game logic usin…


This content originally appeared on DEV Community and was authored by Md Mahbubur Rahman

{ Download Full Source code of GAME | Play this Interactive GAME Online }

Key Takeaways

Beginners following this tutorial will learn:

  • How to set up a TypeScript project and compile it for browser-based games.
  • Structuring game logic using Object-Oriented Programming (OOP) principles.
  • Creating a humanoid player character with arms, legs, torso, and head.
  • Implementing jump physics and gravity simulation for realistic movement.
  • Designing and managing dynamic obstacles with collision detection.
  • Building continuous parallax backgrounds including moving clouds and scrolling ground.
  • Tracking score and implementing progressive difficulty to enhance gameplay.
  • Handling keyboard input for player control.
  • Optimizing animation using requestAnimationFrame for smooth, efficient performance.
  • Understanding real-time rendering loops, event handling, and modular game architecture for maintainable code.

Abstract

This tutorial presents a comprehensive approach to building an Endless Runner game using TypeScript, HTML5, and CSS3. It now incorporates a humanoid player character with arms, legs, torso, and head, continuous cloud generation, and dynamic parallax backgrounds. Readers will explore object-oriented programming, physics simulation, real-time rendering loops, and collision detection through a fully functional browser game.

1. Introduction

Browser-based gaming has evolved with TypeScript and modern Web APIs, enabling high-performance interactive applications. The Endless Runner genre, where a character runs infinitely while avoiding obstacles, is ideal for beginners and covers:

  • Real-time animation (requestAnimationFrame)
  • Humanoid character animation
  • Collision detection with dynamic obstacles
  • Keyboard input handling (jump mechanics)
  • Continuous parallax scrolling with clouds and ground
  • Progressive difficulty and scoring

Learning Objectives

By the end of this tutorial, learners will:

  1. Set up a TypeScript game project.
  2. Apply OOP design to organize game logic.
  3. Implement humanoid character physics and running animation.
  4. Create dynamic obstacles and collision detection.
  5. Design continuous parallax backgrounds with clouds and ground.
  6. Track scores and increase game difficulty progressively.
  7. Deploy the game for browser execution using TypeScript, HTML, and CSS.

2. Project Setup

2.1 Folder Structure

endless-runner/
├── index.html
├── style.css
├── tsconfig.json
├── src/
│   ├── main.ts
│   ├── game.ts
│   ├── player.ts
│   ├── obstacle.ts
│   └── background.ts
└── dist/

2.2 TypeScript Configuration (tsconfig.json)

{
  "compilerOptions": {
    "target": "ES6",
    "module": "ES6",
    "outDir": "./dist",
    "strict": true
  },
  "include": ["src/**/*"]
}

3. HTML and CSS

3.1 HTML (index.html)

<canvas id="gameCanvas" width="900" height="450"></canvas>
<script type="module" src="dist/main.js"></script>

3.2 CSS (style.css)

body { margin: 0; overflow: hidden; background: linear-gradient(to bottom, #74ebd5, #ACB6E5); }
canvas { display: block; margin: 0 auto; }

4. Game Architecture

Class Responsibility
Game Manages game loop, updates, rendering, obstacles, and score
Player Humanoid character with arms, legs, torso, and head; jump physics
Obstacle Moving obstacles and collision detection
Background Continuous clouds and ground for parallax effect

5. Core Classes

5.1 Player (player.ts)

Humanoid character with running animation and jump physics.

export class Player {
  x: number; y: number; width = 30; height = 50;
  velocityY = 0; gravity = 0.8; jumpForce = -14;
  ground: number; runningStep = 0;

  constructor(x: number, y: number) {
    this.x = x; this.y = y; this.ground = y;
  }

  jump() { if (this.y === this.ground) this.velocityY = this.jumpForce; }

  update() {
    this.y += this.velocityY; this.velocityY += this.gravity;
    if (this.y > this.ground) this.y = this.ground;
    this.runningStep += 0.2;
  }

  draw(ctx: CanvasRenderingContext2D) {
    // torso
    ctx.fillStyle = '#3498db';
    ctx.fillRect(this.x, this.y - this.height, this.width, this.height);
    // legs
    ctx.fillStyle = '#2c3e50';
    ctx.fillRect(this.x - 5, this.y, 10, 20);
    ctx.fillRect(this.x + 15, this.y + Math.sin(this.runningStep)*5, 10, 20);
    // arms
    ctx.fillStyle = '#f1c27d';
    ctx.fillRect(this.x - 10, this.y - this.height + 10 + Math.sin(this.runningStep)*5, 10, 30);
    ctx.fillRect(this.x + this.width, this.y - this.height + 10 - Math.sin(this.runningStep)*5, 10, 30);
    // head
    ctx.beginPath();
    ctx.arc(this.x + this.width/2, this.y - this.height - 10 + Math.sin(this.runningStep*2)*2, 10, 0, Math.PI*2);
    ctx.fill();
  }

  collidesWith(obstacle: any): boolean {
    return this.x < obstacle.x + obstacle.width && this.x + this.width > obstacle.x &&
           this.y - this.height < obstacle.y && this.y > obstacle.y - obstacle.height;
  }
}

5.2 Obstacle (obstacle.ts)

export class Obstacle {
  constructor(public x: number, public y: number, public width: number, public height: number, public speed: number) {}

  update() { this.x -= this.speed; }
  draw(ctx: CanvasRenderingContext2D) { ctx.fillStyle = '#ff6b6b'; ctx.fillRect(this.x, this.y - this.height, this.width, this.height); }
}

5.3 Background with Clouds (background.ts)

export interface Cloud { x: number; y: number; radiusX: number; radiusY: number; }
export class Background {
  clouds: Cloud[] = [];
  groundOffset = 0;

  constructor(public ctx: CanvasRenderingContext2D, public speed: number) {
    for (let i=0;i<5;i++) this.clouds.push({x: Math.random()*900, y: 50+Math.random()*100, radiusX: 20+Math.random()*20, radiusY: 15+Math.random()*15});
  }

  update() {
    this.clouds.forEach(cloud => { cloud.x -= this.speed*0.3; if(cloud.x+cloud.radiusX*2<0) { cloud.x=900+cloud.radiusX*2; cloud.y=50+Math.random()*100; } });
    this.groundOffset -= this.speed;
  }

  draw() {
    // sky
    const gradient = this.ctx.createLinearGradient(0,0,0,450);
    gradient.addColorStop(0,'#74ebd5'); gradient.addColorStop(1,'#ACB6E5');
    this.ctx.fillStyle = gradient; this.ctx.fillRect(0,0,900,450);
    // clouds
    this.ctx.fillStyle='rgba(255,255,255,0.8)';
    this.clouds.forEach(cloud => { this.ctx.beginPath(); this.ctx.ellipse(cloud.x,cloud.y,cloud.radiusX,cloud.radiusY,0,0,Math.PI*2); this.ctx.ellipse(cloud.x+cloud.radiusX,cloud.y,cloud.radiusX+10,cloud.radiusY+5,0,0,Math.PI*2); this.ctx.fill(); });
    // ground
    this.ctx.fillStyle='#7ec850';
    this.ctx.fillRect(this.groundOffset%900, 450-40, 900, 40);
    this.ctx.fillRect((this.groundOffset%900)+900,450-40,900,40);
  }
}

5.4 Utility (utils.ts)

export function isColliding(a:any,b:any):boolean{
  return a.x < b.x+b.width && a.x+a.width > b.x && a.y-a.height < b.y && a.y > b.y-b.height;
}

6. Game Controller (game.ts)

import { Player } from './player.js';
import { Obstacle } from './obstacle.js';
import { Background } from './background.js';
import { isColliding } from './utils.js';

export class Game {
  player: Player;
  obstacles: Obstacle[]=[];
  background: Background;
  score=0;
  speed=4;
  gameOver=false;

  constructor(private ctx: CanvasRenderingContext2D){
    this.player=new Player(100,380);
    this.background=new Background(ctx,this.speed);
    this.spawnObstacle();
    this.listenForInput();
  }

  listenForInput(){
    window.addEventListener('keydown',e=>{if(e.code==='Space') this.player.jump();});
  }

  spawnObstacle(){
    setInterval(()=>{if(!this.gameOver) this.obstacles.push(new Obstacle(900,450-40,20,20+Math.random()*40,this.speed));},1500);
  }

  checkCollisions(){this.obstacles.forEach(o=>{if(this.player.collidesWith(o)) this.gameOver=true;});}

  update(){
    if(this.gameOver) return;
    this.background.update();
    this.player.update();
    this.obstacles.forEach(o=>o.update());
    this.obstacles=this.obstacles.filter(o=>o.x+o.width>0);
    this.checkCollisions();
    this.score++;
    if(this.score%5===0) this.speed+=0.5;
  }

  draw(){
    this.background.draw();
    this.player.draw(this.ctx);
    this.obstacles.forEach(o=>o.draw(this.ctx));
    this.ctx.fillStyle='#2d3436'; this.ctx.font='24px Arial';
    this.ctx.fillText('Score: '+this.score,20,40);
    if(this.gameOver){this.ctx.fillStyle='red'; this.ctx.font='40

7. Entry Point (main.ts)

import { Game } from './game.js';

const canvas = document.getElementById('gameCanvas') as HTMLCanvasElement;
const ctx = canvas.getContext('2d')!;

const game = new Game(ctx);

function gameLoop() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  game.update();
  game.draw();
  requestAnimationFrame(gameLoop);
}

gameLoop();
  • This entry point sets up the canvas and rendering context.
  • Initializes the Game class with the updated humanoid character, obstacles, and parallax background.
  • gameLoop uses requestAnimationFrame for smooth animation, clears the canvas each frame, updates game state, and renders all elements.

8. Physics, Optimization, and Reflection

  • Gravity: Simulates realistic jump and fall mechanics for the humanoid character.
  • AABB Collision Detection: Efficiently detects collisions between player and obstacles.
  • Dynamic Difficulty Adjustment: Increases obstacle speed progressively as the score increases.
  • Parallax Scrolling: Clouds and ground move at different speeds to enhance depth perception.
  • Performance Optimizations: Uses minimal per-frame allocations and requestAnimationFrame to maintain 60fps and reduce memory usage.

9. Conclusion

The Endless Runner game with a humanoid player, continuous cloud generation, and dynamic parallax backgrounds demonstrates TypeScript's strength for browser-based interactive applications. Beginners gain hands-on experience with modular OOP design, real-time physics, animation loops, collision detection, and progressive gameplay mechanics. This project serves as a robust foundation for building more complex browser games and reinforces practical software engineering principles.


This content originally appeared on DEV Community and was authored by Md Mahbubur Rahman


Print Share Comment Cite Upload Translate Updates
APA

Md Mahbubur Rahman | Sciencx (2025-10-06T05:22:20+00:00) Build a Browser-Based Endless Runner Game in TypeScript: Step-by-Step Guide for Beginners. Retrieved from https://www.scien.cx/2025/10/06/build-a-browser-based-endless-runner-game-in-typescript-step-by-step-guide-for-beginners/

MLA
" » Build a Browser-Based Endless Runner Game in TypeScript: Step-by-Step Guide for Beginners." Md Mahbubur Rahman | Sciencx - Monday October 6, 2025, https://www.scien.cx/2025/10/06/build-a-browser-based-endless-runner-game-in-typescript-step-by-step-guide-for-beginners/
HARVARD
Md Mahbubur Rahman | Sciencx Monday October 6, 2025 » Build a Browser-Based Endless Runner Game in TypeScript: Step-by-Step Guide for Beginners., viewed ,<https://www.scien.cx/2025/10/06/build-a-browser-based-endless-runner-game-in-typescript-step-by-step-guide-for-beginners/>
VANCOUVER
Md Mahbubur Rahman | Sciencx - » Build a Browser-Based Endless Runner Game in TypeScript: Step-by-Step Guide for Beginners. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2025/10/06/build-a-browser-based-endless-runner-game-in-typescript-step-by-step-guide-for-beginners/
CHICAGO
" » Build a Browser-Based Endless Runner Game in TypeScript: Step-by-Step Guide for Beginners." Md Mahbubur Rahman | Sciencx - Accessed . https://www.scien.cx/2025/10/06/build-a-browser-based-endless-runner-game-in-typescript-step-by-step-guide-for-beginners/
IEEE
" » Build a Browser-Based Endless Runner Game in TypeScript: Step-by-Step Guide for Beginners." Md Mahbubur Rahman | Sciencx [Online]. Available: https://www.scien.cx/2025/10/06/build-a-browser-based-endless-runner-game-in-typescript-step-by-step-guide-for-beginners/. [Accessed: ]
rf:citation
» Build a Browser-Based Endless Runner Game in TypeScript: Step-by-Step Guide for Beginners | Md Mahbubur Rahman | Sciencx | https://www.scien.cx/2025/10/06/build-a-browser-based-endless-runner-game-in-typescript-step-by-step-guide-for-beginners/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.