Personal Project · Not Client Work 2024–2025 AI Product Design · Builder

Lineara: built with AI, not generated by it.

Every fantasy tool shows you a ranked list and calls it advice. Lineara re-scores the entire board on every pick, tells you exactly why a player is worth taking right now, and adapts to your league format, your roster, and what your opponents are actually doing.

RoleDesigner, product thinker, builder
Built withChatGPT · Claude · Claude Code
StackReact · Vite · Tailwind · Zustand · Sleeper API
StatusLive beta · lineara.me ↗
→ What it doesOne core thing, done well

A live draft co-pilot that re-ranks the board on every single pick.

The core feature

Real-time draft recommendations that know the board.

Every pick made by anyone in the draft triggers a full recalculation. Five prioritized recommendations update immediately, each with a plain-language explanation of why that player is worth taking right now: survival probability, tier cliff, positional scarcity, roster fit, value vs. ADP. Not a cached ranking list. Not generic advice. A read on this specific moment in this specific draft.

Connects to

Sleeper API, live draft polling

Signals per pick

6 scoring signals recalculated

Recommendations

5 ranked picks with written reasoning

Formats supported

Snake, Superflex, Best Ball, Guillotine, IDP, Dynasty, Auction

Also included Mock Draft with AI opponents

Full simulated draft against opponents that blend best-player-available and needs-based logic by round. Opponents roster-check before picking. Full AI Coach active throughout so you can practice with the same recommendations you'd see in a live draft.

Also included Try it without an account

Three demo states load with sample data before you connect Sleeper: Startup Draft (board mid-draft with AI Coach active), Rookie Draft, and Post-Draft Hub. So you can see the full experience before linking anything.

After the draft Post-Draft Hub and in-season tools

Team grade and projected points breakdown when the draft ends. Start/Sit, Waiver Wire, Trade Evaluator, and News pages extend the tool into the regular season. Pick grader shows letter grades on every selection in real time.

Draft page, live Sleeper draft, AI Coach open

Live Last: A. Cooper WR · 1:42 remaining
Snake · 12-team · PPR
Mock Draft ×
YOUR PICK #23
Ignore:
AI COACH 4 picks until you're up Use ⌘ to condense
1
Top Pick
94% confidence

Brian Thomas

WR JAXAI #73 OVRWR17Age 23

Brian Thomas projects 12.8 ppg (floor 10.4, ceiling 15), VORP +3.6, 0.7 ppg clear of next available WR. Only 36% survival odds through 4 picks. Get ahead of the run.

PROJ
12.8
10.4-15
VORP
+3.6
ADP
#98
SURV
36%
avail
2
Best Player Available

George Kittle

TE SF · #122 · TE2

15.9 ppg (floor 12.6, ceiling 18.9), VORP +7, 2.7 ppg clear of next available TE. Your RB/WR room is covered.

Proj / VORP
15.9 +7
Surv
75%
3
Draft Now

Marvin Harrison

WR ARI · #183 · WR31

9.9 ppg (floor 8, ceiling 11.6), VORP +0.7. Only 49% survival odds through 4 picks.

Proj / VORP
9.9 +0.7
Surv
49%
4
TE5 Alt

Dallas Goedert

TE PHI · #116 · TE5

13.2 ppg (floor 10.4, ceiling 15.7), VORP +4.3. Fallback if the top 4 TEs go first.

Proj / VORP
13.2 +4.3
Surv
69%
5
WR23 Alt

Romeo Doubs

WR NE · #142 · WR23

12.1 ppg (floor 9.8, ceiling 14.2), VORP +2.9. Fallback if the top 22 WRs go first.

