KickOff 3D is a 3D browser soccer game where you stake crypto, play 1v1 matches, win KO3D tokens, and mint or trade player NFTs.




# KickOff 3D
KickOff 3D is a 3D browser-based soccer game with single-player AI mode and real-time 1v1 multiplayer, featuring on-chain economics and NFTs. The project is split into three packages: frontend/ (Next.js 16 + React 19 + React Three Fiber + Rapier physics + Convex), contracts/ (Foundry/Solidity), and ws/ (Bun WebSocket server).
Gameplay
A match lasts 90 minutes with teams of 8 players (one goalkeeper). The physics engine uses Rapier and rendering is done with Three.js. The SoccerEngine runs the gameTick (ball, collisions, goals, possession, difficulty) and renders inside a GameCanvas with an HUD for score, timer, goal flash, and possession stats. Screen flow is handled by a Zustand store with three phases: splash (menu), playing, and finished (post-match with confetti and rematch option).
Online mode
The Bun WebSocket server is server-authoritative: clients only send inputs and receive the full GameLoopState (16 players, ball, score, possession) at 20 Hz. Rooms are hard-capped at 2 players; when both join, the match starts. The winner takes the 0.002 ETH pot minus a 5% fee. ETH deposits are currently mocked in the frontend, and the stub server in ws/index.ts is pending full implementation.
Web3
The on-chain stack lives in contracts/ with three Foundry contracts:
- KickOffToken.sol — ERC-20 KO3D, 1B max supply, MINTER_ROLE for reward minting.
- KickOffPlayerNFT.sol — ERC-721 player cards (team, position, rating, stats) with mintPlayerCard and mintBatch.
- KickOffMatch.sol — 1v1 escrow with createMatch → joinMatch → reportResult (with ORACLE_ROLE so the WS server can submit outcomes) → claim. Includes cancelOpenMatch, feeBps config, and time-gate.
The frontend uses MetaMask (walletService.ts) to connect and sign; a WalletGuard blocks match access without a wallet. Default network is local Anvil (chainId 31337).
Extra features
- Market tab: mint NFTs and list them on a marketplace
- Leaderboard: Convex stores results via recordMatch and shows wins per player.
- i18n: i18next localization.
- MCP: the frontend exposes a Streamable HTTP MCP server at /api/mcp for external integrations.
KickOff 3D was built from scratch during the hackathon. No code or assets were imported from a prior project — every file under frontend/, contracts/, ws/, and specs/ was written in this window.
Day-by-day progress
Day 1 — Bootstrap & specs
- Initialized the monorepo layout (frontend/, contracts/, ws/, specs/) and set up Next.js 16 + App Router + Bun.
- Wrote the four product specs in specs/spec{1..4}.md (Menu, Market, Entry Fee & Rewards, WebSocket Multiplayer) — they became the source of truth for everything built afterwards.
- Bootstrapped the SoccerEngine skeleton and Three.js scene-builder stub.
Day 2 — 3D engine & physics
- Implemented the core gameTick() in frontend/src/lib/game/engine.ts: ball physics, 8v8 player kinematics, goalkeeper logic, AI difficulty levels, goal detection, possession %, match timer.
- Integrated @dimforge/rapier3d-compat for collisions; the ball–player and ball–goal interactions were the hardest part to tune.
- Built the Three.js scene in lib/game/scene-builder.ts: pitch, goals, stadium, player meshes, ball trail.
Day 3 — UI shell & game flow
- Implemented the phase router in app/page.tsx (splash → playing → finished) backed by stores/useGameStore.ts.
- Built SplashScreen (main menu, difficulty selector, team display), NavBar (PLAY / LEADERBOARD / WALLET / PROFILE), and PostMatchScreen (confetti, score, rematch).
- Implemented the in-game HUD in GameCanvas.tsx: scoreboard, goal flash, countdown, possession bar, keyboard input.
Day 4 — Web3 wiring
- Connected MetaMask via lib/services/walletService.ts + useWalletStore (address, chainId, balance, wrong-chain detection).
- Added WalletGuard so match entry requires a connected wallet; entry fee (0.001 ETH) is mocked at the UI level for now.
- Hooked up Convex: users upsert on connect, recordMatch on PostMatchScreen, leaderboard query.
Day 5 — Polish, i18n, MCP
- Added i18next with en-US + zh-CN locales and a language switcher in the navbar.
- Exposed a Streamable HTTP MCP server at app/api/mcp/route.ts so external agents can introspect the game.
- Ran bun run lint and bun run build; trimmed every component under the 250-line hard limit defined in frontend/AGENTS.md.
What ships vs. what's next
- Shipped (frontend): playable 3D match vs AI, full menu/leaderboard/wallet UX, Convex-persisted profile & leaderboard, MCP introspection endpoint.
- Stubbed during hackathon, not yet finished: server-authoritative WebSocket multiplayer in ws/index.ts, on-chain KickOffMarket.sol listing flow, real ETH entry-fee settlement. Contracts compile and have 12 passing forge tests; the multiplayer transport is the last open gap.
Highlight
End-to-end 3D browser soccer with Rapier physics, Convex persistence, MetaMask, and an MCP server — all under one repo, all written during the hackathon.
Not fundraising. Project was self-funded and built solo during the hackathon. No outside capital raised, no grants, no token sale, no VC conversations. Open to conversations with strategic partners after the hackathon.