# 2D platformer in the browser: build brief

Assembled by AgentGid's starter kit builder from recipe `2d-platformer` v0.3 (https://agentgid.com/build/kits/2d-platformer/). Data snapshot: 2026-10-12.
Constraints: commercial use; free parts only; crediting authors is fine.
Recipe status: test-built on 2026-10-12 (2 builds) with exactly these parts. The fixes from the last build are in this version, which has not been built again as a whole.
Licenses and prices change. Before you ship, open the linked AgentGid pages and confirm.

## What to build

One playable level in a desktop browser: the player runs and jumps across platforms, collects coins, avoids an enemy, and reaches a flag. A title screen, a win screen, sound effects, and a static build you can upload.

## Definition of done

- [ ] `npm run dev` serves the game, and `npm run build` writes a static `dist/` that also works when it is served from a sub-folder.
- [ ] A title screen that starts on a key press and on a click or tap, one level, and a win screen.
- [ ] The player runs, jumps with variable height, lands on platforms, and the camera follows.
- [ ] Coins can be collected and the HUD shows the count.
- [ ] One enemy type: touching it from the side costs one of 3 hearts, landing on it removes it. A fall or the last heart restarts the level with coins, enemies and hearts reset.
- [ ] Reaching the flag ends the level and shows the win screen.
- [ ] Jump, coin, hurt and win each call a sound, counted in the test hook.
- [ ] An automated playthrough starts on the title screen, takes one hit and one fall on purpose, reaches the win screen within 60 seconds, and logs no console errors or warnings from the game.
- [ ] `CREDITS.md` has a row for every third-party file in `dist/`, and `dist/licenses.md` holds the license text of the bundled engine.
- [ ] No Nintendo names, characters, sprites, sounds or level layouts in what you make: source, assets and on-screen text.

Left for the person, because no check can do it:

- Listen to the four sounds once: a check can count play calls, not judge how they sound.
- Upload the build: it needs your account on the host.

## Before you start

These files are already in this folder. Keep them, and never let a scaffolder overwrite them:

- `AGENTS.md`: this brief.
- `CLAUDE.md`: one line that imports AGENTS.md for Claude Code.
- `.mcp.json`: the MCP servers listed under "Tools you have".
- `CREDITS.md`: the license record. Add to it as you add files.
- `kit.json`: this kit as data (parts, versions, checks). `install.txt`: the install commands below. `first-prompt.txt`: the message the person starts you with.

You may do these without asking:

- Install the packages in the Stack section.
- Download the files linked under Assets, from the links given.
- Read the documentation this brief links to.
- Use these without asking, because they do not ship: Node's built-in `http` module or `python3 -m http.server` as a static server, `zip`, and the test runner in the Stack. Its browser download (`npx playwright install chromium`) is allowed too.

Stop and ask the person for:

- an account, a key or a payment;
- a runtime dependency, asset source or API that this brief does not list;
- a check that still fails after two real fixes (say what you tried).

## Stack

Use these parts and versions. Ask before swapping one.

| Part | Choice | Version | Kind |
|---|---|---|---|
| Game engine | Phaser | 4.2.1 | dependency |
| Physics | Arcade Physics | - | built in |
| Dev server and bundler | Vite | 8.3.4 | dev dependency |
| Language | TypeScript | 7.0.2 | dev dependency |
| Level format | Levels as JSON in the repo | - | - |
| Scripted browser tests | Playwright (JavaScript) | 1.64.0 | dev dependency |

Install exactly these versions (the same lines are in `install.txt`):

```sh
npm install --save-exact phaser@4.2.1
npm install -D --save-exact vite@8.3.4 typescript@7.0.2 @playwright/test@1.64.0
```

## Rules for this project

- Import the engine with `import Phaser from 'phaser'`. Its types load through the package itself: do not add `typeRoots` or change `types` in tsconfig.json. (checked with tsc, Oct 12, 2026)
- Write loader paths as `assets/...` with no leading slash. Vite's docs recommend `/assets/...` for files in `public/`; here that breaks as soon as the game is served from a sub-folder. (test build)
- Read the official skill for a subsystem before you write code for it. The plan names the skill for each step; the rest are reference. (Phaser README)
- Everything in `public/` is copied into `dist/`. Keep only files that have a row in CREDITS.md there.
- Keep the checks as plain Node scripts in `tests/`, one per plan step, so that every one of them can be run again after a change.

