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 a 240-tile hand-built level on the way to a cozy bed. Bonk red 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 you through a 240-tile level structured in four acts: a tutorial, a development section with the first floating-platform challenges, a climax in the air across disconnected platforms, and a final stretch that recovers the pace before the goal staircase. The level ends at a cozy bed on a raised wooden plateau.

InputAction
A / D or Left / RightMove
W, Up Arrow, or SpaceJump
S or Down Arrow (in mid-air)Down-pounce — fast slam, instant kill on impact
XThrow a fishbone (only in shooter state, after eating a magic fish)
PPause
MToggle music on / off (also a bezel button)
R, Enter, or SpaceRestart after a game over or win
Left / Right (title screen)Cycle the cat picker
Click a swatch (title screen)Select that cat directly

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.

ComponentImplementation
RenderingHTML5 Canvas at 800 × 480, image-rendering: pixelated
Game loopVanilla JS, requestAnimationFrame, dt clamped to 50 ms
PhysicsPer-axis tilemap collision; gravity 0.5 px / frame²
Variable jumpReduced gravity (0.275) while ascending and jump is held
Forgiveness0.10 s coyote time, 0.10 s jump buffer
Down-pouncevy snaps to 14 (faster than gravity terminal velocity), vx locked to 0; instant kill on impact, +200 score
World spritesProcedural pixel art via ctx.fillRect
Cat spritesVector primitives, four palettes shared with cat-ski; small & big size sets baked at startup
SFXWeb Audio API, OscillatorNode tones
MusicWeb 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
LeaderboardSupabase (anon key, RLS-protected) for the global top-3; localStorage cache so the strip paints instantly
State persistencelocalStorage for selected cat, music preference, leaderboard cache, last-used name
PWAmanifest.json + sw.js; network-first for HTML, cache-first for assets, bumped CACHE_NAME on each release
HostingVercel, deployed from GitHub on push

Designing the level

The level is 240 tiles wide and 15 tiles tall. Each tile is 32 × 32 pixels in the world, so the playable area is 7680 × 480. The horizontal camera scrolls to follow 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 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) that writes features into a 2D char array. The final grid is exported as an array of 240-character strings for game.js to consume. This makes the level easier to reason about and to refactor when something needs to shift.

The structure 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).

ActColsWhat it teaches
1. Introduction0–49 Walking and trivial 3-tile jumps. A treat trail (Guidance) and one slow enemy with an optional high path (Branching) carrying a bonus yarn ball.
2. Development50–123 Wider 4–5 tile pits, a three-layer platform showcase (Layering), and a small mid-air stepping stone that previews the bigger gaps in Act 3 (Foreshadowing). Ends with a clean three-step staircase up.
3. Twist124–184 Disconnected platforms with no ground below, three back-to-back enemies on flat, the level's only 7-tile pit (with two stepping platforms at different heights), and a vertical tower section.
4. Conclusion185–239 An enemy-free recovery flat (Safe Zone — the calm before the goal), one easy pit, one final bonus yarn, and a four-step goal staircase ending at the cozy bed. The bed is visible from col 218 onward so the player always knows where they're going.

The cat's running jump can clear about five tiles flat (or six with a full variable-jump hold) and reach about five tiles vertically. Every pit in the level was sized to stay inside those bounds, with mid-air stepping platforms placed in any gap wider than five tiles. A static check verifies every entity has a solid tile underneath and no platform sits directly above another solid tile (a stacking trap that would have the cat clipping into a tile while standing on the one below).

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.

CatPalette 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 forTool
EditorAny text editor — VS Code, Sublime, even TextEdit will work
Coding collaboratorClaude
Version controlGitHub (free for public repos)
HostingVercel (free tier; auto-deploys from GitHub)
Local previewpython3 -m http.server or npx serve
Browser dev toolsBuilt 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.