Skip to main content
Back to all work
2026PublicPersonal project · Solo dev

Clash Arena · A Pokémon roguelike on a custom ECS engine

Browser-based Pokémon game built solo. Custom ECS for the simulation, Canvas 2D for gameplay, Supabase for persistence and async PvP.

Clash Arena · A Pokémon roguelike on a custom ECS engine

What it is

Clash Arena is a Pokémon game you play in the browser. Three gameplay modes share the same simulation core: wild battles with captures, a gym league with bosses, and a Vampire Survivors-style infinite mode. On top of that, a full meta layer: a Pokédex with 1000+ creatures, captures and team builder, shop, rewards, badges, coin economy, public profiles and a leaderboard. No game engine. Every gameplay loop, render pass and physics check is written by me.

Home view with the Pokémon roster grid

Why I built this

A pure-craft project, no commercial intent. The exercise was designing and building a complete product end to end on my own: auth, profiles, sessions, onboarding and tutorials, every view tied together with a consistent visual language. Glassmorphism, motion-driven feedback and modern UI techniques applied across the whole surface, not just the marquee screen. The Pokémon shell is the excuse; the real goal was proving I could carry a non-trivial app from auth flow to gameplay loop solo, without cutting corners on UX.

The custom ECS

I needed something with specific constraints: no bundle bloat from game libraries, full control over the simulation tick, and an entity model that could scale to bullets and particles without garbage collection pauses. So I built the ECS from scratch, with typed object pools per entity kind and a spatial hash grid for broadphase collisions.

The gameplay renders on Canvas 2D, not React. A single render surface reads the ECS state every frame and draws sprites, hitboxes, particles, projectiles and damage numbers. React only owns the menus and the HUD.

Wild battle on the Canvas 2D renderer

Why Canvas for gameplay instead of WebGL

A full 3D pipeline is elegant but expensive per frame. For a bullet-hell-adjacent game with hundreds of entities on screen, going straight to Canvas 2D was the right tradeoff: less magic, more control, predictable performance, and no reconciler in the hot path.

The gym league

The league is the game's structured campaign: gyms typed by element, leaders with curated movesets and a final cycle that loops with scaled difficulty. Battles run on the same ECS as the wild encounters, but the AI and special-attack profiles flip into harder configurations. Gym progress, badges and unlocked avatars persist server-side.

Gym league hub with leader cards

The infinite mode

Infinite mode is the bullet-heaven loop: you survive waves, level up, choose power-ups, and a chase camera follows you across procedurally chunked terrain. Difficulty scales by wave, and a boss interrupts the cadence at fixed thresholds.

Infinite mode with enemies, projectiles and XP orbs

Captures and team builder

Wild battles and league wins feed a personal collection. The captures view is the meta loop: browse what you've caught, filter by type, swap Pokémon across active team slots, set a leader. The same team carries into wild encounters, gym fights, infinite runs and async PvP.

Captures and team management view

Pokédex and sprite pipeline

1000+ Pokémon, every one of them browsable. The sprite system pulls from multiple public sources through a precomputed manifest, so the browser never hits a 404 at runtime. Stat curves, evolutions, type matchups and shiny variants are hydrated client-side with cached lookups.

Pokédex detail view with stats and sprites

Backend, persistence and async PvP

Supabase handles auth, row-level security and the persistence layer for trainers, captures, teams and run state. PvP is asynchronous by design: it pulls a target trainer's persisted team and runs a deterministic battle locally, no realtime channels, no matchmaking.

What I'd change

The async PvP is the right call for a solo project, but if the game finds an audience the next big block is bringing it online properly: matchmaking, rated ladders and live channels, without breaking the arcade feel.

Play it

To start, just pick a nickname and a password. Nothing else.