Case Study

Shapescape – draw-to-play platformer

A retro browser platformer where the level is whatever you draw. Drag to place collidable platforms, dodge a shooting eye, grab coins, survive. Built for a JS game jam and tuned for mobile and desktop from the same codebase.

TypeScriptReactGame designPhysics

Concept

Most platformers give you a hand-designed world. Shapescape gives you an empty one and a mouse. The core loop is: the world scrolls, an enemy tracks you, you draw terrain in real time to block projectiles, land jumps, and corral coins. Drawing is the game mechanic, not a level editor mode.

Implementation

  • Custom AABB physics: hand-rolled resolver separating horizontal and vertical passes so the player can stand on, bump against, and slide past dynamically-drawn rectangles without tunnelling.
  • Frame-budgeted game loop: a single requestAnimationFrame loop drives physics, enemy AI, projectiles, and spawning inside one React state update per frame to keep things predictable.
  • "Evil fish eye" enemy: tracks the player, accelerates over time, and fires three projectile types — red (blocked by drawn squares), yellow (destroys squares on contact), and blue (passes through everything) — forcing the player to draw strategically rather than just densely.
  • Unified pointer input: mouse and touch handlers share the same drag-to-draw code path, with on-screen controls for mobile (left, right, jump) and keyboard controls on desktop.
  • Pixel-art character: pixelated idle frames and a GIF running animation with direction-based horizontal flipping via CSS transforms.

Outcomes

Shipped as a playable game embedded in this site (tap the age counter to open it) and open-sourced as js-gamejam on GitHub. It became a useful testbed for squeezing game-loop logic into a React component without fighting the framework, and a reminder that the best game-jam mechanics are usually the ones that collapse two systems (drawing + physics) into one verb.