### Phaser, from its AgentGid page

- When porting Phaser 3 code, use the `v3-to-v4-migration` skill and the Migration Guide. Do not use the removed classes `Point`, `Mesh` and `BitmapMask`. FX and Masks are now Filters.
- Serve the game over `http://` from a local web server during development. Do not open the HTML via `file://`.
- More: https://agentgid.com/tools/phaser/

### Vite, from its AgentGid page

- Use the standard scripts: `vite` for dev, `vite build` for production, and `vite preview` to check the build locally.
- More: https://agentgid.com/tools/vite/

### Docs for lookups

Not required reading. Each plan step names what to read first.

- Phaser agent skills (Phaser Studio Inc): Nothing to install: after `npm install phaser` they are in node_modules/phaser/skills/

## Assets and licenses

Add a row to CREDITS.md for every file you bring into the project, at the moment you add it.

### Sprites and tiles and Sound effects: Kenney, New Platformer Pack

- Download: https://kenney.nl/media/pages/assets/new-platformer-pack/1896103897-1764756702/kenney_new-platformer-pack-1.1.zip (pack page: https://kenney.nl/assets/new-platformer-pack). The link is direct: download it yourself.
- Copy into the project only: Spritesheets/spritesheet-tiles-default.png + .xml; Spritesheets/spritesheet-characters-default.png + .xml; Spritesheets/spritesheet-enemies-default.png + .xml; Spritesheets/spritesheet-backgrounds-default.png + .xml; Sounds/sfx_jump.ogg, sfx_coin.ogg, sfx_hurt.ogg, sfx_bump.ogg, sfx_select.ogg, sfx_magic.ogg, sfx_gem.ogg; License.txt
- 1,353 entries in the ZIP (879 PNG, 433 SVG, 10 OGG, 8 XML). Spritesheets/ holds 4 atlases in two sizes with Starling-style XML: tiles (314 frames, 64x64 px), characters (45 frames, 128x128), enemies (60 frames, 64x64), backgrounds (14 frames, 256x256).
- Frames have names, so you can place them without opening the image: character_green_idle, character_green_walk_a, character_green_walk_b, character_green_jump, character_green_hit; slime_normal_walk_a, slime_normal_walk_b, slime_normal_flat; coin_gold; flag_green_a, flag_green_b. Characters also come in beige, pink, purple and yellow.
- Ground: use terrain_grass_block_top_left, _top, _top_right for the top row and terrain_grass_block_left, _center, _right below it. terrain_grass_block on its own is a single boxed tile: a row of it looks like separate boxes. One-tile-high rows: terrain_grass_horizontal_left, _middle, _right. Floating platforms: terrain_grass_cloud_left, _middle, _right (the art is 52 px tall). The same names exist for dirt, purple, sand, snow and stone.
- Characters face right. Every enemy faces left: flip it when it moves right.
- Opaque area inside the frame: the character is about 82x97 px at the bottom of its 128 px frame; slime_normal_walk is about 56x42 px at the bottom of its 64 px frame; coin_gold is about 38x40 px, centred; a flag fills its 64 px tile, so it is shorter than the character. Size physics bodies from these numbers, not from the frame.
- Backgrounds are 14 opaque 256 px bands, not transparent layers. Stack them from top to bottom: sky colour #c3e3ff (background_solid_sky), then background_clouds (sky at its top, white at its bottom), then background_fade_hills, _trees, _desert or _mushrooms (white at the top, pale shapes, sky colour at the bottom) or the background_color_ variant, which ends in the ground colour.
- HUD frames in the tiles atlas: hud_heart, hud_heart_empty, hud_heart_half, hud_coin, and digits hud_character_0 to hud_character_9 with hud_character_multiply. Draw numbers with the digit frames. The digits are white with a pale grey outline, so they vanish on the pack's own sky and clouds: put the HUD on a dark panel.
- Sounds are OGG only. By event: jump sfx_jump, coin sfx_coin, hurt sfx_hurt, landing on an enemy sfx_bump, menu sfx_select. The pack has no fanfare: for win use sfx_magic, or sfx_gem a few times at rising pitch.
- License.txt: Creative Commons Zero; 'You can use this content for personal, educational, and commercial purposes'; crediting Kenney 'is not a requirement'.
- License of this item: CC0.
- Source terms: Commercial use OK; No credit needed; Can ship inside your build.
- AgentGid's rules for Kenney: Do not use the Kenney logo.
- License page: https://kenney.nl/support (AgentGid read it on Oct 10, 2026). Details: https://agentgid.com/assets/kenney/

