Real-time AI coaching overlay for League of Legends — runs entirely on your machine, no account required.
| Off-game (ready to coach) | In-game (active session) |
|---|---|
![]() |
![]() |
An AI-powered in-game coaching assistant for League of Legends. A local Node.js core backend reads live game data from the Riot Games Live Client API and streams real-time recommendations to a Flutter desktop app over WebSocket.
The coaching engine is LLM-powered (Claude, OpenAI, or Gemini): it turns your live game state into role-aware, matchup-specific advice — not generic tips. Without an API key the app still runs locally and shows your live scoreboard and game state; item advice requires a configured provider.
| LoL Coach | Generic tier-list sites | In-client coach | |
|---|---|---|---|
| Reads your live game state | ✅ | ❌ | ✅ |
| Role-aware advice | ✅ | ❌ | partial |
| Lane matchup analysis vs. your actual opponent | ✅ | ❌ | ❌ |
| Runs locally — your game data stays on your PC | ✅ | ❌ | ✅ |
| Bring your own LLM key (Claude / OpenAI / Gemini) | ✅ | ❌ | ❌ |
| Works on a second screen / phone | ✅ | — | ❌ |
| Open source, no account | ✅ | ❌ | ❌ |
# 1. Core (run on the same PC as League)
cd core && npm install && npm run dev
# 2. Desktop app (macOS / Windows / Linux)
cd app && flutter pub get && flutter runOpen Settings, enter your Summoner Name and an LLM API key (required for item advice — without one you get live stats only), then start a game.
- Role-aware strategy — advice adapts to your role. A support gets vision-score feedback and ADC-protection cues; a Tryndamere top gets split-push timing, not "group for teamfights".
- Lane matchup analysis — compares your CS, KDA, level and items against your specific lane opponent in real time.
- Concrete counter-play — one actionable sentence: what to do right now against this specific champion.
- Counter-item recommendations — flags Grievous Wounds, Banshee's Veil, QSS, or Randuin's based on the enemy composition across all 170+ champions.
- Multi-provider AI — plug in your own Claude, OpenAI, or Gemini API key. Item advice requires a configured provider; without one the app still shows live game state and the scoreboard.
- Trigger on events — recommendations fire on
GAME_STARTED,ITEM_PURCHASED,PLAYER_DIED, and on manual request via the in-app FAB.
┌──────────────────────────────────────────────────────────┐
│ Your PC (in-game) │
│ │
│ LoL Client ──► Riot Live Client API (port 2999) │
│ │ │
│ ▼ │
│ Node.js Core (TypeScript) │
│ ┌─────────────────────────────────┐ │
│ │ Poller → Parser → EventDetector│ │
│ │ RecommendationEngine + Cache │ │
│ │ StateMinifier │ │
│ │ LLM Provider (Claude/OpenAI/..)│ │
│ │ WebSocket Server (port 8765) │ │
│ └─────────────────────────────────┘ │
└──────────────────────────────────────────────────────────┘
│ WebSocket (LAN)
▼
┌─────────────────────────┐
│ Flutter Desktop App │
│ Live scoreboard │
│ Counter-item picks │
│ Lane matchup analysis │
│ Role-specific strategy │
│ Settings & Connection │
└─────────────────────────┘
| Component | Tech |
|---|---|
| Core | Node.js 18+, TypeScript, Zod, ws, tsyringe (DI), @anthropic-ai/sdk, openai, @google/genai |
| Desktop app | Flutter 3.x, Dart, flutter_riverpod (DI/state), web_socket_channel, flutter_secure_storage, shared_preferences |
| Tests | Jest + ts-jest (core), flutter_test (app) |
Architecture documentation: a full arc42 architecture document covers the building blocks, runtime scenarios, and decisions. Diagrams are authored in PlantUML (
docs/umls/*.puml, rendered to*.svg) and include sequence diagrams for the recommendation pipeline, the WebSocket auth handshake, and the tsyringe DI bootstrap.
- Node.js 18+
- Flutter SDK 3.4+
- League of Legends installed (the core backend reads from the local Live Client API)
cd core
npm installStart the core backend:
npm run dev # development (watch mode)
npm start # production (requires npm run build first)The core backend waits for an active game. Once detected it starts pushing game state and recommendations over WebSocket.
cd app
flutter pub get
flutter runIn the Settings tab configure:
- Your Summoner Name
- The Core IP and Port (default
127.0.0.1:8765) - Your LLM Provider (None, Claude, OpenAI, or Gemini) and API Key
Settings are persisted locally via shared_preferences. Once connected the app switches to the Coach tab automatically.
- Polling — The core polls
https://127.0.0.1:2999/liveclientdata/allgamedataevery second. - Event detection —
EventDetectorcompares consecutive snapshots and emits typed events. - Recommendation engine — On each trigger event
RecommendationEnginerequests an LLM analysis (aCacheServicereuses prior analyses keyed on state + risk level to avoid duplicate calls). Without a configured provider this step is skipped — the app shows live game state only. - State minification — Before calling the LLM,
StateMinifierdistils the game snapshot to CS, vision score, KDA, position, gold and live status for every player — minimising token cost while keeping all coaching-relevant data. - Role-aware LLM analysis — The LLM receives the minified state, the player's role, their lane opponent's stats, and role-specific instructions. It returns a structured JSON with
winCondition,immediateAction,lateGamePlan,laneMatchupAnalysisandcounterPlay. Returned item ids are validated against Data Dragon and owned items to drop hallucinated or duplicate picks. - WebSocket broadcast — Events and recommendations are pushed to all connected Flutter clients as JSON.
- Session token budget — An optional input-token cap (Session Token Budget in Settings; default: unlimited) guards against runaway API spend. Once the cumulative session input-token count reaches the cap, the core emits
LLM_BUDGET_EXCEEDEDand skips further LLM calls, and the token-usage progress bar in the Recommendation panel turns red. The budget resets when a new game session starts. - Local-only security — The core binds to
127.0.0.1and verifies every WebSocket upgrade with a shared secret auto-generated at~/.lolcoach/.secret. The Flutter app reads the same file from disk; connections from other machines or without the correct token are rejected.
lolclient/
├── core/ # Backend (Node.js/TypeScript)
│ ├── src/
│ │ ├── types.ts # Zod schemas + TypeScript types
│ │ ├── poller.ts # Live Client API polling
│ │ ├── parser.ts # Raw data → ParsedGameState
│ │ ├── eventDetector.ts # State-change event detection
│ │ ├── stateMinifier.ts # Token optimisation for LLM
│ │ ├── llmProvider.ts # AI factory + prompt builder
│ │ ├── recommendationEngine.ts # LLM analysis + cache + item validation
│ │ ├── heuristic.ts # Legacy rule-based engine (not in runtime path)
│ │ ├── orchestrator.ts # Wires events → recommendations → broadcast
│ │ ├── wsServer.ts # WebSocket server
│ │ ├── data/
│ │ │ ├── champions.json # AP / CC / healer classification (~170 champs)
│ │ │ └── items.json # Item ID → name mapping
│ │ └── index.ts # Entry point / DI wiring
│ └── src/__tests__/ # Jest unit + integration + E2E tests
│
├── app/ # UI (Flutter)
│ ├── lib/
│ │ ├── models/ # Data models (Strategy, ItemRecommendation, …)
│ │ ├── services/ # CoachService + WsClient (ChangeNotifier)
│ │ ├── providers.dart # riverpod provider definitions (DI / lifecycle)
│ │ ├── screens/ # MainScreen, HomeScreen (Coach tab)
│ │ └── widgets/ # RecommendationPanel, GameView, ConnectionForm
│ └── test/ # Widget, unit and golden tests
│
├── skills/ # Agent Skills
│ ├── lolcoach-agents/ # Primary vendor-neutral skill
│ ├── lolcoach-claude/ # Claude-specific rules
│ └── lolcoach-gemini/ # Gemini-specific rules
│
└── deployment/ # Build & packaging scripts
├── build_unix.sh # macOS + Linux build script
└── build_installer.ps1 # Windows MSIX build script
./test.sh # core + app (goldens excluded)
./test.sh --coverage # core tests with coverage report
./test.sh --goldens # include app golden pixel tests
./test.sh --update-goldens # regenerate golden baselines, then run all
./test.sh --core-only # skip app
./test.sh --app-only # skip core
./test.sh --watch # jest watch mode (app skipped)cd core
npx stryker run
# Report: core/reports/mutation/report.htmlcd app
flutter test # unit + widget tests
flutter test --update-goldens test/widgets/recommendation_panel_golden_test.dart
# regenerate golden baselinesGolden tests require a baseline PNG committed under
app/test/goldens/. Run--update-goldensonce and commit the generated files before golden tests will pass.
This project is set up with agent skills to enforce architecture and quality rules for all AI assistants.
Skills live in .claude/commands/ and are committed to the repo, so every contributor gets them automatically.
| Skill | What it does |
|---|---|
/test |
Runs the full suite — flutter analyze → app tests → core tests. Accepts the same flags as test.sh (e.g. /test --app-only). |
The hook runs flutter analyze before every commit that touches app/ (~4 s). It mirrors the CI analyze step so lint errors never reach the pipeline.
After a fresh clone, install it once:
cp scripts/hooks/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commitThe hook is intentionally lightweight. It only runs analyze, not the full test suite, so commits stay fast.
All rules live in the lolcoach-agents skill under Conventions. AI assistants apply them without being asked.
| Rule | Detail |
|---|---|
| Commit messages | Conventional Commits in English — type(scope): summary |
| Test names | Microsoft naming — Subject_StateUnderTest_ExpectedBehaviour |
| Test structure | AAA pattern — Arrange / Act / Assert with a blank line between each phase |
| File | Purpose |
|---|---|
lolcoach-agents |
Vendor-neutral context — architecture, conventions, quality gates. Read by all AI assistants. |
lolcoach-claude |
Claude-specific settings — model defaults, response style, commit rules. |
lolcoach-gemini |
Gemini-specific overrides. |
MIT © 2025 Suhay Sevinc