Proj / VORP
12.1 +2.9
Surv
91%
6 intel · TE run: 3 of last 12 picks
Draft Board
SCARCITY QB0/12 RB0/24 WR0/24 TE1/12
▼ Show
Position
NFL Team
All Teams ▾
Show
Available ▾
Sort By
AI Ranking ▾
Team Needs
K
DEF
Rankings
AI Ranking
Sleeper
Personalized for Your Roster · How it works?
FIT/AI
Player
Surv
Bye
VORP
1/73
Brian Thomas
WR JAX · WR17 · -12 vs ADP
36%
7
+3.6
2/29
George Kittle -24 vs ADP IR
TE SF · TE2
75%
9
+7.0
3/69
Dallas Goedert -18 vs ADP
TE PHI · TE5
69%
5
+4.3
4/111
Marvin Harrison -5 vs ADP
WR ARI · WR31
49%
11
+0.7
5/89
Romeo Doubs -44 vs ADP
WR NE · WR23
91%
14
+2.9
6/74
Travis Kelce -33 vs ADP
TE KC · TE7
80%
12
+4.0
7/115
Chris Godwin -11 vs ADP
WR TB · WR34
63%
11
-9.1
8/93
Mark Andrews -20 vs ADP
TE BAL · TE11
72%
14
+2.0
9/99
Jake Ferguson -21 vs ADP
TE DAL · TE12
73%
7
+1.6
10/125
Alec Pierce -12 vs ADP
WR IND · WR37
65%
14
-0.8
11/71
Aaron Jones -8 vs ADP
RB MIN · RB29
59%
6
+4.2
12/106
Brenton Strange -30 vs ADP
TE JAX · TE15
79%
7
+1.2
My Team 8 / 16
PROJ
109.4
VORP
+50.8
GRADE
A+
Roster
Watch
Log
Grades
QB Trevor Lawrence 20.4
RB1 Jonathan Taylor 21.6
RB2 Omarion Hampton 15.4
WR1 Jaylen Waddle 9.5
WR2 Jameson Williams 11.3
TE Tucker Kraft 13.6
FLX Josh Jacobs 17.6
K Empty
DEF Empty
BN1 Jared Goff 20.3
● Live · 2026 season
LINEARA
§01The problem

Rankings show you who’s good. They don’t show you whether to take them at this pick.

The same player can be a must-take or completely safe to wait on depending entirely on board context: how many picks until you're up, whether teams between now and your next pick all need his position, and whether he's the last player in his tier before a quality drop. None of that is in the rank or the ADP.

Every existing fantasy tool has the same gap: a list sorted before the draft started that doesn't update based on what's happening in the room. Lineara recalculates the whole board on every pick using live board state, and explains the reasoning in plain language.

§02How it was built

Three tools. Different jobs. None of it was one-shot.

The scoring engine started in conversations with ChatGPT and Claude before any code existed. ChatGPT for rapid ideation, throwing draft theories at something that could push back. Claude for stress-testing the logic, challenging edge cases, and working through why a resistance function was the right answer when multiple urgency signals fire at once. By the time the model went into Claude Code, the architecture was decided.

Claude Code's job was to build what was already designed. Every output got reviewed against the model. If survival probability was overcorrecting or need urgency fired at full strength on pick 1, that was a bug, not a design discovery. The logic came first.

Built over a few weeks of focused sessions. Format differentiation (dynasty, best ball, guillotine all have meaningfully different math) each required separate research passes before they could be specified for implementation. The dual ranking system (AI Rank, stable VORP, and Fit Rank, personalized and live-updating) came out of the same phase.

Claude Code session: implementing the scoring architecture

Conversation
MD
You

here's the scoring structure. base is VORP. multiply by survival urgency. need boost scales by how far behind you are vs. the rest of the draft, not just whether you're empty at a position. hard cap total context at +45%. build this out and show me the need urgency piece specifically

C
Claude

Built. The fill-gap approach means urgencyScale is 0 when everyone in the draft is equally empty (pick 1, round 1), and approaches 1.0 only when you're meaningfully behind the field at a position. That prevents full urgency from firing on your first pick just because you have zero RBs.

const myFill   = (myPosCounts[pos] || 0) / Math.max(1, starterSlots)
const fillGap   = Math.max(0, sc.fillPct - myFill)
const urgScale  = Math.min(1.0, fillGap * 4 + (atRisk ? 0.35 : 0))
const baseBoost = urgency === 'critical' ? 0.18
  : urgency === 'high' ? 0.10 : 0.04
score *= 1.0 + baseBoost * urgScale
MD
You

verify the math: urgencyScale should actually return 0 on pick 1 when nobody has drafted anything yet

C
Claude

Confirmed. Pick 1, all teams equally empty: myFill = 0, sc.fillPct = 0, so fillGap = max(0, 0 - 0) = 0. atRisk is false. urgencyScale = min(1.0, 0 + 0) = 0. The boost collapses to 1.0 + (0.18 * 0) = 1.0. No adjustment applied. Matches the spec.