### Font: Google Fonts, Lilita One

- Font file: https://raw.githubusercontent.com/google/fonts/main/ofl/lilitaone/LilitaOne-Regular.ttf (27 KB)
- License text to keep next to it: https://raw.githubusercontent.com/google/fonts/main/ofl/lilitaone/OFL.txt
- Specimen: https://fonts.google.com/specimen/Lilita+One
- Designer: Juan Montoreano. License of this font: OFL-1.1.
- Source terms: Commercial use OK; No credit needed; Can ship inside your build, with conditions.
- AgentGid's rules for Google Fonts: Keep the license file with font files you bundle in an app.
- License page: https://github.com/google/fonts (AgentGid read it on Oct 10, 2026). Details: https://agentgid.com/assets/google-fonts/

## Tools you have

These MCP servers are in the config file listed under "Before you start".

- Playwright MCP: Lets the agent open the app in a browser, click, and take screenshots
- AgentGid: Live data for choosing tools, assets and APIs while building

If a server's tools are missing from your session, either the person has not approved them yet (Claude Code holds the servers of a project `.mcp.json` as "Pending approval" until the person accepts them), or the config was added after the session started. Ask the person to approve the servers or restart the agent in this folder; until then, use the links in this brief.

## Ship

- Where to publish: itch.io (HTML5 game). Upload a ZIP with index.html and the game runs on your itch.io page. Free: 'itch.io costs nothing to use'.
- Limits: ZIP: at most 1,000 files after extraction, 500 MB in total, 200 MB per file, paths up to 240 characters, case-sensitive names.
- HTML5 games can take payments only as donations.
- The game is served from a sub-folder: every path must be relative.
- Package: zip the contents of `dist/`, not the folder, so that `index.html` is at the top level, and delete an older ZIP first, because `zip` adds to an existing archive: `rm -f game.zip && (cd dist && zip -r ../game.zip .)`
- Uploading needs the person's itch.io account. Hand over the ZIP with these settings in itch.io's own words: pick "HTML Game" from the Kind Of Game list, upload the ZIP, choose "Embed in page" with a 1280 x 720 viewport, and turn on the "Fullscreen Button".
- Details: https://itch.io/docs/creators/html5

## Plan

Work one step at a time. Do not start a step until the previous one meets its "Done when" lines.

### 1. Scaffold

- This folder already holds the kit files, and `create-vite` refuses a folder that is not empty: it prints `Operation cancelled` and still exits with 0. Scaffold next to them and copy the result in: `npm create vite@latest .scaffold -- --template vanilla-ts --no-interactive && cp -a .scaffold/. . && rm -r .scaffold`. Never pass `--overwrite`: it deletes AGENTS.md. Check that `package.json` exists afterwards.
- Set `name` in package.json and `<title>` in index.html: both say `scaffold`.
- Run the install lines of the Stack section. The template pins older versions; the install replaces them.
- Delete the template's demo: `src/counter.*`, `src/style.css`, `src/assets/`, `public/favicon.svg` and `public/icons.svg` (a sheet of brand logos). Rewrite the main file. In index.html replace the favicon link with `<link rel="icon" href="data:,">`.
- Create `vite.config.js` with `export default defineConfig({ base: './', build: { license: { fileName: 'licenses.md' }, chunkSizeWarningLimit: 1600 } })` (import `defineConfig` from 'vite'). Without `base: './'` the build only works at the root of a domain. `license` writes the engine's license text into `dist/`. The limit silences Vite's 500 kB warning, which the engine alone exceeds: do not split the engine.
- Leave tsconfig.json as the template wrote it. It forbids `enum`, constructor parameter properties (`constructor(private x: T)`) and namespaces (error TS1294) and wants `import type` for types: use string unions and explicit fields.
- Game config: 1280x720, `scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH }`, Arcade physics, and `parent: 'game'`. In index.html, a `<div id="game">` with `html, body, #game { height: 100%; margin: 0 }`.
- Read first: `node_modules/phaser/skills/game-setup-and-config/SKILL.md`; `node_modules/phaser/skills/scenes/SKILL.md`; `node_modules/phaser/skills/scale-and-responsive/SKILL.md`
- Done when:
  - The dev server shows an empty scene at 1280x720 that scales with the window, and the console has no errors or warnings.
  - `npx tsc` passes.
  - `npm run build` passes and `dist/index.html` loads its script from `./assets/`.
  - The kit files listed under "Before you start" are still there.

