Live MLB companion app: today's slate with pitch-by-pitch feeds, at-bat outcome probabilities, ballpark spray charts, and team "nerd" standings.
Not affiliated with Major League Baseball.
┌─────────────┐ poll MLB Stats API ┌─────────────┐
│ ingestor │ ──────────────────────────► │ MLB API │
│ (Go) │ └─────────────┘
└──────┬──────┘
│ POST /predict, /predict_steal
▼
┌─────────────┐ predictions ┌─────────────┐
│ ml-engine │ ──────────────────────────► │ Supabase │
│ (Python) │ │ (Postgres) │
└─────────────┘ └──────┬──────┘
│ realtime / REST
▼
┌─────────────┐
│ web │
│ (Next.js) │
└─────────────┘
| Package | Role |
|---|---|
web/ |
Next.js app — live/historical games, spray charts, nerd stats, UI |
ingestor/ |
Go worker — polls live games, calls ml-engine, writes predictions |
ml-engine/ |
sklearn models + HTTP inference (/predict, /predict_steal) |
supabase/ |
Schema migrations + sync-schedule Edge Function |
scripts/ |
Ballpark / season-game fetch helpers |
Predicted at-bat outcomes: strikeout, walk, HBP, single, double, triple, home run, field out, GIDP, sac fly, sac bunt. Steal attempt/success probabilities are modeled separately.
- Node.js 22+
- Go 1.26+
- Python 3.12+
- A Supabase project (Postgres + optional Edge Functions)
The frontend can run against public MLB schedule/feeds without the ingestor or ml-engine. Live prediction overlays need the full stack.
cd web
cp .env.example .env.local # set NEXT_PUBLIC_SUPABASE_* keys
npm install
npm run devOpen http://localhost:3000.
Useful scripts:
npm run lint
npm run typecheck
npm run testcd ml-engine
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txtTrain (needs Supabase credentials in ml-engine/.env for extract steps):
python 04_fetch_player_stats.py
python 01_extract_data.py
python 01b_extract_steal_events.py
python 02_train_model.py
python 02b_train_steal_model.py
python 03_predict.py # sanity-check scenarios
python serve.py # http://127.0.0.1:8765Endpoints:
GET /healthPOST /predict— at-bat outcome probabilitiesPOST /predict_steal— steal attempt / success
Models and parquet data under ml-engine/models/ and ml-engine/data/ are gitignored. For Render deploys, upload at_bat_model.joblib, steal_model.joblib, and player_stats.parquet before first launch (see render.yaml).
cd ingestor
cp .env.example .env # DATABASE_URL and/or SUPABASE_SERVICE_ROLE_KEY
# ML_ENGINE_URL=http://127.0.0.1:8765
go run ./cmd/ingestorBy default it auto-discovers live games from the MLB schedule, polls every few seconds, and persists predictions to Supabase. Set USE_MOCK_PREDICTOR=true to skip ml-engine.
# From repo root, with the Supabase CLI linked to your project
supabase db push
supabase functions deploy sync-scheduleThen follow supabase/setup-cron.sql to schedule schedule sync.
- Web — Vercel (set
NEXT_PUBLIC_SUPABASE_URL,NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY; optionallyML_ENGINE_URL/NEXT_PUBLIC_ML_ENGINE_URL). - ml-engine — Render free tier via Docker (
render.yaml); cold starts can exceed Vercel's proxy timeout, so browser-directNEXT_PUBLIC_ML_ENGINE_URLis useful. - Nerd stats — daily GitHub Action
.github/workflows/daily-nerd-stats.ymlaggregates and commits season counters. - Ballpark / player BIP JSON — season spray data lives under
web/data/ballpark-hitsandweb/data/player-bip. Seedocs/data-storage.mdfor size limits, mobile cost, and why uncapped GitHub JSON breaks Vercel deploys.
mlb-atbat-predictor/
├── web/ # Next.js 16 + React 19 + Tailwind
├── ingestor/ # Go live-game poller
├── ml-engine/ # training pipeline + inference server
├── supabase/ # migrations, Edge Functions, cron setup
├── scripts/ # shared Node data fetchers
├── docs/ # API / fetch notes
└── render.yaml # ml-engine Render service
This project is not affiliated with Major League Baseball. All MLB trademarks, logos, and media remain the property of MLB and its clubs. Predictions are experimental model output for curiosity — not betting advice.
Data & media
- Schedules, live feeds, box scores, and related stats from the public MLB Stats API (
statsapi.mlb.com) - Play video clips via Baseball Savant when a play GUID is available
- Stadium geometry and ballpark context from MLB-published field data and community resources such as GeomMLBStadiums
- Gameday-style images (stadium backgrounds, uniforms) and player headshots from MLB static asset CDNs / MLB.com
Public endpoints are used for informational and educational purposes; accuracy and availability are not guaranteed. Rights holders who need something adjusted can reach out at timberlake2025@gmail.com.
Thanks
Huge thanks to MLB and Baseball Savant for publishing the data that makes this possible, to Bill Dilday and contributors of GeomMLBStadiums, and to the open-source stacks behind the app (Next.js, React, Supabase, scikit-learn, Go, and friends). Built by Kairui — if WTBB is useful, you can buy me a coffee.
MIT © 2026 Kairui Cheng