AICoach.jsx (actual source)
// Phase 1: VORP as the base quality signal.
// Shift up so all top-80 players have a positive base.
let score = Math.max(1, (proj.vorp ?? -(player.rank / 20)) + 15)

// Dynasty / keeper: age curve + mileage + multi-year blend
if (isDynastyMode(settings.leagueType)) {
  score *= dynastyAgeMult(player, seasonStats, prevSeasonStats, teamMode)
}

// Survival urgency: 1.0x (safe) up to 1.35x (almost gone)
score *= 1.0 + 0.35 * (1 - pSurvive)

// Need boost only when you're behind the field.
// urgencyScale = 0 when everyone equally empty (pick 1).
if (need) {
  const myFill   = (myPosCounts[pos] || 0) / Math.max(1, starterSlots)
  const fillGap   = Math.max(0, sc.fillPct - myFill)
  const urgScale  = Math.min(1.0, fillGap * 4 + (atRisk ? 0.35 : 0))
  const baseBoost = urgency === 'critical' ? 0.18
    : urgency === 'high' ? 0.10 : 0.04
  score *= 1.0 + baseBoost * urgScale
}

// Phase 3: saturation. Too many at one position.
// Penalty scales with how rigid the roster format is.
if      (alreadyHave >= starterSlots + 2) score *= 1 - 0.65 * rigidity
else if (alreadyHave >= starterSlots + 1) score *= 1 - 0.38 * rigidity
else if (alreadyHave >= starterSlots)     score *= 1 - 0.15 * rigidity
§03The scoring engine

500+ lines. Three phases. Built to not lie to you.

All six signals could produce runaway recommendations unconstrained. A player might be last in his tier, in a positional run, with 25% survival, and behind on your roster. Every signal firing at once. Without a check, the engine recommends a mediocre player because circumstances feel urgent. The resistance function and ±45% hard cap prevent that: context tells you someone is urgent, not that someone bad is suddenly good.

Phase 1 (VORP) sets the floor. Phase 2 (context signals) adjusts within the cap, with resistance dampening boosts when projection lags behind best available. Phase 3 (saturation) penalizes overloading a position, scaled to how many flex slots the format allows.

How the engine changes by format

Best Ball rewards ceiling since optimal lineups are auto-set. Guillotine punishes inconsistency since one bad week eliminates you. Dynasty applies age curves: a 23-year-old and 28-year-old with identical projections are not worth the same capital. Auction replaces survival probability with Fair Market Value and VORP-per-dollar efficiency. The format grid below has the full breakdown.

The dual ranking system: AI Rank and Fit Rank

The board starts in pure AI Rank, a stable VORP-based ordering that doesn't change as picks happen. The moment you make your first pick, it switches to Fit Rank, reordering around what your roster actually needs, which positions are becoming scarce, and who gets harder to land as the draft progresses. Same engine, two outputs: one objective, one personalized.

FIT / AI
1 Fit rank: personalized position based on your roster needs, scarcity, and positional value.
3 AI rank: stable overall rank based on VORP, dynasty age curve, and scoring format. Doesn't change as picks happen.

Rookies show instead of a fit rank. They're slotted where Sleeper's dynasty community ranks them since they have no stats to project from.

The six signals, built up one real problem at a time

VORP

The only starting point that makes sense

Raw points don't work: a 14-point WR2 is more valuable than a 14-point QB5 because replacement cost is completely different. VORP anchors every base score to how much better a player is than what you'd get off waivers. Everything else is a multiplier on this number.

Survival

A statistical model, not a guess

Normal distribution centered on consensus rank, with position-specific volatility coefficients: QB 1.22 (goes at wildly different points by league), TE 0.90 (premium or streamer, little in between), WR 1.00 as baseline. CDF computed inline via Abramowitz & Stegun polynomial with no library dependency. Opponent demand and position run pressure layer on top.

Opponent demand

Who's actually pulling on the pool

The model reconstructs every roster from the draft log and estimates how many starter slots each team still needs per position. A team needing two RBs pulls harder than a team needing one. This separates survival probability from a simple ADP offset: it's the real pressure in this room, not a historical average.

Position runs

12-pick window, recency-weighted

Tracks pick rate over the last 12 selections, with the most recent four counting 1.5x. Compared against baseline rates (RB: 0.28, WR: 0.34, QB: 0.13, TE: 0.13) to distinguish real momentum from normal clustering. When a position is being drafted above its baseline rate, urgency fires, catching things opponent demand and ADP miss entirely.