### 2. Assets

- Download every pack and file listed under Assets yourself: the links are direct. Unzip packs outside the project and copy only the files named there into `public/assets/<pack-name>/`, keeping the pack's own sub-folders and its License.txt. Use folder names without spaces.
- CREDITS.md already has a row for each of those files. Correct it if you copy more or fewer.
- Load everything in a Preload scene with a progress bar. Load the font there too: `this.load.font('Lilita One', 'assets/<folder>/LilitaOne-Regular.ttf', 'truetype')`, then use that family name in `this.add.text`.
- Read first: `node_modules/phaser/skills/loading-assets/SKILL.md`; `node_modules/phaser/skills/audio-and-sound/SKILL.md`
- Done when:
  - A temporary test scene draws one tile, one character frame and one enemy frame, and plays one sound after a key press.
  - `[...document.fonts].map(f => f.family + ':' + f.status)` lists the font as loaded, and a screenshot shows text set in it.

### 3. Level

- Describe the level as data in `src/levels/level1.json` and build it in code. Use tiles as the unit, x from the left and y counted up from the ground row: ground and platforms as rectangles or runs of tiles, plus spawn points for coins, enemies, the player and the flag. Document the format where the loader lives.
- Make the level about four screens wide and set world and camera bounds to match. Keep the bottom of the world open, or the pits get a floor: `this.physics.world.setBounds(0, 0, width, height, true, true, false, false)`.
- Background: make each TileSprite the size of the screen with `setScrollFactor(0)` and move its `tilePositionX` with `camera.scrollX` (times 0.1 to 0.3 for parallax). Round `tilePositionX` to whole pixels: with a fractional offset WebGL draws a one-pixel seam at every repeat, and the Canvas renderer does not show it.
- Expose the test hook now: `window.__game = { game, state, player, coins, coinsCollected, coinsTotal, enemies, flag, hearts, camera, sounds }`, with `state` one of `'title'`, `'playing'`, `'won'`. Publish it in dev builds and, in the production build, when the URL contains `?test`. Later steps and the final playthrough read it.
- Read first: `node_modules/phaser/skills/physics-arcade/SKILL.md`; `node_modules/phaser/skills/cameras/SKILL.md`; node_modules/phaser/skills/sprites-and-images/SKILL.md (TileSprite, for a repeating background)
- Done when:
  - Screenshots with the camera at the left end and at the right end show ground, platforms and a background.
  - The same two screenshots taken with WebGL (not the Canvas renderer) show no thin vertical lines in the background.
  - A normal visit to the built game has no `window.__game`; with `?test` it has one.

### 4. Player

- Move with arrow keys and A/D, jump with Space, W or Up.
- Create the keys in a small scene that is launched once and never restarts, and read them from the level scene: `scene.restart()` destroys a scene's Key objects, and a key held across a death then reads as up.
- Take the jump press from the key's event (`key.on('down', ...)` sets a flag that the next frame uses), not from `isDown` or `JustDown` in `update`: a press that starts and ends between two frames is invisible to polling.
- Make the jump feel right: cut upward speed when the key is released early, allow a jump for about 0.1 s after walking off a ledge, and remember a jump press for about 0.1 s before landing. Refill the ledge timer only while the player stands on the ground and is not moving up.
- "On the ground" means `body.blocked.down`, not `touching.down`, which every overlap sets.
- Switch between idle, walk and jump frames, and flip the sprite to face the way it moves.
- Size the physics body from the opaque part of the frame, not from the frame.
- Read first: `node_modules/phaser/skills/input-keyboard-mouse-touch/SKILL.md`; `node_modules/phaser/skills/animations/SKILL.md`
- Done when:
  - A scripted test holds Right until x has grown by 300 px (fail after 5 s), then presses jump: y rises and returns to the ground.
  - A tap of the jump key (give it a hold time in the script, for example 50 ms) peaks lower than a held press, and a zero-length press still jumps.
  - A screenshot with physics debug drawing on shows the body outline on the character, not around the whole frame.

### 5. Gameplay

