StatScout icon

StatScout

TestFlight

Statcast-style player cards, leaderboards, and search for iOS. Mobile-friendly Baseball Savant percentile bars with nightly Python ingestion from pybaseball.

StatScout player card with percentile bars

The pitch

Baseball Savant is incredible on desktop, but unusable on mobile. The percentile bars don't fit, the tables require horizontal scrolling, and search is buried under menus.

StatScout brings the Savant experience to iPhone: player search in two taps, percentile bars optimized for portrait, leaderboards that actually scroll smoothly. Built for checking stats at the ballpark.

Specs

Platform iOS 17+
Language Swift
Data Pipeline Python + pybaseball → Supabase, nightly GitHub Actions
Backend Supabase Postgres with search indexes
Data Coverage All MLB players, 2020-2025 seasons, 50+ stat categories
Percentiles Calculated against positional averages (min 100 PA/IP)

What mattered

Mobile-first percentile bars. Savant's bars are designed for desktop width. I redid the component for portrait: stacked when needed, compact labels, touch-friendly targets. Same data density, different geometry.

Fuzzy search. Type "judge" and get Aaron Judge. Type "shohei" and get Ohtani. Handles nicknames, misspellings, and partial matches. Search is local after initial sync. Works offline at the stadium.

Nightly data refresh. A Python script runs every night via GitHub Actions, pulls fresh data from pybaseball, calculates percentiles, and upserts to Supabase. The iOS app pulls incrementally. No full refresh unless the schema changes.

Leaderboard performance. Lists are virtualized with SwiftUI's LazyVStack. 500+ players in a list scrolls at 60fps. Images are lazy-loaded with a tiny LRU cache. First paint is under 2 seconds on cellular.

Positional context. A shortstop's 90th percentile OPS is different from a first baseman's. All percentiles are calculated within position groups, not league-wide. The app knows you're looking at a defender vs. a slugger.

The best baseball stats app is one that doesn't make you wait for the website to load.