The sideline rotation manager for youth soccer coaches.
A free, open-source PWA — no backend, no accounts, all data on your device.
Try the Live App · Features · Screenshots · Getting Started
Get BenchAssist — Open the live app on your phone, tap Share → Add to Home Screen. No app store needed.
Live game day — track rotations, plan practices, all from the sideline.
| Rotation Grid | Team Management |
|---|---|
![]() |
![]() |
Smart Rotation Solver — Two-tier solver runs in a Web Worker: MIP solver (HiGHS WASM) for bench assignment, then exhaustive search for position optimization. Respects constraints: no consecutive bench, minimum play time, goalie rest, and skill balance.
Coach Plan — Manually build rotation plans with a direct-entry matrix. Lock cells as hard (must satisfy) or soft (preferred, relaxable) constraints that the solver honors.
Playtime Optimization — Post-solve analysis detects when adjusting period divisions could improve play-time balance by 5%+ and suggests optimized configurations.
Live Game Mode — Period timers, audio substitution alerts, Now/Next focus view. Add or remove players mid-game with automatic re-solving. Regenerate with lock policy control and preview before applying.
Position-Aware Scheduling — 14 sub-positions across 4 position groups. Players auto-assign to formation slots based on their preferences.
Practice Planner — ~100 curated drills filtered by age bracket (U6–U18). Generate plans by category, duration, and player count. Swap drills and save favorites.
Team & Roster Management — Bulk-import players, track skill rankings, goalie eligibility, and preferred positions.
Bilingual — Full English and Spanish (es-MX) localization.
Data Portability — Selective export/import, schema migrations, and 8-second undo on all destructive actions. All data stays on your device.
git clone https://github.com/matthewevans/benchassist.git
cd benchassist
pnpm install
pnpm devOpen localhost:5173 in your browser.
All Commands
| Command | Description |
|---|---|
pnpm dev |
Start Vite dev server with HMR |
pnpm build |
Type-check + production build |
pnpm test |
Run Vitest in watch mode |
pnpm test:run |
Run tests once (CI mode) |
pnpm lint |
Run ESLint |
pnpm format |
Format all files with Prettier |
pnpm typecheck |
Type-check without emitting |
| Layer | Technology |
|---|---|
| Framework | React 19, TypeScript 5.9, Vite 7 |
| Styling | Tailwind CSS v4, shadcn/ui (New York), Radix UI |
| State | useReducer + Immer (single context, discriminated-union actions) |
| Routing | React Router v7 |
| Solver | Web Worker — MIP (HiGHS WASM) + exhaustive search |
| i18n | i18next + react-i18next (en, es-MX) |
| Testing | Vitest, Testing Library, fishery factories |
| PWA | vite-plugin-pwa, auto-update, standalone display |
| Quality | ESLint, Prettier, Husky + lint-staged |
All app state flows through a single useReducer in AppContext with Immer for immutable updates. The reducer uses a discriminated union of action types with an exhaustive never check. State auto-persists to localStorage with a 500ms debounce.
The rotation solver runs in a Web Worker using a two-tier approach: a MIP solver (HiGHS WASM) determines bench assignments, then exhaustive search refines position placement and scores by team-strength variance. After solving, an optimization check evaluates whether adjusting period divisions could improve play-time balance. Mid-game re-solves preserve existing rotations and support constraint relaxation fallback.
The undo system wraps the reducer with Immer's produceWithPatches, capturing inverse patches for destructive actions (delete, import, merge). A ref-based stack holds up to 30 undo entries.
Project Structure
src/
├── components/ # React components
│ ├── ui/ # shadcn/ui primitives
│ ├── game/ # Game-specific components
│ └── layout/ # App shell & navigation
├── context/ # AppContext (single reducer + Immer)
├── data/ # Drill library (~100 entries)
├── hooks/ # Custom hooks (solver, timer, undo, etc.)
├── i18n/ # i18next config + locale JSON files
├── pages/ # Route-level page components (lazy-loaded)
├── storage/ # localStorage persistence & export/import
├── types/ # Domain model, solver messages, drill types
├── utils/ # Stats, validation, positions, optimization, etc.
└── workers/ # Web Worker + solver modules (MIP, exhaustive)
Contributions are welcome! Please open an issue first to discuss what you'd like to change.
- Fork the repo
- Create a feature branch (
git checkout -b feature/my-change) - Make your changes and add tests
- Run
pnpm test:run && pnpm lint && pnpm buildto verify - Open a Pull Request
MIT — Matt Evans