- Coins with an overlap check and a HUD counter. Put the HUD on a dark translucent panel.
- Give coins a rectangular static body (`setSize`), not `setCircle`: for a static circle Arcade sets `blocked.down` on the player even in an overlap check, and the player can then jump off a coin in mid-air.
- One patrolling enemy type, placed a few times, with patrol ranges that keep clear of the spots where a player lands after a pit. Touching it from the side costs a heart, knocks the player back and gives about 1 s of invulnerability; without that, the overlap fires on every physics step and one touch takes all hearts. Landing on it removes it and bounces the player.
- 3 hearts. A fall below the level, or the last heart, restarts the level from the start with coins, enemies and hearts reset. There is no game-over screen.
- Make the goal a trigger from the floor to the top of the screen at the flag's x, not only the flag sprite: a full jump clears the sprite.
- On static bodies, call `refreshBody()` after `setOrigin`, `setScale` or `setPosition`.
- Read first: `node_modules/phaser/skills/groups-and-containers/SKILL.md`; `node_modules/phaser/skills/tweens/SKILL.md`; node_modules/phaser/skills/text-and-bitmaptext/SKILL.md (ignore its line that Phaser cannot load web fonts)
- Done when:
  - Each rule has its own check, and a check may place the player through the hook: a coin overlap raises the counter and the HUD shows it; falling onto a coin does not allow a jump in mid-air; side contact lowers hearts by one and does not repeat for about a second; landing on the enemy removes it; a fall restarts the level with coins and hearts reset; the last heart does the same; a key held across a restart still moves the player; touching the goal sets state to `won`.
  - A debug screenshot shows every coin's and enemy's body on its art.
  - A screenshot shows the HUD count readable over the sky and over the clouds.

### 6. Sound and screens

- Map the events jump, coin, hurt and win to sound files, and count every play call per event in `window.__game.sounds`.
- Route every play call through one helper that first checks `this.cache.audio.exists(key)`. The sounds are OGG only: a browser that cannot decode OGG should get a silent game, not an exception.
- Add a title scene and a win scene. Start on Space, Enter, a click or a tap: inside an embedded frame the game has no keyboard focus until it is clicked. On the win screen ignore input for the first half second: Space is also the jump key.
- Read first: `node_modules/phaser/skills/audio-and-sound/SKILL.md`
- Done when:
  - Starting from the title screen, a run reaches the win screen.
  - After the first key press `game.sound.locked` is false, and the four counters in `window.__game.sounds` rise during a run.

### 7. Verify and ship

- Write the playthrough as code that runs inside the page and decides every frame (in Phaser: `game.events.on('poststep', fn)`), reading `window.__game` and the level JSON. Fixed key timings will not finish a level, and neither will playing by alternating tool calls. Use one real key press for Start, so that audio unlocks. Let the run take one hit and one fall on purpose, and send the movement keys again after the restart.
- Run `npm run build` and serve the build from a sub-folder, not from the root, or the check proves nothing: copy `dist/` to `<tmp>/game/`, run `python3 -m http.server 8080 --directory <tmp>`, and open `http://localhost:8080/game/?test`. Run the playthrough there.
- List `dist/`: every file must be the page, the script, `licenses.md`, or a file with a row in CREDITS.md.
- Count the files in `dist/` (itch.io accepts at most 1,000) and package the build as the Ship section says.
- Done when:
  - The playthrough log shows jumps, coins, hits, deaths, wins, the sound counters, the final state, and zero console errors and warnings.
  - Every line under Definition of done is true.

## How to check your work

- Start the dev server in the background with `npm run dev -- --strictPort` and read the URL from its output. If the port is taken, pass another one with `--port`; do not stop a server you did not start.
- After each change: take a screenshot, read `window.__game` and read the console. Treat console errors and warnings from the game as a failed step. Messages from the browser's own graphics driver are not the game's.
- On a machine without a GPU (CI, cloud containers) WebGL runs in slow motion, and timed checks fail through no fault of the game. For timed checks, set `window.FORCE_CANVAS = true` in an init script before the page loads and use the Canvas renderer.
- Players get WebGL, so look at WebGL too: take the title screen, a few camera positions and the win screen with WebGL and look for drawing faults. The Canvas renderer hides some of them.
- Write checks as states with a time limit ("until x > 700, fail after 5 s"), not as fixed sleeps. After editing a file, wait for the dev server's reload before the next check.
- Once for every step that adds physics bodies, turn on debug drawing (`physics: { arcade: { debug: true } }`, behind `?debug`) and compare each outline with its art.
- When a screenshot looks wrong, say what you expected to see before you change code.

## Known traps

