We built the suite as one Vite and TypeScript app with a DOM hub. Each game is code-split, so Three.js or Phaser loads only when that game starts. Every game has the same shape: a pure, deterministic rules engine with no DOM, and a renderer that only draws the engine’s state and turns input into engine calls.
Key design decisions
- Fixed-step, seeded engines. All three engines advance at exactly 1/60 s and draw randomness from a seed stored in the state, so a fight can be replayed exactly. Vitest drives the engines directly, without a browser.
- Bots as balance tests. A bot that reads only on-screen tells (wind-ups and shockwave rings) must beat Krodh on at least two of four seeds, and an idle player must lose. A second bot must clear all three beat ’em up stages. Tuning those tests is how we arrived at Krodh’s 1,900 HP, health regeneration and the dodge dash.
- Characters built in code. Suryaveer and Krodh are jointed 3D rigs made from primitives with toon shading and inverted-hull outlines, posed procedurally for every move. The beat ’em up uses matching 2D jointed figures baked to textures, and the hub portraits are rendered from the real 3D models at start-up.
- Design for the lure. In the city, a strike makes Krodh chase the hero, and strikes count triple once he is pinned in a park or construction site. Strikes in busy blocks cause collateral damage. The best play is to lead him away from people, not just to hit him harder.
- Readable 3D on any screen. The arena camera frames both fighters, the city camera pulls in front of towers that would hide the hero, and a beacon and pointer always show where Krodh is. Phones get a joystick and buttons, and a quality preset turns off shadows on slower devices.
Architecture
src/art holds the 3D rigs, props and 2D fighters. src/core holds the Three.js environment, input, synthesised audio, the save and the PRNG. src/games holds the arena, brawler and city engines with their renderers, and src/ui the hub, modals and portrait rendering.