SaberIQa thing I built
AI & Automation · a thing I built

SaberIQ

Most stats apps dump numbers. This one explains them— because I wanted my nephew to actually understand the game.

SaberIQ is a full-stack sabermetrics app with Claude built into every data view. It doesn’t just show you a player’s BABIP or xFIP — it tells you what those numbers mean for this player, right now. A real app, built solo, for one very specific user first.

numbers → understanding
BABIP.3612025 · qualifiedCLAUDE EXPLAINS.361 is well above leagueaverage — some of this hotstreak is luck, and likely tocool as the season goes on.for THIS player · right now
AI-in-productFull-stack · soloNext.js · Claude APISupabase · VercelSabermetricsWCAG 2.1 AA
01Why I built it

It started with wanting to teach my nephew the game.

I love baseball — not just the box score, the subtleties: why a pitcher’s ERA lies, why a hitter on a heater is about to cool off, what the numbers are quietly telling you. I wanted my nephew to see those things too. But every stats site I’d point him to just threw numbers at him with no story.

So I built the thing I wished existed: a stats app where every number explains itself, in plain language, for the exact player and moment you’re looking at.

This is the whole pattern of how I build — I needed something, nothing did it well, so I made it. This one just happened to come from love of the game.

02The problem

A number without a story doesn’t teach anyone anything.

Sabermetrics is full of meaning that a raw figure hides. BABIP tells you whether a hot streak is real or luck. An xFIP that diverges from ERA says a pitcher is over- or under-performing his actual command. Strand rate hints at regression coming. But you only learn that if someone explains it — and no dashboard does.

Every other stats app

Here’s a table of numbers. Good luck. The meaning lives in the reader’s head, or nowhere.

What I wanted instead

Here’s the number — and here’s what it means for this player, in language a curious kid (or anyone) can follow.

03What it does

Claude, inline in every view — five ways.

The AI isn’t a chatbot bolted on the side. It’s woven into the product as five distinct, context-aware explainers, each server-side so the model always has the real data.

saberiq.app/player/bobby-witt-jr
SaberIQ
PlayersLiveCompareFantasy
BW
Bobby Witt Jr.
SS · Kansas City Royals · 2025 · qualified
WAR 9.4 · elite
AVG
.332
7th, AL
wRC+
168
68% above avg
BABIP
.358
tap to read
xwOBA
.402
backs the bat
K%
18.1%
below league
Claude · BABIPfor THIS player · right now
A .358 BABIP sits well above the ~.300 league norm. Some is genuinely earned — his xwOBA (.402) and top-scale sprint speed say the loud contact and infield hits are real. But a slice is batted-ball luck that tends to regress. Read: expect a modest cooldown, not a cliff — the floor stays high because the underlying contact quality is legitimate.
EXPLAINER 01Stat explainer

Every metric explains itself.

Tap any number on a player page and Claude reads it for thisplayer’s actual season — grounded in the real stat line, never a generic definition. Here a .358 BABIP gets the honest verdict: partly earned, partly luck, a modest cooldown coming — not a cliff.

EXPLAINER 02At-bat predictor

What’s likely to happen — and why.

For the matchup in front of you, the model lays out the probable outcomes, the pitcher’s put-away pitch and location, and the reasoning behind the edge. Skubal ahead 1–2 goes upstairs; Judge chases when he’s behind. The numbers become a scouting read.

saberiq.app/live/at-bat
SaberIQ
PlayersLiveCompareFantasy
TS
PITCHER
Tarik Skubal LHP · DET
1–2 · 2 out · bases empty
BATTER
RF · NYY Aaron Judge
AJ
LIKELY OUTCOME — THIS AT-BAT
Strikeout
36%
Groundout
16%
Flyout
19%
Hit
24%
Walk
5%
PUT-AWAY PITCH
catcher view
high four-seam, away
Claude · matchupfor THIS player · right now
Ahead 1–2, Skubal goes to the elevated four-seam— his best whiff pitch, and Judge’s chase rate climbs when he’s behind. The changeup down-and-away is the counter if Judge lays off. Edge: pitcher — but Judge only needs one mistake over the plate to flip the inning.
saberiq.app/live/manager
SaberIQ
PlayersLiveCompareFantasy
HOU 3 · NYY 2
Bot 7 · 2 out · runners on 1st & 2nd
Balls
Strikes
THE DECISION
Starter at 94 pitches. Stay in, or go to the lefty for the LHB due up?
Stay inGo to pen ✓
Claude · manager advisorfor THIS player · right now
Three things point to the bullpen: the starter’s four-seam velo is down 1.6 mph since the 5th, the due batter is 4-for-8 career off him, and your lefty holds LHBs to a .210 wOBA. The platoon edge plus the fatigue signal outweigh riding the hot hand. Recommend: go to the pen.
EXPLAINER 03Manager advisor

