POUNCE// README
Building a browser cat platformer with a hand-built tile level · play the game →
About Pounce
Pounce is a free browser side-scrolling platformer. The player picks one of four cats and bounds through three hand-built levels (960, 720, and 720 tiles wide) on the way to a cozy bed. Bonk boxes for cat-food cans (grow bigger) or magic fish (unlock fishbone projectiles), stomp dogs and crawling kids, shoot or pounce the wasps that fly above. Vanilla HTML, CSS, and JavaScript on top of an HTML5 Canvas with Web Audio for both sound effects and a chiptune music loop. No build step, no framework, no runtime dependency beyond Google Fonts and Supabase for the global high-score leaderboard.
The project shares its DNA with
cat-ski: same author, same
plain-prose voice, same free deploy toolchain (GitHub → Vercel),
same four cat palettes. Source is at
github.com/ashtonmorrow/bros.
The production URL is pounce.mike-lee.me.
Pounce sits alongside the other small browser experiments at mike-lee.me: cat-ski (a SkiFree tribute), pear, and go. They all share the same vanilla stack and ship-on-push deploy pattern.
How to play
The cat starts on a flat patch of grass. Walking right takes the player through a four-movement level structured in kishōtenketsu acts: a tutorial, a development section with the first floating-platform challenges, a vertical climb across disconnected platforms, and a rooftop run that recovers the pace before the goal staircase. Each level ends at a cozy bed on a raised wooden plateau. Levels 2 and 3 follow the same beat structure with their own themes (a wasp-heavy night garden, a final-sprint homecoming).
| Input | Action |
|---|---|
| A / D or ← / → | Move |
| W, ↑, or Space | Jump |
| S or ↓ (in mid-air) | Down-pounce — fast slam, instant kill on impact |
| X | Throw a fishbone (only in shooter state, after eating a magic fish) |
| P | Pause |
| M / N | Toggle music / sound effects (also buttons below the game frame) |
| C | Toggle high-contrast mode |
| R, Enter, or Space | Restart after a game over or win |
| 1 / 2 / 3 (title screen) | Jump straight to that level if unlocked |
| E / H (title screen) | Switch difficulty to easy / hard (normal is the default) |
| ← / → or click (title screen) | Cycle / select the cat picker |
Standard-layout gamepads are also auto-detected: A jumps, X shoots, B pounces, Start advances, Select pauses, the left stick or D-pad moves. On phones and tablets, on-canvas touch buttons appear automatically.
The cat starts small. Bonking a red ?-style box from
below pops a cat-food can; eating it grows the cat to big, which
absorbs one extra hit before death. Bonking another box while big pops a
magic fish; eating it grants the shooter state and the X-key
fishbone projectile that arcs, bounces off the floor up to four times, and
one-shots any enemy on contact. Hits revert the cat all the way back to
small, Mario-style.
Three obstacle animals oppose the cat. Dogs walk and patrol the ground, can be stomped from above or shot. Crawling children are shorter and slower; their hitbox is low enough that a normal jump usually clears them. Wasps fly in a sine wave and cannot be stomped from a regular jump — you have to either shoot them with a fishbone or come down on them with a down-pounce. Down-pounce one-shots anything, even wasps, and pays out 200 points instead of the standard 100 stomp bonus.
Score adds up across treats (10 each), yarn balls (50 each), pop-up cans (100), magic fish (500), stomps (100), pound-stomps (200), and projectile kills (200). Time bonus on the level-complete screen at 5 points per second remaining. A pit fall costs a life and respawns the cat at the start; the cat starts with three lives.
The top three high scores are global, backed by a Supabase table with a localStorage cache so the strip paints instantly even before the network has responded. If your final score cracks the top three, a five-character name prompt appears.
How the game is built
The game is split across four small JavaScript files plus an
index.html entry point. There is no build step, no framework,
and no runtime dependency. The constraint of staying on a vanilla stack is
deliberate: it keeps the deploy pipeline as small as possible (a
git push triggers an automatic Vercel rebuild that takes
seconds), and it forces honest engineering decisions because no library is
going to paper over a bad choice.
| Component | Implementation |
|---|---|
| Rendering | HTML5 Canvas at 800 × 480, image-rendering: pixelated |
| Game loop | Vanilla JS, requestAnimationFrame, dt clamped to 50 ms |
| Physics | Per-axis tilemap collision; gravity 0.5 px / frame² |
| Variable jump | Reduced gravity (0.275) while ascending and jump is held |
| Forgiveness | 0.10 s coyote time, 0.10 s jump buffer |
| Down-pounce | vy snaps to 14 (faster than gravity terminal velocity), vx locked to 0; instant kill on impact, +200 score |
| World sprites | Procedural pixel art via ctx.fillRect |
| Cat sprites | Vector primitives, four palettes shared with cat-ski; small & big size sets baked at startup |
| SFX | Web Audio API, OscillatorNode tones |
| Music | Web Audio chiptune loop — square-wave melody + triangle bass over an I–V–vi–IV progression in C, 132 BPM, 4 bars; 1.4× panic-mode tempo at the last 30 seconds |
| Leaderboard | Supabase (anon key, RLS-protected) for the global top-3; localStorage cache so the strip paints instantly |
| State persistence | localStorage for selected cat, music preference, leaderboard cache, last-used name |
| PWA | manifest.json + sw.js; network-first for HTML, cache-first for assets, bumped CACHE_NAME on each release |
| Hosting | Vercel, deployed from GitHub on push |
Designing the levels
Pounce ships three levels — MEADOW WALK (960 tiles wide), NIGHT GARDEN (720), and HOMEWARD BOUND (720). Each tile is 32 × 32 pixels in the world; tilemaps are 15 rows tall. The horizontal camera follows the cat and clamps at the world's left and right edges so the player can't walk off-screen behind the start or past the goal. The camera also leads the cat's facing direction by a small eased look-ahead so the player can see what's coming.
The level data is not a hand-typed ASCII grid. It is built
programmatically in js/level.js through a thin builder API
(ground, plat, ent,
treatArc) that writes features into a 2D char array. Each
level function builds its own grid with the builder's
makeBuilder(width) factory, so the three levels don't
accidentally write into one another. The grids are exposed via
window.LEVELS for game.js to load.
Each level follows the four-act kishōtenketsu framework popularised by Koichi Hayashida at Nintendo (intro → development → twist → conclusion), and each section is annotated with which of the six 2D platformer level design patterns it uses (Guidance, Foreshadowing, Safe Zone, Layering, Branching, Pace Breaking).
| Level | Width | Theme |
|---|---|---|
| 1. MEADOW WALK | 960 tiles | The full tutorial-through-mastery arc. Four movements: tutorial-and-pits, forest gauntlet with a wasp corridor, cliff approach with a vertical staircase, and a rooftop run with a mid-air stepping plat over a wide canyon. Ends at a goal staircase to the bed. Two hidden sky routes for curious players. Tutorial hints fade in along the early stretch to teach jump, pounce, and shoot. |
| 2. NIGHT GARDEN | 720 tiles | Wasp-heavy. The hidden-sky-route trick from level 1 gets re-used as the required path through one stretch (the row-3 plateau is the only solid surface for thirty tiles; falling = pit death). Followed by a lily-pond movement (four floating pads over a wide pit), a hedge tunnel funneling the cat under a wasp swarm, and a twilight stair into the goal. |
| 3. HOMEWARD BOUND | 720 tiles | The trilogy's climax. No tutorial — the player has earned this. Builds to a long “boss room” flat packing every enemy type into one space with a low ceiling, a climactic vertical staircase with a sentry wasp at the apex, a hidden rest spot above the descent valley, and a calm runway into the final cozy bed. |
The cat's running jump can clear about six tiles flat (or seven and a half with a full variable-jump hold) and reach about five and a half tiles vertically. Every pit in every level was sized to stay inside those bounds, with mid-air stepping platforms placed in any gap wider than six tiles. A programmatic audit walks each level after build, flagging any pit that exceeds the six-tile flat-jump threshold without a step inside it — the build is rejected if any survives.
The four cats
The player picks a cat on the title screen. Four palettes are available: SHADOW (black), WHISKERS (tabby), PATCHES (calico), and GINGER (orange). The drawing code is the same vector function used in cat-ski, parameterised by a palette object. A single set of geometry produces all four cats.
| Cat | Palette character |
|---|---|
| SHADOW (black) | Pure-black fur, gold eyes, no markings |
| WHISKERS (tabby) | Warm orange fur with mackerel stripes and a forehead M |
| PATCHES (calico) | White base with orange and black irregular patches; cool blue eyes |
| GINGER (orange) | Bright orange tabby with green eyes |
The cat is drawn front-facing rather than as a side-view sprite, so it doesn't flip when you change direction. That is a deliberate trade-off: the vector geometry doesn't survive a horizontal flip cleanly because markings and patches mirror with it. A front-facing chibi cat reads correctly to the player without the flip.
Working with Claude
The project was built with Claude as a coding collaborator, in roughly the
same workflow used to build cat-ski: a long prompt at the start that lays
out the constraints (vanilla stack, no copyrighted assets, original art,
keep it small enough to swap in better art later), then iterating in short
cycles where each cycle adds one feature, runs a verification step
(node --check on every JS file plus a programmatic level
sanity check), and ships.
The character picker is a useful example. The first version of the cat
sprite was procedural pixel art drawn with fillRect. It read
as "small orange thing" but not unmistakably as a cat. Rather than redo
the pixel art, the fix was to share the existing vector
drawCat function from cat-ski. Claude pulled the function and
its four palettes from the cat-ski source, baked each
palette × state combination into a small canvas at startup, and wired
a swatch-and-arrow picker into the title screen. The change took one
iteration because the constraint — "the cat needs to look like a cat
at all four colours without forking the drawing code" — was the same
constraint cat-ski had already solved.
How you can build this yourself
You don't need much. The toolchain Pounce uses is free or open source.
| What for | Tool |
|---|---|
| Editor | Any text editor — VS Code, Sublime, even TextEdit will work |
| Coding collaborator | Claude |
| Version control | GitHub (free for public repos) |
| Hosting | Vercel (free tier; auto-deploys from GitHub) |
| Local preview | python3 -m http.server or npx serve |
| Browser dev tools | Built into Chrome / Safari / Firefox |
The single most useful constraint is to start without a framework. The surface area of vanilla HTML / Canvas / Web Audio is small enough to learn in an afternoon, and the resulting game is small enough that you can deploy a change in under a minute and reload to see it. If a feature starts asking for a framework, that's signal that the feature is too big for this kind of project; pick a smaller version of it.