---
title: Phaser
url: 'http://wiki.teletypegames.org/development/phaser'
markdown: 'http://wiki.teletypegames.org/development/phaser.md'
lang: en
date: '2026-07-31'
description: 'Phaser is an open source HTML5 framework for 2D games, written in JavaScript/TypeScript. WebGL and Canvas rendering, arcade and matter.js physics, tweens, particles, many plugins. A Phaser game is a plain web page — nothing to compile — so its pipeline is the simplest of all our engines. Website: h…'
taxonomy:
  tag:
    - howto
---

# Phaser

Phaser is an open source HTML5 framework for 2D games, written in JavaScript/TypeScript. WebGL and Canvas rendering, arcade and matter.js physics, tweens, particles, many plugins. A Phaser game is a plain web page — nothing to compile — so its pipeline is the simplest of all our engines.

**Website:** https://phaser.io

# Tutorials

- [*Making your first Phaser 3 game*](https://phaser.io/tutorials/making-your-first-phaser-3-game)
- [*Phaser 3 Examples*](https://phaser.io/examples)
- [*Phaser 3 API Documentation*](https://newdocs.phaser.io/docs/3.90.0)
- [*Phaser by Example (free book)*](https://phaser.io/news/2024/04/phaser-by-example-book)
- [*HTML5 Game Development mini-degree articles (Zenva)*](https://gamedevacademy.org/category/tutorials/phaser-tutorials/)

---

# Makefile

No bundler, no `node_modules`. The pinned `phaser.min.js` and the HTML shell are downloaded at build time, and `src/*.js` is concatenated **in filename order** into `game.js` — prefix files with numbers when load order matters. The repo holds game code only.

Templates are in [phaser-tools](https://git.teletypegames.org/build/phaser-tools): copy `example-makefile.make` to `Makefile`, set `PROJECT` and `PHASER_VERSION`.

## Variables

| Variable | Description | Default |
|---|---|---|
| `PROJECT` | Name used in output filenames | `phaserdemo` |
| `PHASER_VERSION` | Phaser release from jsDelivr | `3.90.0` |
| `SRC_DIR` | Game sources | `src` |
| `DIST_DIR` | Exported artifacts | `dist` |
| `WEB_DIR` | Assembled web bundle | `dist/web` |
| `VERSION_FILE` | Version file for CI | `.version` |
| `INDEX_HTML_URL` | HTML shell | phaser-tools `web/index.html` |

## Targets

```sh
make build                   # node --check on every file in src/
make web                     # download phaser.min.js + index.html, concat src/*.js
make serve                   # web bundle on http://localhost:8000 (python3 http.server)
make export VERSION=1.0.0     # dist/web/ zipped as phaserdemo-1.0.0.html.zip
make watch                   # re-check on change under src/ (needs fswatch)
make clean                   # remove dist/
```

## VS Code

Copy `example-tasks.json` to `.vscode/tasks.json`. Tasks: **Serve** (default, `Cmd+Shift+B`), **Build Web**, **Make build**.

## Generated artifacts

| File | Description |
|---|---|
| `dist/web/` | Web bundle (index.html, phaser.min.js, game.js) |
| `phaserdemo-<version>.html.zip` | Web build |

## Requirements

Local only: `make`, `git`, `jq`, `zip`, `curl`, `node`, `python3` for `make serve`, and `fswatch` for watch mode. CI images bring their own tools.

---

# Pipeline

The pipeline is not in the game repo. Woodpecker asks the update server, and WarpEngine serves the whole config from `/build/config` (template `lib/warp_engine/ci_templates/phaser.yaml.erb`).

The repo needs three things:

```yaml
# .woodpecker.yaml
platform: phaser
name: phaserdemo   # only when the software name differs from the repo name
```

- `metadata.json`
- the `application_token` Woodpecker secret: a WarpEngine ApplicationToken with `update` + `upload` scopes

Builder images come from the update server's `ci_platforms` config. Preview the served pipeline:

```sh
curl "https://teletypegames.org/build/config?platform=phaser"
```

## Steps

| Step | What it does |
|---|---|
| 1. version | on alpine: version from `metadata.json`, else git |
| 2. build | in the `phaser-builder` image (Node + make/zip/curl, from `builder.Dockerfile` in phaser-tools): syntax check, download the runtime and shell, assemble and zip |
| 3. upload | `POST /build/upload`, `X-Update-Secret` header |
| 4. publish | `POST /build/publish?platform=phaser` |

## Demo project

[phaser-demo](https://git.teletypegames.org/demos/phaser-demo) — animated "TELETYPE GAMES" wave text on Phaser 3.90, the same demo as [ebitengine-demo](http://wiki.teletypegames.org/development/ebitengine).

'; var prevLevel = 2; for (var i = 0; i < headings.length; i++) { var h = headings\[i\]; var text = h.textContent.trim(); var slug = text.toLowerCase() .replace(/\[áàâä\]/g,'a').replace(/\[éèêë\]/g,'e').replace(/\[íìîï\]/g,'i') .replace(/\[óòôöő\]/g,'o').replace(/\[úùûüű\]/g,'u') .replace(/\[^a-z0-9\]+/g, '-').replace(/^-|-$/g, ''); if (!slug) slug = 'heading-' + i; if (used\[slug\]) { slug += '-' + i; } used\[slug\] = true; h.id = slug; var level = parseInt(h.tagName.charAt(1)); var cls = level > 2 ? ' class="toc-child"' : ''; html += '- [' + text + '](#'%20+%20slug%20+%20')
'; } html += '
'; toc.innerHTML = html; // Scroll-spy: highlight current heading var links = toc.querySelectorAll('a'); var ids = \[\]; for (var j = 0; j < links.length; j++) ids.push(links\[j\].getAttribute('href').slice(1)); function updateActive() { var current = ''; for (var k = 0; k < ids.length; k++) { var el = document.getElementById(ids\[k\]); if (el && el.getBoundingClientRect().top <= 80) current = ids\[k\]; } for (var m = 0; m < links.length; m++) { links\[m\].parentNode.classList.toggle('active', links\[m\].getAttribute('href') === '#' + current); } } window.addEventListener('scroll', updateActive, { passive: true }); updateActive(); })();

---

## Navigation

- Parent: [Development](http://wiki.teletypegames.org/development.md)
- Previous: [Löve](http://wiki.teletypegames.org/development/love.md)
- Next: [Pixelorama](http://wiki.teletypegames.org/development/pixelorama.md)
