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.
Open Lineara live ↗A live draft co-pilot that re-ranks the board on every single pick.
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.
A full simulated draft against opponents that blend best-available and needs-based logic and roster-check before each pick, with the AI Coach active throughout so you practice on the real thing.
A startup draft mid-board with the Coach live, a rookie draft, and a post-draft hub all load with sample data before you connect Sleeper, so you see the full experience first.
A team grade and projected-points breakdown when the draft ends, plus Start/Sit, Waiver Wire, Trade Evaluator, and News for the regular season, with a pick grader scoring every selection in real time.
Draft page, live Sleeper draft, AI Coach open
Built first for beginners. Deep enough for everyone else.
Newcomers who get overwhelmed by rankings, ADP, and jargon. The AI Coach explains every pick in plain language so they can draft with confidence instead of second-guessing each choice.
- Plain-language reasoning on every pick
- Five recommendations, and why each one
- No prior fantasy knowledge assumed
People who already know the game and just want more information. They can skip the explanations and go straight to the deeper numbers, using Lineara to pressure-test their own reads in real time.
- VORP, tier breaks, and survival odds
- Full board context on demand
- A fast second opinion mid-draft
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.
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
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
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
verify the math: urgencyScale should actually return 0 on pick 1 when nobody has drafted anything yet
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.
// 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
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.
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
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.
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.
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.
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.
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.
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 lays out what the engine looks at when it ranks players and makes recommendations. It opens from the setup screen before you connect your account. These are the exact points, pulled from the source.
VORP engine
Every player is scored by Value Over Replacement: how much they outscore the last draftable starter at their position. Worth is measured against what you could get for free, not raw points.
Anchored to consensus
That score is blended with community consensus (ADP), so the AI can flag genuine value but never free-falls a proven player far below where the market has him off one down year.
Projections from real stats
Points-per-game projections are built from actual production, blending recent seasons to smooth out flukes, and recomputed for your league’s exact scoring settings.
Survival probability
It predicts the chance each player is still on the board at your next pick from their ADP, positional volatility, and opponent demand, so you don’t reach for someone who’d come back to you.
Tier cliffs & runs
Urgency spikes when you’re the last man up in a tier before a quality drop, or when a run on a position is unfolding in real time.
Roster-needs fit
Recommendations weigh your current roster and starting requirements, so the board points you to players who fill real needs, not just the best name left.
Situational flags
Beyond the numbers, it surfaces context flags (role, competition, QB play, age, injuries, usage) for the things stats alone can’t see.
Tuned to your league
All of the above adapts automatically to your scoring, roster slots, and draft type: snake, auction, dynasty, superflex, and more.
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
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.
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 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.
Always happy to talk design, AI, or an interesting problem.
uxmdoss@gmail.com LinkedIn →