Data collection
Python pipelines clean season, prediction, salary and salary-cap data before publishing it to Supabase. The site reads those tables instead of scraping data when a page loads.
A personal basketball data project
I wanted to know when NBA players usually peak, so I collected the data and looked at it a few different ways. The project now covers individual career trends, league-wide peak patterns, next-season forecasts and salaries across different cap eras.
01Individual careers · 1976–2026
Search for a player and follow BPM, VORP, win shares, win shares per 48 minutes or PER across their career.
02League-wide patterns · Updated through 2025–26
See when qualifying players reached their strongest consecutive three-season stretch across the same five metrics.
03Contracts by era · 1984-85 onward
Compare contracts across eras using salary as a share of that season’s cap instead of nominal dollars.
How it works
Python pipelines clean season, prediction, salary and salary-cap data before publishing it to Supabase. The site reads those tables instead of scraping data when a page loads.
The pages load data on the server, while API routes handle player search and the salary explorer’s follow-up requests. Larger indexes and aggregate results are cached, and each view only sends the rows it needs to the browser.
The career, peak-age and salary charts are built for this project with Canvas and SVG. Player, metric, team, season and display choices are kept in the URL where it makes sense, so a specific view can be shared.
The analysis defines its eligibility rules and separates observed results from forecasts. The player search, salary calculations, chart helpers, URL state and loading and error states are covered by automated tests.
Next.js and TypeScript on the site, Python for the data pipelines, and Supabase PostgreSQL for the cleaned data.
Next.js · TypeScript · Python · Supabase · PostgreSQL · Canvas · SVG · Vitest