Tier cliffs

Being last in a tier is a different kind of risk

Players cluster into quality tiers with real drop-offs between them. The last player in a tier faces concentrated demand that generic survival probability doesn't capture. Everyone in the room sees the same cliff. When a player is last or second-to-last in his tier, the model fires a separate urgency signal.

Forward scarcity

Not just this player. The whole position at your next pick.

Survival asks "will this player be there?" Forward scarcity asks "if I pass, how many comparable options exist at my next pick?" The engine runs survival probability against every starter-quality player at the position and sums expected survivors. Fewer than 1.5 fires a moderate signal. Fewer than 0.5 fires a hard one.

The in-app "How the AI works" modal, verbatim

The app ships with a modal that explains the scoring logic per format. It opens from the setup screen before you connect your account. These are the exact points, pulled from the source.

Standard Snake Default

The default format. Teams alternate pick direction each round. The largest player pool, the most balanced scoring signals.

1

VORP engine

Every player is scored by Value Over Replacement, measuring how much they outscore the last draftable starter at their position.

2

Survival probability

Predicts the chance a player is still available at your next pick using their ADP, positional volatility, and opponent demand.

3

Tier cliffs

When you're at the last player in a tier before a quality drop, urgency spikes so you don't miss the window.

4

Positional runs

Detects when a run is happening at a position in real time and adjusts urgency accordingly.

Superflex / 2QB QB-heavy

An extra starting slot that can be filled by any QB. Changes the value hierarchy of every position from pick one onward.

1

QB demand scaling

SFLEX leagues need ×1.82 the QBs of a standard league; 2QB leagues need ×2.08. Replacement level is recalibrated accordingly.

2

Dynamic urgency

QB urgency is critical with no QB, high once you're set, and medium with two solid starters. No irrational panic.

3

Hoarding limits

The engine suppresses QB picks after QB4 in SFLEX (QB5 in deep leagues) so elite skill players are never overshadowed by backup QBs.

4

Replacement collapse

Once the top 24 QBs are off the board in a 2QB league, value drops sharply. The engine knows this cliff is coming.

Best Ball Auto-lineup

No weekly lineup decisions. Your best available starters are auto-set each week, so ceiling and variance are assets, not problems.

1

Ceiling contribution

Scores players by their average of top-8 weekly performances, reflecting what they contribute when auto-started in an optimal lineup.

2

Spike week rate

Players who score ≥120% of their average in a given week get a volatility bonus (up to ×1.14). Boom-bust is rewarded, not penalized.

3

Reduced depth penalty

Owning 5+ WRs is good, not wasteful. Saturation penalties are cut nearly in half vs. managed leagues.

4

Stack bonus

Drafting a QB's WR1 gets a ×1.08 correlation boost; WR2/TE from the same QB get ×1.05. Total stack boost capped at ×1.10.

Guillotine / Chopped Survival

The lowest-scoring team gets eliminated each week. One bad week, a bad injury, or a concentrated bye week can end your season.

1

Floor over ceiling

Consistency multipliers up to ×1.14 for stable volume players. High boom/bust gets penalized ×0.92. You can't afford a zero week.

2

Bye week penalty

2 starters sharing a bye: ×0.96. 3+ starters: ×0.90. The engine avoids building you a bye-week timebomb.

3

Stricter injury math

Out players get ×0.50 vs. ×0.60 standard, Doubtful gets ×0.68. Dead roster spots are more dangerous when you can't survive a low score.

4

Early-round reliability

Locked starters get a ×1.08 reliability boost in rounds 1 through 4, the weeks you're most likely to be eliminated.

IDP Defensive players

Linebackers, linemen, and defensive backs score alongside your offense. Each position group has completely different value drivers.

1

Separate replacement levels

LBs, DLs, and DBs each get their own VORP baseline. A DB isn't competing for replacement value against a LB.

2

Position group volatility

LBs draft most predictably (tackle-floor, ×0.92). DL/EDGE are sack-volatile (×1.18). DBs are INT-boom (×1.12).

3

Opportunity confidence

Every-down starters get ×1.12. Rotation players get ×0.92. Uncertain roles get ×0.82. Snap share matters for IDP.

4

Depth-based timing

In shallow IDP leagues (1-2 spots), offense still dominates early. The engine applies a ×0.82 dampener to prevent premature IDP runs.

Dynasty / Keeper Long-term

