← all work
product★ · live on Laravel Cloud

EasyScores

EasyScores is a mobile-first sports tracker for the NBA, NFL and MLB. It leads with what fans actually check: live scores, linescores, leaders and box scores, standings, and a home screen built around your favorite teams. Heads-Ups surface finals and close games late for the teams you follow, and a bet tracker sits quietly under Extras.

LaravelInertia.jsReactTypeScriptTailwind CSSFortifyPHPStanLaravel Cloud
3 leagues
NBA, NFL and MLB, with a new league one enum case away
1 call
shared across concurrent requests, with stale-on-failure fallback
0 → 1
designed, built & deployed solo

the problem

Mainstream sports apps bury the score under video, news and betting promos. Checking how your team is doing should take one tap, work well on a phone, and not hammer a rate-limited data provider every time someone refreshes.

what i built

A Laravel + Inertia/React app backed by ESPN's public API behind a provider-agnostic SportsProvider interface. A shared cache layer sets lifetimes from the games themselves: live games refresh on a short interval, upcoming games refresh every half hour or so until tip-off to keep odds current, finished data is held for a week. Concurrent requests share one provider call, and the last good response is served if the provider fails. On top of that: favorite teams, team season pages, game detail pages, Heads-Ups, watched games, and a bet tracker, with a bottom tab bar on phones and a sidebar on desktop.

in action

select a screenshot to enlarge it · scroll sideways for more

// cache lifetimes follow the games themselves
foreach ($games as $game) 
{
    if ($game->status->isFinished()) 
    {
        continue;
    }
 
    $start = $game->startTime();
 
    // live, or due to have started: refresh on the live interval
    if ($game->status->isLive() || $start === null || $start->isPast()) 
    {
        return $this->liveTtl();
    }
 
    $nextStart = $nextStart === null ? $start : $nextStart->min($start);
}
 
// nothing live: hold until the next game starts
return $this->untilStart($nextStart);