- Phaser 4 is not Phaser 3. The classes Point, Mesh and BitmapMask are gone, and FX and masks became filters. (AgentGid: Phaser)
- Do not start the project with `npm create @phaserjs/game@latest`: it only works at an interactive terminal, and its template pins older versions than this kit. (checked by running it, Oct 12, 2026)
- Kenney's atlases use Starling-style XML: load them with `this.load.atlasXML(key, png, xml)`, then refer to frames by name. (Phaser skill: loading-assets)
- A font whose path has a space, in the file name or in a folder, fails to load with a warning only, and the game silently uses a system font. `document.fonts.check()` still answers true, so look at a screenshot. (Phaser source and test build)
- Two Phaser skills disagree about fonts. The text skill says Phaser cannot load web fonts; the loading-assets skill documents `this.load.font`, and that loader exists in 4.2.1. Use it. (Phaser skills, checked Oct 12, 2026)
- Browsers block sound until the first key press or click. Phaser unlocks audio by itself after that, so start the game from a title screen and do not add your own workaround. (Phaser skill: audio-and-sound)
- A game with no sound at all has a clean console. Count the play calls, or nothing checks the audio. (test build)
- itch.io accepts at most 1,000 files in a ZIP, and this pack alone has 1,353 entries: ship the atlases and sounds you use, not the whole pack. (itch.io docs)
- The physics skill's first example calls `setCollideWorldBounds(true)`. With world bounds equal to the level, that turns the bottom of every pit into a floor. (test build)
- The engine's own bundle contains strings such as `SNES_USB` (its gamepad tables). A search of `dist/` for console brand names finds them: they are not yours to remove. (test build)

## Warnings you can ignore

- Without `build.chunkSizeWarningLimit`, every build prints "(!) Some chunks are larger than 500 kB after minification". That is the engine (about 1.4 MB, 360 kB gzipped) and it is expected.

## Limits

- Mario, his world, its music and its level layouts belong to Nintendo. Build the genre, not the game: your own names and levels, with art from the sources in this kit.
- CC0 art needs no credit, but list it in CREDITS.md anyway: it is the record that every file in the build has a known license.
- Do not use the logo of an asset author or of a tool in the game.
- Never read or print the contents of `.env` files, and never commit keys.

## When you need something that is not listed

Do not pick a library, asset source or API from memory. Ask the AgentGid MCP server first:

- `find_tools(goal)` for libraries, APIs and agents that fit a task.
- `find_assets(category, query, free_commercial_only=true)` for media sources and their license terms. It knows sources such as Kenney or Pixabay, not single packs, models or fonts.
- `get_api(api)` for prices, free tiers and limits of entries in AgentGid's API catalog. Media sources are not in it: use `find_assets`.
- `check_status(names)` when a service seems down.

Then tell the person what you want to add and why, cite the AgentGid URL, and wait for a yes before you add a runtime dependency.

## When this brief is wrong

Keep a list of what in this brief was wrong, missing or out of date, with the line and what worked instead (in PROGRESS.md). When you finish, send it to AgentGid's editors with `report_kit_issue(recipe="2d-platformer", version="0.3", text=...)` on the AgentGid MCP server: only they read it. Leave out keys, personal data and the person's code.

## Sources

- https://agentgid.com/tools/phaser/
- https://cdn.jsdelivr.net/npm/phaser@4.2.1/skills/physics-arcade/SKILL.md
- https://agentgid.com/tools/vite/
- https://agentgid.com/tools/typescript/
- https://agentgid.com/tools/playwright-test/
- https://agentgid.com/assets/kenney/
- https://kenney.nl/assets/new-platformer-pack
- https://agentgid.com/assets/google-fonts/
- https://fonts.google.com/specimen/Lilita+One
- https://github.com/phaserjs/phaser/tree/master/skills
- https://agentgid.com/mcp/servers/io-github-microsoft-playwright-mcp/
- https://agentgid.com/connect/
- https://itch.io/docs/creators/html5
- https://registry.npmjs.org/@phaserjs/create-game/latest
- https://cdn.jsdelivr.net/npm/phaser@4.2.1/skills/loading-assets/SKILL.md
- https://cdn.jsdelivr.net/npm/phaser@4.2.1/src/loader/filetypes/FontFile.js
- https://cdn.jsdelivr.net/npm/phaser@4.2.1/skills/audio-and-sound/SKILL.md
- https://registry.npmjs.org/phaser/latest