Talk through the decision in front of you.

A live game-manager mode that reads the moment: score, base-out state, the count (rendered as an accessible role="meter" gauge), and the call. Stay with the tiring starter, or go to the lefty? Claude weighs velo drop, the platoon split, and career history, then commits to a recommendation.

EXPLAINER 04Compare narrative

Two players, and the story of how they differ.

Line two players up and the numbers get a narrator. Witt and Henderson are both elite young shortstops — but one wins on contact and speed, the other on power and plate discipline. The comparison ends with which build fits your league, not just who has the bigger number.

saberiq.app/compare
SaberIQ
PlayersLiveCompareFantasy
Bobby Witt Jr.
SS · KC
BW
vs
GH
Gunnar Henderson
SS · BAL
168
wRC+
155
32
HR
37
31
SB
21
8.4%
BB%
12.1%
9.4
WAR
8.6
Claude · how they differfor THIS player · right now
Two elite young shortstops who get there different ways. Witt wins on pure contact and legs — higher average, more steals, elite BABIP. Henderson wins on power and discipline — more homers, a much better walk rate, bigger damage vs RHP. For a steals-and-average build, Witt; for an OBP or power league, Henderson edges it.
saberiq.app/fantasy/start-sit
SaberIQ
PlayersLiveCompareFantasy
Tonight’s lineup call
Yahoo Fantasy · connected7 games · lock 7:05 ET
Start/SitRosterMatchupsWaivers
BB
SS
Bo Bichette
@ COL · RHP
Coors Field + platoon edge vs RHP — smash spot
START
AS
OF
A. Santander
vs LHP
career .680 OPS vs lefties — sit the platoon
SIT
KM
2B
Ketel Marte
vs RHP
hot 14-day xwOBA, favorable park factor
START
CS
SP
C. Sánchez
@ MIA
weak lineup, GB-heavy — safe ratios + Ks
START
Claude · the swap that mattersfor THIS player · right now
The call that moves your night: bench Santander, start Bichette. Santander into a lefty is a known hole (.680 career OPS vs LHP); Bichette gets Coors + a RHPhe’s historically hit. Everyone else is a straightforward start. One swap, real edge.
EXPLAINER 05Start / sit advisor

A real call on your real roster.

Wired to Yahoo Fantasy over OAuth, it reads tonight’s slate against your actual lineup and makes the start/sit calls — with the one swap that moves your night surfaced first. Bench the lefty-killer’s bad platoon spot; start the bat headed into Coors. One edge, clearly reasoned.

04How I built it

The AI teaches. The judgment is still mine.

The interesting part isn’t that it calls an API — it’s the guardrails around it. The stack:

Next.js 15TypeScriptSupabaseMLB Stats APIClaude claude-sonnet

Context, not vibes

Every AI route runs server-side with the real stat line in hand, so the explanation is grounded in this player’s actual numbers — never a generic definition.

Keys never leak

All five Claude routes live in one server module; the API key is never exposed to the client.

Runs on nothing

A full mock mode returns canned data with zero credentials — so the app boots and demos without a single real key. That’s an engineering choice, not an accident.

Debugged in the weeds

A Node.js 25 localStoragebug 500’d every page; I traced it to auth loading at module time and fixed it with a dynamic import + a polyfill.

05What it demonstrates

AI as the teaching layer — on a real, accessible, shipped app.

SaberIQ is a whole product I designed and built solo: auth, database, a third-party OAuth integration, a live-game mode, and an AI layer that has a job to do rather than a demo to give.

And it’s accessible on purpose — a full WCAG 2.1 AA pass, not a checkbox.
Combobox ARIA on searchTablist semantics on the Fantasy Hubrole="meter" on the ball/strike countLive regions on every async view

Status: built and UAT-complete, not yet public — it’s waiting on a domain connection before I open it up. I’m not going to call it “launched” until it actually is.

The point

The tools got faster. The love of the game is why I built it.

A hiring manager can read this as AI-in-product done with judgment. I read it as the app that finally lets me watch a game with my nephew and explain what’s really happening. Both are true — and that’s exactly how I like to build.

← How I approach AI work