Inspiration

When LeBron left the Lakers this summer, every sports show ran the same segment: "Where should he go next?" Analysts guessed. Fans argued in group chats. ESPN's Trade Machine spun up salary numbers. But nobody could actually show you why a fit would or wouldn't work, they could only tell you.

That gap bugged us. The internet has more NBA data than any human could process, LLMs can now reason about it in real time, and modern web tooling can turn any dataset into something you can touch. So why does every basketball debate still end with "I don't know, feels like he'd fit"?

Fit Check is our answer: the tool we wished existed every time an analyst said "he'd just work better there."

What it does

Fit Check is a lineup-fit simulator for the NBA. You pick any of the 30 teams, drag any player from a pool of 232 onto their court, and instantly see how the lineup changes. A composite fit score updates across four dimensions (spacing, usage overlap, defensive versatility, and positional balance) and the team's projected net rating animates from its baseline to the new value.

Every drop generates a Gemini-powered take: a one-sentence verdict, a "Case For" and "Case Against" with three bullets each, and a shareable hot take. You can stack multiple swaps to fully reimagine a roster, and the fit function recomputes over the entire modified lineup. Scenario chips at the top are dynamically generated per team, suggesting swaps that address the selected team's weakest sub-score plus marquee star trades.

How we built it

The stack is Next.js 14 + TypeScript + Tailwind, with dnd-kit for drag-and-drop, Framer Motion for the animated transitions, and Recharts for the team identity radar. The analytical takes come from Google Gemini 2.0 Flash using structured JSON output so the LLM response reliably maps to UI components.

The biggest architectural decision was pre-computing all NBA data at build time. A Python script hits nba_api once, computes role tags and identity percentiles, and dumps three static JSON files (players.json, teams.json, scenarios.json). The production app never touches the NBA API, it only reads static data and calls Gemini. This meant sub-100ms interactions, zero rate-limit risk, and deterministic Vercel deploys.

The fit function itself is a weighted heuristic: 30% spacing (3PT volume × efficiency, with a nonlinear penalty for stacking non-shooting bigs), 30% usage fit (quadratic penalty above 105% total usage), 25% defensive versatility (with a bonus for having both a rim protector and a perimeter stopper), and 15% positional redundancy. The projected net rating delta is a linear mapping tuned against real trades from last season. Gemini responses are cached in-memory by lineup hash so re-dropping the same player is instant.

Challenges we ran into

nba_api is a liability at runtime. It rate-limits and randomly 403s. Moving to a static build was the single most important pivot of the project.

Getting drag-and-drop, animated re-layout, and net rating counters to cooperate without janky reflows took multiple attempts. The final version uses a single source of truth for lineup state and lets Framer Motion drive all transitions.

Gemini's JSON mode is reliable but not bulletproof. Roughly 1 in 200 responses came back with a subtle schema violation. Every parse is wrapped in a try/catch with a hardcoded fallback so a bad LLM response never breaks the UI.

The hardest question we anticipated was "how do you validate the fit score?" We addressed it by tuning the net rating mapping against real mid-season trades and surfacing a confidence pill (Medium confidence · 2,041 poss.) that honestly signals when we have less lineup data to work with. Showing uncertainty is more credible than pretending you have none.

Accomplishments that we're proud of

The interaction sells itself in 5 seconds. Drop a player, watch the number move, read the take. No onboarding needed.

Gemini's structured output made the analytical layer feel real. Instead of a generic paragraph, every drop produces a punchy verdict grounded in the actual sub-score numbers, plus a Case For/Against that reads like an actual sports debate.

Multi-player swapping works. You can fully rebuild a roster and the fit function recomputes over the entire modified lineup, not just the last change.

The dynamic scenario chips per team turned a static feature into something that adapts. Pick the 76ers and you see shooters; pick the Warriors and you see identity-flipping stars.

Most importantly, Fit Check is opinionated. Every hackathon sports project we saw was a neutral dashboard. Ours produces a take.

What we learned

Static data is a superpower. If a dataset fits in a few hundred KB, ship it as JSON and skip the runtime API entirely. Every interaction gets faster and every deploy gets more reliable.

LLMs are best as structured reasoning layers, not chat interfaces. Gemini isn't the star of Fit Check. It turns four numbers into natural language, and constraining it to JSON schemas made it reliable enough to build a UI around.

Density per pixel beats number of pixels. A single screen that rewards 30 seconds of looking beats five screens that reward none. Every "should we add another view?" impulse got redirected into "how do we make this view richer?"

Knowing what to cut matters as much as knowing what to build. We shipped and then removed a shot-zone heatmap because it fought with the players visually. The replacement (role tags and synergy lines) delivered the same analytical signal with a fraction of the noise.

What's next for Fit Check

Historical fit lookups. Retro-project any trade from the last 20 years. Would peak KD have actually fit on the 2016 Warriors? The math already works; we just need historical roster data.

Salary cap integration. Layer ESPN Trade Machine-style salary matching on top of fit scoring so trades are both good basketball and legal under the CBA.

Full lineup builder. Right now you swap into existing rosters. Let users draft a lineup from scratch, a 5-player fantasy team optimized for fit, not just talent.

Model upgrade. The current heuristic is defensible but coarse. Replace the four-factor weighted average with a lightweight model trained on actual lineup on/off data.

Other leagues. The framework is sport-agnostic. Soccer formations, NFL personnel packages, and cricket batting orders all have "fit" problems that would work with the same drop-and-see-what-breaks interaction.

Built With

Share this project:

Updates

Submission history