How to Build a Simple Game in the Browser with Phaser 3 and TypeScript
Browser-based games have come a long way in recent years. Frameworks like Phaser make it easier than ever for developers to create engaging web games without needing to be graphics programming experts. Combining Phaser with TypeScript allows you to leverage the power of static typing to write cleaner and more maintainable game code.
In this in-depth guide, we‘ll walk through building a complete game in Phaser 3 and TypeScript from start to finish. Whether you‘re new to browser game development or looking to level up your skills, this post will equip you with the knowledge you need to start making games.
Why Phaser and TypeScript?
Phaser is a well-established and popular open source framework for making HTML5 games in JavaScript. Created by Richard Davey of Photon Storm, Phaser streamlines 2D game development by providing a robust suite of tools and APIs for graphics, physics, input, sound, and more.
Compared to lower-level graphics APIs like WebGL and Canvas, Phaser is much easier to get started with as a web developer. It provides sensible defaults and abstracts away many of the details of cross-browser support and performance optimization. At the same time, it‘s extremely flexible and not overly opinionated, giving you full control over your game.
Phaser 3, released in 2018, was a major update that modernized the framework and added powerful new features. It introduced a scene-based architecture, enhanced physics systems, a unified loader, and more. Phaser games can be compiled to native desktop and mobile apps, opening up even more possibilities.
While Phaser is written in plain JavaScript, the Phaser community maintains TypeScript definitions that allow the framework to be used seamlessly from TypeScript. TypeScript is a statically typed superset of JavaScript from Microsoft that compiles to plain JavaScript.
For game programming, using TypeScript provides some notable advantages:
- The strong typing catches bugs at compile-time before they cause runtime errors
- Code editors provide richer autocompletion, refactoring, and inline documentation
- Defining interfaces for game objects and config makes the codebase more understandable
- ES6+ features like classes, imports, and async/await are supported
TypeScript has seen rapid adoption by the JavaScript community in recent years. Frameworks like Angular use it as a first-class language. Many developers find the productivity benefits far outweigh the initial learning curve.
By combining Phaser with TypeScript, we get a powerful yet accessible technology stack for game development that is fun to use and allows us to write high-quality code. So let‘s dive in and start building!
Setting Up a Phaser/TypeScript Project
Before we start coding, we need to configure our project and install some dependencies. I recommend using Visual Studio Code as your code editor since it has excellent built-in TypeScript support, but any editor will work.
First, create a new directory for the project and initialize it as an npm package:
mkdir phaser3-typescript-game
cd phaser3-typescript-game
npm init -y
Next, install Phaser 3 along with some development dependencies:
npm install phaser
npm install --save-dev typescript webpack webpack-cli ts-loader webpack-dev-server
Here‘s what each of these dependencies does:
phaser
is obviously the game framework itselftypescript
is the TypeScript compilerwebpack
is a module bundler that will compile our TS code and assets into a browser-friendly buildts-loader
allows Webpack to transpile TypeScriptwebpack-dev-server
provides a local development server with live reloading
With the dependencies in place, let‘s add some configuration files. Create a tsconfig.json
file in the root to configure the TS compiler:
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"sourceMap": true
},
"include": [
"src/**/*"
]
}
And add a webpack.config.js
to configure the build process:
const path = require(‘path‘);
module.exports = {
entry: ‘./src/index.ts‘,
module: {
rules: [
{
test: /\.tsx?$/,
use: ‘ts-loader‘,
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ ‘.ts‘, ‘.tsx‘, ‘.js‘ ]
},
output: {
filename: ‘game.js‘,
path: path.resolve(__dirname, ‘dist‘)
},
mode: ‘development‘
};
This config tells Webpack to compile the src/index.ts
file and all its dependencies into a dist/game.js
bundle. It also configures TypeScript compilation and sourcemaps.
Finally, add some NPM scripts to your package.json
for ease of development:
"scripts": {
"start": "webpack serve --config webpack.config.js",
"build": "webpack --config webpack.config.js"
}
We now have a complete project setup that‘s ready for game development! Run npm start
and Webpack will launch a dev server at http://localhost:8080/
.
Creating the Game
Time for the fun part – actually coding our game! We‘ll be making a simple space-themed endless runner where the player tries to survive as long as possible while dodging oncoming asteroids.
Game Configuration
Let‘s start by creating a src/index.ts
file that will serve as our game‘s entry point. Here we‘ll configure Phaser with some basic settings:
import ‘phaser‘;
const config: Phaser.Types.Core.GameConfig = {
type: Phaser.AUTO,
width: 800,
height: 600,
physics: {
default: ‘arcade‘,
arcade: {
gravity: { y: 200 }
}
},
scene: {
preload: preload,
create: create,
update: update
}
};
new Phaser.Game(config);
This sets up a basic 800×600 Phaser game using Arcade physics. The scene
property defines the game loop methods. These can go in the same file for simple games:
function preload() {
// Load assets
}
function create() {
// Initialize game objects
}
function update() {
// Main game loop
}
Adding the Player
Next, let‘s create a Player
class to represent our spaceship character. Make a new file src/player.ts
:
export default class Player extends Phaser.Physics.Arcade.Sprite {
constructor(scene: Phaser.Scene, x: number, y: number) {
super(scene, x, y, ‘player‘);
scene.add.existing(this);
scene.physics.add.existing(this);
this.setCollideWorldBounds(true);
}
moveLeft() {
this.setVelocityX(-200);
}
moveRight() {
this.setVelocityX(200);
}
}
Our player builds on the Arcade physics Sprite
class. It has basic left/right movement methods. We position it in the middle of the screen and make sure it can‘t move outside the game area.
In preload
, load the player‘s image asset:
function preload() {
this.load.image(‘player‘, ‘assets/player.png‘);
}
And instantiate it in create
:
let player: Player;
function create() {
player = new Player(this, 400, 500);
}
Handling Input
To let the player control the ship, we need to wire up Phaser‘s input events. Modify create
to set up the cursor key bindings:
function create() {
// ...
const cursors = this.input.keyboard.createCursorKeys();
this.input.keyboard.on(‘keydown-LEFT‘, () => {
player.moveLeft();
});
this.input.keyboard.on(‘keydown-RIGHT‘, () => {
player.moveRight();
});
}
Spawning Obstacles
Now we need something for the player to dodge! Let‘s create an Obstacle
class similar to the Player
:
export default class Obstacle extends Phaser.Physics.Arcade.Sprite {
constructor(scene: Phaser.Scene, x: number, y: number) {
super(scene, x, y, ‘obstacle‘);
scene.add.existing(this);
scene.physics.add.existing(this);
this.setVelocityY(100);
}
}
Obstacles will spawn at the top of the screen and fall downwards. We‘ll generate them on a timer in the update
loop:
const obstacleSpawnRate = 3000; // spawn every 3 seconds
let lastObstacleTime = 0;
function update(time: number) {
const elapsedTime = time - lastObstacleTime;
if (elapsedTime > obstacleSpawnRate) {
lastObstacleTime = time;
const x = Phaser.Math.Between(50, 750);
const y = 0;
const obstacle = new Obstacle(this, x, y);
}
}
Finally, add collision detection between the obstacles and player:
function create() {
// ...
this.physics.add.overlap(player, obstacles, handleObstacleCollision, null, this);
}
function handleObstacleCollision() {
this.physics.pause();
player.setTint(0xff0000);
gameOver = true;
}
If the player touches an obstacle, the game ends.
Displaying Score
As a final touch, let‘s display the player‘s survival time as a score. Create a text object in create
:
let scoreText: Phaser.GameObjects.Text;
function create() {
// ...
scoreText = this.add.text(16, 16, ‘score: 0‘, {
fontSize: ‘32px‘,
fill: ‘#fff‘
});
}
And update it in the update
loop:
function update(time: number) {
if (!gameOver) {
const seconds = Math.floor(time / 1000);
scoreText.setText(`score: ${seconds}`);
}
// ...
}
Next Steps
Congratulations, you‘ve just built a complete browser game with Phaser and TypeScript! There are countless ways you could expand on it:
- Add different obstacle types and spawn patterns to increase difficulty over time
- Implement power-ups and special abilities to help the player
- Display a Game Over screen and let the player restart
- Include sound effects and background music
- Make the game responsive for different screen sizes
The skills you‘ve learned can be applied to create any kind of 2D action or arcade-style game. To learn more, I recommend these resources:
- The official Phaser documentation and examples
- Emanuele Feronato‘s Phaser tutorials
- The Phaser 3 Game Development book by Adam Lynch
- HTML5 Game Development with Phaser and TypeScript Video Course
While Phaser does simplify the process, developing browser games is still a cross-disciplinary challenge spanning design, programming, game logic, performance, and more. Persistence is key – start small, prototype quickly, and don‘t be afraid to experiment. With time and practice, you can use these tools to build increasingly sophisticated games and interactive experiences. Happy coding!