Active · Jun 10, 2026
Ki Clash
A real-time 1v1 PvP game from the Korean schoolyard 기싸움 hand game — read the opponent, charge ki, strike on the right beat. Python (FastAPI) and a second Go runtime share one Redis-backed game state; Next.js 16 PWA, room-based PvP, 6 character ultimates, AI-generated sprites.
Python + Go dual runtime · shared Redis state · 164 tests · 136 commits · 6 ultimates · 36 sprites
- Role
- Solo (AI-pair-programmed with Claude Code)
- Stack
- Python 3.11 · FastAPI · Go 1.23 · Next.js 16 · React 19 · PostgreSQL · asyncpg · Redis · Caddy · Tailwind v4

A real-time 1v1 port of 기싸움, the Korean schoolyard hand game. The product is a small game; the engineering is the point — two server runtimes (Python and Go) sharing one Redis-backed game state, room-based PvP, and an AI sprite pipeline.
What it does
- Real-time 1v1: both players pick charge / block / punch / ki-burst / teleport at the same time. Moves reveal on the same instant and an outcome matrix decides who took damage, who built ki, who got dodged. First to win 2 of 3 rounds takes the match.
- Two ways in: Quick Match (auto-matched from a global queue) or Create / Join Room — host gets a 4-character code, a friend joins, both pick a character and ready up. This is the "an interviewer can play with me in 30 seconds" path.
- Six characters, each with a signature ultimate that fires on the final blow.
The engineering worth pointing at
- Two parallel server runtimes, one shared truth. Python (FastAPI) and Go both read and write the same Redis JSON blob (
ki_clash:game:{id}). The canonical mutation — load →WATCH/MULTI/EXEC→ publish to per-player Redis channels — is ported byte-for-byte across runtimes. End-to-end verified: a room spawned via the Python REST endpoint, both players connect over Go's WebSocket, both submitcharge, both receive correctly personalizedturn_resultenvelopes (go-server/test_e2e.py). Go is wired to take the hot path through a one-line Caddy route swap; Python stays authoritative until then. - Distributed-state hardening, with the bugs written down. An in-process PvP simulator surfaced four real concurrency bugs — a spurious
opponent_reconnectedon first connect, a duplicatewaiting_for_action, message ordering, and a missingturn_number— each fixed with a symptom / cause / commit entry in the repo's troubleshooting log and a regression test so they don't come back. - An AI sprite pipeline that survives a transparency and copyright pass. 36 fighter PNGs (6 characters × 6 poses) generated through Pollinations/flux, then re-processed through rembg (U2Net) to strip the white backgrounds the generator leaves. The renderer picks the right PNG per pose and drops the CSS "puppet" rotation to scale-only when a pose is already oriented, so a
ko.pngthat's lying down isn't double-rotated. One regeneration pass was already done on a sprite that landed too close to a licensed character. - A written reasoning trail. 15 design-decision records (DR-1 through DR-15) and a 2,512-line engineering log — backend language choice (Python vs Spring vs Go), the asset-pipeline shape, Redis-as-truth with stateless workers, optimistic concurrency for turn submission — each annotated with the alternative considered and the trade-off taken.
What it doesn't do (yet)
- No automated frontend tests. The engine, matchmaking, game store, AI opponent, and pub/sub have 151 Python test functions; the Go engine has 13 — but the React UI is verified by hand.
- The Go runtime is verified end-to-end but not yet authoritative; flipping it on is a one-line Caddy uncomment plus a redeploy.
- A single-round-trip Lua atomic submit was written, then reverted over a Redis
cjsonempty-array encoding limit.WATCH/MULTI/EXECis fine at current contention (single digits per day). - Mobile (Expo) is paused on a React 19 / Reanimated peer-dependency conflict; it runs as a PWA on mobile browsers in the meantime.
Project log
Chronological record of troubleshooting, retros, and updates while building this.
Week of Jun 8, 20261 entry · 1 Tech retro
Week of Jun 1, 202614 entries · 3 Update · 3 Troubleshoot · 3 Tech retro · 5 UX retro
Go-live night — real AWS deploy + the bugs only prod surfaced
TroubleshootJun 7, 2026 · 4 min
Go-live on AWS EC2 (t3.micro) exposed bugs no local run had: a single-player 'Game not found' from 2 uvicorn workers over in-memory state, a broken disconnect/reconnect + forfeit-stats + /health HEAD path, a pubsub listener that crashed on idle, and OOM during builds — fixed with --workers 1, a session repair, get_message polling, and 2GB swap baked into user_data.
Viewport-locked single-screen mobile + installable PWA
UX retroJun 7, 2026 · 5 min
Locks every gameplay phase to one no-scroll viewport (100svh, capped arena, compact spacing) on a single stable skeleton that never reflows, removes the growing in-match turn history, makes the web client an installable home-screen PWA, and cuts the AI reveal/round hold from 5.1s/4.5s to 3s.
PixiJS v8 WebGL effects (DR-17) and the additive-overlay lesson (DR-18)
Tech retroJun 7, 2026 · 5 min
A PixiJS v8 WebGL effect engine and /pixi-test harness wired into both PvP and vs-AI matches — plus the day's hard lesson: the first integration replaced the dynamic KiAuraArena and silently deleted its character motion, so it was reverted and re-done as an additive transparent overlay (DR-18: enhance = compose, not replace).
AWS infra as Terraform IaC — VPC + EC2 + separated EBS + EIP
Tech retroJun 4, 2026 · 4 min
One Terraform module stands up the JJAN! backend on AWS — a /16 VPC, a single t3.micro EC2 host, a lifecycle-protected EBS data volume that outlives instance replacement, and an Elastic IP — followed by four deploy fixes the first real build surfaced (Go base bump, distroless self-healthcheck, Caddy domain, Vercel build env).
DR-16 — client-ready handshake + 8-dimension live verification
TroubleshootJun 4, 2026 · 5 min
Gated turn 1 behind a two-player presence check so the 5s turn clock never starts against a ghost opponent, split the room start-guard from the handoff-guard to fix a 'Match starting' hang, made CharacterAvatar render the real fighter PNG, then ran an 8-dimension live verification that surfaced 4 bugs.
Vercel monorepo routing + canonical-domain redirects
TroubleshootJun 3, 2026 · 2 min
Added a root-level vercel.json so Vercel builds the web/ subdirectory in this monorepo instead of failing on a missing root package.json, then layered 301 redirects so www and the old kiclash alias both funnel to the canonical jjan.daeseon.ai.
Rebrand to JJAN! · 짠 + recruiter-ready bilingual README
UpdateJun 2, 2026 · 3 min
Renamed the user-visible product from "Ki Clash" to "JJAN! · 짠" across the web surfaces (metadata, lobby, shop, share-sheet) while keeping the internal ki-clash code slug untouched, and shipped a grounded bilingual EN/KO recruiter README plus a problem-indexed troubleshooting reference.
Character-specific ultimate finishers + real fighter PNGs on select
UX retroJun 2, 2026 · 3 min
Replaced the one generic match-end beam with six per-character signature ultimates, swapped the emoji placeholders on character-select for the real fighter PNGs under global-friendly names, and closed the 6×6 fighter sprite matrix with seokjin + yuri's pose sets.
Phase 11 — in-house FX overhaul (Lottie-quality, no external assets)
UX retroJun 2, 2026 · 4 min
Phase 11 rebuilds all six arena effects in-house with framer-motion + canvas-confetti + multi-layer SVG (zero external dependency), fixes white-box fighter backgrounds with local AI bg removal + pose-aware sprite transforms, and splits the Attack (POW punch) and Energy Wave (energy-beam) moves into visually disjoint FX.
Deploy-night checkpoint — Phase 7G/8/10 done, Phase 9 (live deploy) pending Jason
UpdateJun 1, 2026 · 3 min
Deploy-night checkpoint: Phases 1-4, 7, 8, and 10 are done and the backend is production-ready; Phase 9 (the actual AWS EC2 + Vercel deploy) is the only outstanding work and is waiting on Jason — nothing is live yet.
Phases 7D-F — fighter sprites, hit/KO reactions, image-first pipeline + 6 AI idle sprites
UX retroJun 1, 2026 · 1 min
Phases 7D-F replaced placeholder fighters with humanoid then shōnen-knockoff sprites, added hit/KO reactions and a match-end final-blow KO sequence, and moved to an image-first pipeline backed by 6 AI-generated idle sprites.
Phase 10 — standalone Go WebSocket game server (scaffold → game loop → hardening)
Tech retroJun 1, 2026 · 3 min
Three commits stood up the Go game-server tier from DR-1's hybrid 2-tier design: a WebSocket scaffold, a full game-loop port wired for prod, and a hardening pass adding cross-instance reconnect, metrics, Sentry, unit tests, and a load harness.
Phase 7 — cinematic match-end finale + anime ki-aura arena
UX retroJun 1, 2026 · 2 min
Phase 7 ships a cinematic match-end finale and an anime ki-aura arena in the web client, realizing decision DR-2's move from pixel art to a high-impact arcade anime aesthetic.
Phase 8 — Tekken-style room PvP (create code, share, character select, ready up)
UpdateJun 1, 2026 · 1 min
Phase 8 adds Tekken-style room PvP — create a room code, share it, pick a character, and ready up — with a backend rooms API plus the frontend /pvp flow, followed by a chore to auto-derive the WebSocket URL from the API URL in production.
Week of May 25, 20269 entries · 2 Update · 2 Troubleshoot · 5 Tech retro
RESUME-HERE checkpoint added to the engineering log
UpdateMay 28, 2026 · 1 min
Added a Part 0 'RESUME HERE' checkpoint to docs/engineering-log.md so the next session opens with a single where-are-we / what's-next handoff snapshot.
Phase 6 — production stack scaffold (AWS EC2 + Vercel hybrid)
UpdateMay 27, 2026 · 2 min
Scaffolded the production deployment stack as an AWS EC2 + Vercel hybrid — a single docker-compose stack (db + redis + api + caddy) on EC2 with Caddy terminating TLS and reverse-proxying the FastAPI backend, and the Next.js frontend on Vercel.
Wrote the Engineering Decision Reference (DR-1..11)
Tech retroMay 26, 2026 · 2 min
Added a 'Part 2 — Engineering Decision Reference' section to docs/engineering-log.md with 11 deep trade-off analyses (DR-1..DR-11), each documenting the decision, options, trade-off table, reasoning chain, meta-pattern, and interview framing.
Multiplayer networking reference (Ki Clash vs LoL / Valorant)
Tech retroMay 26, 2026 · 1 min
Created a standalone Korean networking reference doc that maps Ki Clash code onto real-time multiplayer concepts, doubling as Toronto game/systems interview prep.
Phase 2 — test coverage + observability (JWT auto-recovery, JSON logs, Sentry, Prometheus)
Tech retroMay 26, 2026 · 4 min
Phase 2 added 52 new tests (game engine, matchmaking) and the observability backbone — structured JSON logging, Sentry, and Prometheus /metrics — plus a client-side JWT 401 auto-recovery fix. Suite grew from 52 to 99 tests.
Phase 3 — fixed the 4 PvP concurrency bugs
TroubleshootMay 26, 2026 · 2 min
Phase 3 fixed the 4 PvP concurrency bugs documented as xfail during Phase 2.1; the integration suite went from 6 passed / 4 xfailed to 10 passed, and the full suite to 126 passed.
Phase 4 — Redis-backed stateless PvP (game store, cross-worker pub/sub, 149 tests)
Tech retroMay 26, 2026 · 4 min
Phase 4 externalized all in-memory PvP state to Redis, added per-player pub/sub for cross-worker routing, and used optimistic concurrency for atomic two-player turns — landing the full suite at 149 tests passing in ~23s.
PvP simulator surfaces 4 concurrency bugs (captured as xfail tests)
TroubleshootMay 26, 2026 · 3 min
Driving a real Bo3 match through a PvP simulator against the live docker stack surfaced 4 concurrency/protocol bugs, which were then locked into a permanent pytest integration harness as xfail tests.
Asset pipeline + fallback components; pixel-art moved to deprecated/
Tech retroMay 25, 2026 · 2 min
Scaffolded a drop-in asset pipeline with graceful emoji fallback components on web and mobile, and moved the pixel-art code into a deprecated/ folder via git mv since 12+ active screens still import it.
Week of May 18, 20261 entry · 1 Tech retro
Week of Feb 23, 20261 entry · 1 Troubleshoot
Week of Feb 16, 20266 entries · 3 Update · 3 UX retro
6 original Korean fighters, character select, selection timer, AI trash talk + tutorial
UX retroFeb 22, 2026 · 2 min
Added a roster of 6 original Korean fighters with a dedicated character select screen, converted the countdown into a selection timer, layered in AI trash talk and an interactive tutorial, and ported the whole set to mobile.
Core game built in a day — engine, backend API, web frontend
UpdateFeb 22, 2026 · 1 min
Single milestone commit shipped the product-agnostic game engine, an AI opponent with difficulty tiers, a versioned FastAPI backend, and a Next.js web frontend — plus dev-workflow slash commands.
Game feel — Web Audio SFX, card-flip reveal, screen shake, countdown rhythm
UX retroFeb 22, 2026 · 1 min
A game-feel pass on the web client: a 3-beat countdown between action confirm and reveal, procedural Web Audio sound effects with a mute toggle, a card-flip reveal with screen shake on outcomes, and an animated AI thinking indicator.
Pixel-art fighter system (web + mobile)
UX retroFeb 22, 2026 · 2 min
Built a dedicated pixel-art rendering layer plus per-fighter frame data for 6 fighters, wired it into the live web game UI, and ported the whole system to the React Native mobile client.
PvP realtime backend — WS manager, Redis matchmaking, session loop
UpdateFeb 22, 2026 · 1 min
Built the realtime PvP backbone for Ki Clash: a room-based WebSocket manager, a Redis-backed FIFO matchmaking service, and a WebSocket-driven game session, plus a PvP frontend with matchmaking and game UI.
React Native (Expo) mobile app + full game flow
UpdateFeb 22, 2026 · 1 min
Scaffolded a React Native (Expo) mobile app with TypeScript and expo-router, then built the full game UI, AI/PvP flow, and ported the web 'game feel' features to native.