Rosters carry over year to year. A 23-year-old and a 28-year-old with the same current projection are not worth the same draft capital.

1

Age multiplier

Young players (21-24) get a significant VORP boost for long-term value. Veterans 30+ are penalized for aging curves.

2

Rookie modeling

Rookies are split into archetypes (immediate, upside, and situational) based on draft capital and depth chart slot.

3

Two-season stats

Projections blend 2025 stats (65%) and 2024 stats (35%) to stabilize volatile single-season samples.

4

Position scarcity

Dynasty replacement levels account for deeper rosters. The pool that matters isn't who's available this week, but who's available over years.

§04Design work

A fantasy app that shows a lot of information. That was always going to be true.

Each recommendation card shows projected PPG, floor, ceiling, VORP, ADP, position rank, survival probability, and a reason paragraph. Five of those, plus an intel feed with tier cliff warnings, position run alerts, handcuff flags, and stack opportunities. Dense by design. The job was making it navigable without reducing what matters.

Managing the density

Compact mode strips cards to name, position, points, reason. The AI Coach panel collapses entirely. Positions can be muted mid-draft, players dismissed individually. The intel feed sits below, secondary but always visible, never interrupting pick flow.

Three-panel desktop layout

Desktop: three columns with filters and team config on the left, player board in the center, and team panel on the right with AI Coach as an overlay. Mirrors ESPN and Sleeper so there's no learning curve. AI Coach sits alongside the board, not in place of it.

Mobile

Bottom tab nav: Players, Filters, My Team. Draft state persists across tabs. AI Coach shows as a banner above the player list.

Typography and visual system

Inter for body and UI. Space Grotesk for display (player names, headers). JetBrains Mono for all numerics, legible at small sizes and visually distinct from surrounding text. Position colors (QB red, RB emerald, WR blue, TE orange) run consistently across every surface so users build an unconscious map of the board.

Player profile modal, tap any player on the board to open

Player Profile

Brian Thomas

WR JAX · Age 23 · Yr 2
Proj PPG
12.8
VORP
+3.6
OVR Rank
#98
AI Pos Rk
WR17
Sleeper: WR22
Fit / AI RK
4/73
board / overall
AI Take

Projects 12.8 ppg drawing from a full season of 2025 data, with a realistic range of 10.4–15 ppg.

+3.6 VORP, clear starter value well above the typical waiver pickup at WR. Projecting more per game than any freely available replacement.

AI ranks WR17 · Sleeper has WR22. Model has him 5 spots higher than consensus, driven by stronger 2025 receiving production than the consensus has priced in.

Projections
12.8
Proj PPG
high conf.
+3.6
VORP
vs replacement
4/73
Fit / AI RK
board pos / overall
Opportunity 8.4 tgt/g 5.1 rec/g
Season Trend 2025
Half-PPR · 14 games
Target share leader: 28% team target share in 2025, leads all JAX receivers by a significant margin.
Stack opportunity: Pairs well with Trevor Lawrence in 2QB/Superflex formats.
§05Reflection

What this actually was.

Using ChatGPT and Claude to design the scoring model before touching a codebase was the decision that made everything else possible. The logic had to be right before the implementation could be right.

What surprised me

Claude Code's output quality depended directly on the quality of what I brought in. Vague specs produced vague code. The more precisely I could describe what a signal should do at edge cases, the less revision it needed. Compact mode, mute controls, the intel feed, the three-panel layout: none of those came from Claude Code. All decided before any conversation started.

What I'd do differently

  • Build the scoring engine as an isolated, testable module first. Calibration would've been faster without UI state in the way.
  • Write a short spec for each signal before implementing it. The smoothest sessions were the ones with a well-defined problem going in.
  • Ship one format completely before extending to others. Handling dynasty and best ball edge cases alongside the base snake format added early friction that didn't need to exist.

What's still in progress

The scoring logic is still being tuned. Edge cases surface during real drafts: signals that overcorrect in specific formats, urgency that fires slightly too early or too late. Each one gets addressed as it shows up. That calibration work doesn't really end, it just gets more precise.

Sleeper was the starting point because it has a strong API and a large dynasty community, but the plan is to extend support to other platforms. The core engine isn't Sleeper-specific. Adding ESPN or Yahoo is a data layer problem, not an architecture problem.

Visit lineara.me ↗

Next case → T-Mobile’s eSIM e-commerce experience.