---
title: "Pixel Platformer"
slug: platformer
version: 1.0.0
updated: 2026-10-20
sha256: a27c81febba354effa05e4442e3ec236b36e513f7a6d1f7f4bfb5f0efefb5981
category: fun
category_name: "Fun & Games"
source: https://promptbai.bonq.foo/r/platformer.md
check: https://promptbai.bonq.foo/r/platformer.json
---

# Pixel Platformer — a promptbai recipe (v1.0.0)

> A side-scrolling jump-and-run game with your own hero, Mario-style

## Instructions for the AI agent (read first)

You are an AI coding agent. A person (who is probably **not** a programmer) has asked you to build this for them by following this recipe from promptbai (https://promptbai.bonq.foo).

1. **Read the whole recipe before you start.** Then follow it section by section.
2. **Talk to the person in plain language.** Avoid jargon. When you need a decision from them, ask one short question at a time and offer a sensible default.
3. **Ask when something is unclear** instead of guessing on things that matter to them (names, colors, personal details). Use your judgment on purely technical details. If the person doesn't answer or isn't available, use the defaults the recipe gives and keep going.
4. **Respect the Constraints section.** If the person's setup makes a constraint impossible, explain why and suggest the closest alternative.
5. **Check your work** against the Acceptance checks before you say you are done.
6. **Write a file named `promptbai.json`** in the root of the project folder with these values (formatting may differ), so the build can be updated later:

```json
{
  "slug": "platformer",
  "version": "1.0.0",
  "sha256": "a27c81febba354effa05e4442e3ec236b36e513f7a6d1f7f4bfb5f0efefb5981",
  "source": "https://promptbai.bonq.foo/r/platformer.md",
  "check": "https://promptbai.bonq.foo/r/platformer.json"
}
```

7. **If you can't create files yourself** (for example, you are a chat assistant), give the person each file in full, one at a time. Tell them exactly what to name it, where to save it, and how to save it with an app they already have.
8. **Finish with the Hand-off section:** tell the person, simply, where their new thing is, how to open it, and how to use it. Include a short "your choices" note listing the options you used (theirs or the defaults) and how to change them later.
9. **Where practical, keep core logic** (dates, calculations, parsing) in plain functions you can test outside a browser, for example with `node`. In a single-file app, put them in their own `<script id="core">` block and load that block into node for tests.

If anything in this recipe turns out to be unclear or wrong, ask the person whether you may tell the promptbai editors, and see https://promptbai.bonq.foo/agents.md for how.

To check for updates later, fetch https://promptbai.bonq.foo/r/platformer.json and compare its `version` with the one in `promptbai.json`. The full update protocol is at https://promptbai.bonq.foo/agents.md. The `sha256` is the SHA-256 of everything after the marker line below (UTF-8, LF line endings).

---

<!-- promptbai:recipe-body -->
## Goal

Build a classic 2D side-scrolling platformer in the spirit of Super Mario Bros., as a single web page that opens by double-clicking. It has tight, responsive jumping, enemies to stomp, blocks to bump, power-ups, coins, a goal flag, and four hand-designed levels ending in a boss fight. All art, sound and music are original and generated in code.

The game lives or dies on how the jump feels. Use the physics numbers below as the starting point and keep them in one `PHYS` object so they are easy to tune.

## Before you start, ask the person

1. The hero: a **fox** 🦊, a **robot** 🤖, a **cat** 🐱, or a **kid in a cap** 🧢 (or describe your own). (Default: fox.)
2. The game's name for the title screen. (Default: "Pixel Platformer".)
3. Who's playing? **Normal** (3 lives, a timer) or **Gentle** for young kids (unlimited lives, no timer, and falling in a pit just puts you back on the last solid ground). It can be changed later in the options. (Default: Normal.)

## Features

- **Screen:** an internal resolution of 320 × 240 (20 × 15 tiles of 16 px), scaled up by the largest whole number that fits the window, with letterboxing and crisp pixels (`imageSmoothingEnabled = false`, CSS `image-rendering: pixelated`). Game logic runs at a fixed 60 steps per second with an accumulator, independent of the display's refresh rate.
- **Controls:**
  - Keyboard: ← → or A D to move, ↓ or S to crouch (big form only), Z / Space / K to jump, X / Shift / J to run and to throw. Enter or P pauses, M mutes.
  - Game controller: the Gamepad API with the standard mapping (d-pad or left stick to move, bottom face button to jump, left face button to run and throw, Start to pause).
  - Touch devices: on-screen ◀ ▶ buttons bottom-left and Run and Jump buttons bottom-right (at least 64 px), shown only on touch screens.
- **Hero physics** (pixels per frame at 60 steps per second; keep these in `PHYS`):
  - Speed: walk max 1.6, run max 2.6. Ground acceleration 0.10 walking and 0.14 running. Slowing down with no input 0.12. Skidding (pressing against the current direction) 0.30, with a skid pose.
  - In the air: acceleration 0.07, and the hero keeps their momentum.
  - Jump: starting speed −6.5, or −7.0 when moving faster than 2.2. While rising with jump held, gravity is 0.30. Otherwise gravity is 0.70. Maximum fall speed 6. So holding jump gives a high jump of about 4.4 tiles, and a tap gives a short hop of about 2 tiles.
  - Forgiveness: 6 frames of coyote time (you can still jump just after running off a ledge) and a 6-frame jump buffer (a jump pressed just before landing still happens).
  - Hitbox: 12 × 14 px when small, 12 × 26 px when big. A big hero crouching uses 12 × 14 and can't walk, only slide to a stop.
- **Collision:** tile-based, one axis at a time (X then Y).
  - Head bumps: when the hero's head hits a block from below, the block under the centre of the head is the one that gets hit.
  - Corner correction: if the head clips a block edge by 4 px or less, nudge the hero sideways and let the jump continue.
  - One-way platforms are solid only from above, and only when the hero's feet were above them on the previous frame.
- **Blocks and items:**
  - Bumping a block from below makes it hop up 4 px. Anything standing on a bumped block is hit from beneath (enemies are knocked out, coins are collected).
  - A prize block gives a coin or a power-up, then becomes an empty used block. A multi-coin variant gives up to 10 coins, as many as you can bump within 4 seconds.
  - Breakable bricks shatter into 4 tumbling pieces when hit by a big hero, and just bump when hit by a small one.
  - A hidden block is invisible until bumped from below. Each level has one, holding an extra life.
- **Power-ups:**
  - Small → **Big** after collecting a *berry*. It rises out of its block, slides at 1 px/frame, bounces off walls and falls off ledges.
  - Big → **Spark** form after collecting a *spark flower*. The flower stays put. A prize block gives a berry to a small hero and a flower to a big one.
  - In spark form, the run button throws a bouncing spark. At most 2 can be on screen. A spark moves at 4 px/frame, bounces with an upward speed of −3 when it lands, disappears when it hits a wall, and knocks out any enemy it touches.
  - Getting hit makes the hero small (from Big or Spark), then gives 120 frames of blinking invulnerability. A hit while small loses a life.
- **Enemies** (all original designs):
  - *Walker:* walks at 0.5 px/frame, turns around at walls and walks off ledges. Stomping flattens it.
  - *Shell-back:* stomping it makes it hide in its shell. Touching the shell kicks it, and it slides at 4 px/frame, bounces off walls and knocks out enemies, and it can hurt the hero too. A shell left alone for 8 seconds wakes up.
  - *Spiky:* can't be stomped (doing so hurts the hero). Only sparks, kicked shells or bumps from below beat it.
  - *Flyer:* flies in a sine wave. Stomping it once makes it a walker.
  - Spawning: enemies appear when their start column comes within 2 tiles of the screen edge for the first time. They don't respawn, and are removed when they fall off the map.
- **Stomping:** a hit counts as a stomp when the hero is falling and their feet were above the enemy's top half on the previous frame. A stomp bounces the hero up at −4 (−6.5 if jump is held). Stomps chained without landing score 100, 200, 400, 800, 1000, 2000, 4000, 8000, then an extra life for every further stomp.
- **Score and lives:**
  - Points: coin 200, power-up 1000, brick 50, and time bonus 50 per second left.
  - 100 coins give an extra life.
  - Normal mode: 3 lives and a 300-second timer per level; at 0 the hero loses a life. Game over returns to the title, keeping unlocked levels.
- **Goal:**
  - A flag pole at the end of each level. Touching it slides the hero down. The bonus depends on how high the hero grabbed it, in five bands from 100 at the bottom to 5000 at the very top.
  - The hero then walks into a door, the time bonus counts up with a ticking sound, and the next level unlocks.
  - Each level has a midpoint checkpoint flag. A life lost after touching it restarts from there.
- **Dying:** the hero pops up, then falls off the screen without colliding with anything. Wait 2 seconds, then restart from the last checkpoint.
- **Camera:** follows the hero horizontally with a dead zone in the middle third of the screen and never shows past the level edges. It doesn't scroll vertically (levels are exactly 15 tiles tall).
- **Four levels**, each 150–220 tiles wide and stored as ASCII tile maps in the code:
  1. **Green Hills:** daytime and gentle, teaching the basics.
  2. **Underground:** a dark palette with low ceilings, lots of bricks and multi-coin blocks.
  3. **Treetops:** one-way platforms, horizontal moving platforms over long drops, and flyers.
  4. **Castle:** lava pits, rotating fire bars, then a boss arena. The boss is twice the hero's size, walks back and forth and hops every 2–3 seconds. It takes 3 stomps (or 6 sparks) to beat, with 60 frames of invulnerability after each hit and more speed each time. Beating it ends the game with a victory screen and the final score.
- **Tile legend** for the maps (use it, and add letters if needed):
  - Terrain: `.` empty, `#` ground, `=` hard block, `B` brick, `-` one-way platform, `[` `]` pipe top, `{` `}` pipe body, `~` lava.
  - Blocks: `?` prize block with a coin, `P` prize block with a power-up, `$` multi-coin brick, `H` hidden block with an extra life.
  - Things: `o` coin, `M` moving platform, `R` fire bar pivot.
  - Enemies: `g` walker, `k` shell-back, `s` spiky, `f` flyer.
  - Markers: `@` hero start, `C` checkpoint, `F` flag pole base (the pole is drawn 9 tiles tall), `X` boss.
- **Art:** all sprites are pixel art defined in code as small strings with a palette character map, drawn once into offscreen canvases at startup. The hero has idle, 3-frame run, jump, skid, crouch and death frames in all three forms (Spark form changes colours). Add parallax background layers per level (hills and clouds, cave rocks, sky and treetops, castle bricks).
- **Sound and music:**
  - Short chiptune effects made with the Web Audio API (square and triangle oscillators plus noise): jump, coin, stomp, bump, brick break, power-up, power-down, extra life, death, flag and pause.
  - An original looping melody for each level, written as note arrays and played with a lookahead scheduler. It speeds up 1.25× when 100 seconds are left.
  - Never use or imitate the melodies of existing games.
- **Screens:** a title screen with the game's name, "Press Jump to start", a level select of unlocked levels with each one's best score, and Options (Normal or Gentle, sound and music volume, and a toggle for showing touch controls). Add a pause screen and a game over screen.
- **Saving:** unlocked levels, best score per level and the options are kept in `localStorage`.

## Constraints

- One self-contained `index.html` with inline CSS and JavaScript: no libraries, no external requests, no image or audio files. It works offline when opened from the file system.
- **Everything original:** don't use Nintendo's characters, names, sprites, sounds or music (no Mario, no Goombas, no question-mark-block sound effects copied from the games), and don't use any other company's either.
- Keep good contrast: the hero and enemies must be easy to see on every background.
- Put the physics, collision, tile-map parsing and level checks in a `<script id="core">` block that uses no DOM, so they can be tested with `node`.
- Expose `window.game` for testing: `loadLevel(n)`, `hero` (position, velocity, form, onGround), `setInput({left, right, jump, run, down})` and `step(frames)`.
- **Level design rules:**
  - Every required jump must be possible at walking speed in Green Hills. In later levels, gaps that need a running jump are fine.
  - Never require a jump higher than the maximum jump height minus half a tile.
  - Place nothing unfair just past a screen edge (no enemy within 3 tiles of a landing spot after a blind jump).

## Build steps

1. Core in node: `PHYS`, the hero step function, tile collision with corner correction, and the map parser. Test the jump heights and distances.
2. Canvas, scaling, the fixed-step loop, input (keyboard first), and drawing Green Hills with placeholder rectangles.
3. Hero sprites and animation, the camera, prize blocks, bricks, coins and power-ups.
4. Enemies, stomping, shells, damage, invulnerability, dying and checkpoints.
5. The flag pole, scoring, HUD (score, coins, level, time, lives), and the title, pause and game over screens.
6. Levels 2–4, moving platforms, fire bars, lava and the boss.
7. Sound effects and music, gamepad and touch controls, saving, and Gentle mode.
8. Run the level checks below, then tune and polish.

## Acceptance checks

- **Jump (node):** from standing with jump held, the hero rises between 4.0 and 4.8 tiles. A one-frame tap rises between 1.5 and 2.5 tiles. The longest walking jump from flat ground to the same height and the longest running jump, both measured with `step()`, match what the levels assume.
- **Levels (node):** a checker built from the measured jump numbers reads each map and fails if any gap is wider than the measured jump distance (walking distance for Green Hills, running distance elsewhere) minus 1 tile, or if any required step up is taller than the measured high jump minus half a tile. Every level has exactly one `@`, one `F` (or `X` for level 4), one `C` and one `H`.
- **Collision (node):** the hero never ends up inside a solid tile, including when running into a wall corner at full speed. Jumping into a brick's edge with a 3 px overlap slides past it (corner correction), and a 6 px overlap bumps it.
- Stomping a walker flattens it and bounces the hero. Touching it from the side hurts the hero. A big hero becomes small and blinks, and a small one dies.
- A kicked shell knocks out a row of walkers and the chain score rises. A spiky hurts a hero who stomps it.
- Prize blocks give a berry to a small hero and a flower to a big one, and become used blocks. A big hero breaks bricks and a small one only bumps them.
- Grabbing the flag near the top scores 5000, and the time bonus is added. The next level unlocks and stays unlocked after a reload.
- All four levels can be completed. The boss is beaten by 3 stomps and the victory screen shows.
- The game plays fully with only the keyboard, only a gamepad, and only touch (check touch in the browser's device emulation). No console errors.
- Gentle mode: no timer is shown, lives don't go down, and falling into a pit puts the hero back on the last ground they stood on.

## Hand-off to the person

Tell them where `index.html` is and to double-click it. Give them the controls in a short table (keyboard, gamepad and touch). Mention that holding Jump longer jumps higher, and holding Run lets you jump much further. Explain how to switch to Gentle mode for young kids in Options. Progress and best scores are saved in this browser only. A local file can't go on a tablet's home screen; if they want that, explain it would need to be put online first, and offer to help.
