PitchPulse⚽ Football

Every match has a pulse!
λ Lambdas (H/A) —/—
PP Exp. Score —
🔥 Best Bet —
Top EV —
λ Lambdas (H/A) —/—
PP Exp. Score —
🔥 Best Bet —
Top EV —

League Baseline

The league's average goals per match — converts raw form into attack and defence strength. Pick a league to auto-fill, or set the numbers yourself.

Advanced — pick a league or override
Using defaults: Home 1.50  |  Away 1.20 (toggle Advanced to change)

Team Form

Enter each side's name, then their season-to-date MP-GF-GA (home venue for Home, away venue for Away) — copy it straight off the league standings.

HOM
AWA

Core Team Stats Required✓ Ready

Season totals off the league table — the model's main input.

Form Check Optional✓ Ready

Recent results, home and away mixed (usually the last 5). Powers Form Check on the Verdict tab.

Bookmaker Odds (Optional)

Type the bookies' decimal odds and they feed the EV Finder card and Model Comparison tab automatically, de-vigged behind the scenes. Same odds as the EV Finder card's own ranking on the Verdict tab — edit here and it stays in sync.

📊 Model Comparison

What PP predicts for the current fixture vs. what the Market expects, read from the de-vigged odds in the EV Finder card on the Verdict tab. The Market side needs odds entered there: all three 1X2 for the score picture and 1X2 tiles, Over/Under's own pair, BTTS's own pair.

PP
⚽ Most Likely Score
Home
– – –
Away
Top correct score probability
Expected Goals
—
Over 2.5
—
BTTS Yes
—
HT Score (Est)
—

1X2

Home
– –
–
Draw
– –
–
Away
– –
–
Most likely score –
Vs league baseline –

Over / Under

Over
––
–
Under
––
–
Most likely Over score –
Vs league baseline –

Both Teams to Score

Yes
––
–
No
––
–
Most likely BTTS score –
Vs league baseline –

Top correct scores

    Market Implied
    ⚽ Bookies' Most Likely Score
    Home
    – – –
    Away
    Enter odds in the EV Finder card on the Verdict tab
    Expected Goals
    —
    Over 2.5
    —
    BTTS Yes
    —
    Overround
    —

    1X2

    Home
    – –
    –
    Draw
    – –
    –
    Away
    – –
    –
    Most likely score –
    1X2 overround –

    Over / Under

    Over
    ––
    –
    Under
    ––
    –
    Most likely Over score –
    O/U overround –

    Both Teams to Score

    Yes
    ––
    –
    No
    ––
    –
    Most likely BTTS score –
    BTTS overround –

    Top correct scores

      Calculate probabilities on the Inputs tab to see the data analysis.

      Calculate probabilities on the Form tab to see your Match Verdict.

      📋 Match Log

      No matches logged yet. Run the model, then hit "Add to Match Log".

      💡 How To Use PitchPulse

      Each side's season standings, one league baseline, and PitchPulse turns it into a full set of match probabilities in one tap.

      Step 1 — League Baseline: Pick a Country and League to auto-fill the average home/away goals for that competition, or type your own. Defaults are Home 1.50 / Away 1.20 — a generic mid-table read. Switching Advanced back off restores these defaults.

      Step 2 — Team names: Name your Home and Away side.

      Step 3 — Season Tally: Copy each team's season-to-date record straight off the league standings table (home rows only for the Home side, away rows only for the Away side) — matches played, goals for, goals against — into the three MP / GF / GA boxes. Neutral Venue on? Neither side is at home, so enter away rows for both teams.

      Step 4 — Elo rating (optional): Each team-name row has an Elo pill. Leave both blank for a form-only read, or fill in both (e.g. from clubelo.com or eloratings.net) to shift expected goals toward the stronger side (the fixture's total expected goals stays exactly as the model computed it — Elo only changes how it's split) — useful when the two teams aren't from the same league or division, so their Season Tally numbers alone don't capture the quality gap. It only takes effect once both sides have a value.

      Step 5 — Calculate: Hit "⚡ Calculate Probabilities". The Model Comparison tab's PP column fills in with 1X2, Over/Under, BTTS, and the top correct scores; the stats strip up top shows Lambdas (H/A), PitchPulse's own top correct score (PP Exp. Score), the model's Best Bet — the market that wins the probability/excess hybrid ranking among markets that clear 50% raw probability, shimmering gold whenever all 3 of the top 3 most probable correct scores agree with it — and Top EV, the single positive-EV market ranked highest once odds are entered on the Inputs tab.

      Step 6 — Track it: On the Verdict tab, hit "+ Add to Match Log" to save the pick, then fill in the real final score once the match is played. Signal Check and Calibration build up as your log grows. The log archives automatically each day — use 🗄 Archive or the ⬇ CSV/JSON buttons to export for analysis.

      📊 Where To Get Your Team Stats

      PitchPulse doesn't pull live data — you feed it the numbers, which keeps it fast, offline-friendly, and usable for any league on earth.

      Season MP-GF-GA — matches played, goals for, goals against

      Official league standings tables — most league sites publish separate home/away standings splits (e.g. premierleague.com, laliga.com) with MP/GF/GA columns ready to copy straight in, season-to-date.

      FlashScore.com & WhoScored.com — the standings tab usually has a Home/Away split alongside the overall table.

      FBref.com — free standings and squad stats for almost every senior league worldwide, including home/away splits.

      League averages

      Use the Country/League selector on the Form tab first — it auto-fills from a built-in database. If your league isn't listed, search "[league name] goals per game average" for the current season, or just leave the defaults (1.50 home / 1.20 away) as a generic baseline.

      ⚙️ Good To Know

      Everything stays on your device

      Inputs, theme, and the Match Log are saved in your browser's local storage only. There's no backend and nothing is ever sent anywhere.

      Installing PitchPulse

      Served over HTTPS, PitchPulse installs as a standalone app via the "Install App" button (or your browser's install prompt) and keeps working offline once installed.

      Light / dark mode

      Toggle the sun/moon icon in the header any time — your choice is remembered on next visit.

      ❓ Methodology & FAQ

      What model does PitchPulse use?

      A Poisson model built on each team's season-to-date record. Each team's season MP-GF-GA — matches played, goals for, goals against (home-only for the Home side, away-only for the Away side) — is blended with a league-average prior via adaptive shrinkage, so a small early-season sample doesn't overreact to a handful of results. Those shrunk rates become attack/defence strength relative to the league baseline, which convert into expected goals (λ) for each side — from there, independent Poisson distributions build the score matrix, with an adaptive Dixon-Coles / bivariate-Poisson correlation blend applied before it's renormalized (see the next question). Every market on the Model Comparison tab's PP column is derived from that final matrix.

      What is the Dixon-Coles correction, and why is it adaptive?

      Independent Poisson treats home and away goals as statistically unrelated, but real match goals are mildly coupled by things like game state and tactics — and that shows up specifically in the low-scoring cells. The Dixon & Coles (1997) tau correction adjusts exactly those four cells (0–0, 1–0, 0–1, 1–1) before the matrix is renormalized. Rather than a single fixed literature constant, ρ here is adaptive: it scales with the league's own average goals, growing more negative for defensive/low-scoring leagues and lighter for high-scoring ones — anchored so a league at the app's default baselines (1.50/1.20) reproduces the original literature-standard ρ = -0.13 exactly. It's derived purely from the League Baseline inputs above (never from either team's own GF/GA form) and runs automatically in the background — there's nothing to configure.

      What's the bivariate Poisson blend, and when does it kick in?

      Dixon-Coles only touches four cells — the low-score corner it was built for. Everywhere else in the matrix, independent Poisson is still uncorrelated, which matters more as a league's scoring rate climbs and more of the probability mass sits outside that corner. A bivariate Poisson model (two independent legs plus a small shared "common-shock" term, λ3, that couples every cell) fills that gap without changing either side's expected goals — λ3 is carved out of both λHome and λAway, not added on top. Which correction actually runs is itself adaptive to League Baseline: leagues at or below the app's own reference average (≈1.34 goals) run pure Dixon-Coles, leagues at 2.0+ run pure bivariate Poisson, and anything in between blends the two score matrices on a straight linear ramp. Same as Dixon-Coles above — nothing to configure, it retunes itself off the League Baseline inputs.

      Why season-to-date instead of last 5 matches?

      An earlier version read the last 5 matches and dropped the single best and worst to keep one outlier scoreline from dominating the read — that worked, but it also capped how much real evidence the model could ever use: a team's read stayed pinned to roughly 3 match-equivalents of real data all season, no matter how many games it actually played. Season MP-GF-GA uses everything played so far instead, with adaptive shrinkage (see "How are λ calculated?" below) doing the small-sample protection early on — and that protection fades naturally as the season goes on and real MP grows, rather than staying fixed.

      How are λ (expected goals) calculated?

      HomeAttack = Home team's shrunk home goals-for ÷ league average home goals. HomeDefence = shrunk home goals-against ÷ league average away goals. Away strengths mirror this using the team's away form. Before that division, each side's raw season GF/GA rate (goals ÷ matches played) is blended with a few "average" matches worth of the league baseline — so a genuine 0 across a handful of real matches doesn't collapse to a guaranteed clean sheet, while a team with a deep season sample is barely pulled off its own real rate. That blend strength is adaptive on two axes: it scales with the league's scoring level (goals follow a Poisson distribution, so a small sample is proportionally noisier in a low-scoring context than a high-scoring one — a defensive league at 0.8 goals/game gets pulled toward the baseline more firmly, around 46% weight, than a high-scoring one at 2.5 goals/game, around 33%), and it fades as real matches played (MP) grows — a team 2 games into its season leans heavily on the baseline, the same team 30 games in barely does. λHome = HomeAttack × AwayDefence × league average home goals, and λAway = AwayAttack × HomeDefence × league average away goals. A strength of 1.00× means exactly league average; 1.25× means 25% above it.

      What's on the Data Analysis tab?

      It sits between Model Comparison and Verdict, deliberately, as a "read the match setup" step before Best Bet is revealed — nothing on it names or hints at which market Best Bet actually picks. Market Split draws the same 1X2/O-U/BTTS probabilities Model Comparison already tabulates, just as donut charts. Score Read shows how much of the correct-score matrix sits in the top scoreline and the next four vs. everything else, with a Concentrated/Moderate/Wide-open tier — that's about the shape of the matrix as a whole, not a hint at any one scoreline. Expected Goals shows λHome vs λAway as a split bar against this league's typical total, and Goal Total Split regroups that same matrix into 0–1 / 2–3 / 4+ total goals instead of a single Over/Under cut. Score Matrix lays out the full correct-score grid itself — each team's goal counts bucketed 0, 1, 2, 3, 4, and 5+ along its own axis, shaded by probability, with the single most likely scoreline ringed — so the shape described by Score Read and Goal Total Split is visible cell by cell too. Setup Notes is a short plain-language bullet list generated from everything above (expected-goals gap, total vs league baseline, Elo gap, and the most extreme attack/defence multiplier), purely descriptive rather than a recommendation.

      What are the default league averages, and why do they matter?

      1.50 home goals, 1.20 away goals per match — a generic mid-table baseline. Raw goal counts only mean something relative to context: 1.8 goals a game is modest in a high-scoring league and strong in a defensive one. Pick your league from the Country/League selector to auto-fill the real numbers, or enter them yourself for the sharpest read.

      What is Best Bet, and how is it different from "highest probability"?

      Best Bet asks one question: after allowing for how unsure the model is, does any market pay more than its price? For each of the seven direct markets (Home/Draw/Away, Over/Under 2.5, BTTS Yes/No) it works out a cautious probability — the model's estimate minus about one standard error, because expected goals built from a handful of matches are only estimates. It then compares that cautious figure with the market's price and sizes the edge with a Kelly fraction. The eligible market with the biggest Kelly fraction wins; if none has a positive edge the answer is No Bet.

      With odds entered (Markets View), the price is the odds you typed, the Verdict card shows the break-even, your edge, EV and a suggested stake (a quarter-Kelly share of your bankroll, capped at 5%), and any axis you've fully priced has its de-vigged probability blended in at 35% — bookmakers are the strongest single predictor, so the model has to earn any disagreement. Markets with no odds can't be picked. With no odds, Best Bet runs model-only: each market is priced at the fair price of a typical fixture in this league, a pick must also be more likely than not on its cautious figure, and the card tells you the minimum odds worth taking. Model-only is indicative — enter odds to turn it into a real bet.

      Once at least 20 of your logged matches have a final score, every probability is also calibrated against that history (a gentle logistic correction that only moves if your results clearly show the model is over- or under-confident). The top-3 correct scores are kept as a corroborating note, not a gate.

      What is the "cautious probability", and why is it lower than the Prob shown?

      Every probability in the app rests on expected goals (λ) estimated from the matches you entered, blended with a league-average prior. A team with 8 matches played gives a much shakier λ than one with 30, so Best Bet re-runs the score matrix across a spread of plausible λ values for each side and measures how far each market's probability moves. The cautious probability is the central figure minus about one standard error (an ~80% lower bound). Thin samples and early-season fixtures widen the spread, so they need a bigger edge to qualify — no separate warning flags are needed. The breakdown table shows both figures side by side.

      Why does Best Bet sometimes shimmer gold?

      It shimmers when the pick is unanimous — all 3 of the top 3 correct scores agree on it. The Verdict tab always shows a confirmation badge next to the pick — 3/3 or 2/3 scorelines backing it shows green "AGREE", 1/3 shows amber "NEUTRAL", and 0/3 shows red "DIVERGE" (an explicit warning that none of the model's own top-3 scorelines land on the pick) — so you can see the strength of that corroboration at a glance even when it isn't full 3/3. Only the exact 3/3 case shimmers; 2/3 is still shown as agreement, just not the unanimous version of it.

      When a Best Bet is confirmed this way and you log it, the Match Log entry itself is marked with a gold "✨ CONFIRMED" badge and the same shimmer, so it stands out from the rest of your log.

      What is the EV Finder, and how is it different from Best Bet?

      Best Bet picks the market with the best risk-adjusted edge (using your odds when you've entered them, otherwise a model-only baseline). The EV Finder lists every market at the odds you're being offered: "is each of these a good price?" You type in the decimal odds for each of the same seven markets, and it compares them against the model's own fair probability: EV% = (fair probability × odds) − 1. Positive EV means the price is better than the model's fair value; negative means it isn't — regardless of whether that market happens to be the current Best Bet. A market can be the model's top confidence pick and still be poor value if the odds are short, and a market the model rates lower can still be +EV if the price is generous. Odds you type in persist while you keep recalculating the same fixture, but aren't saved to local storage — a fresh page load starts blank.

      Bookmaker De-Vig reads those same odds a different way, running quietly in the background rather than as its own visible card: instead of comparing them to the model, it strips the bookmaker's own margin (the "overround") out of each group of odds — 1X2, Over/Under 2.5, BTTS — to reveal the bookmaker's true fair probabilities. A group needs every one of its odds filled in before it can be de-vigged exactly, since the margin can't be isolated from a partial set. Once the three 1X2 odds are in, it goes a step further and searches for the λHome/λAway pair whose own score matrix would reproduce those de-vigged prices, surfacing the bookies' own implied most-likely scorelines — the same kind of read the Verdict tab gives for the model's numbers, but built entirely from the market's, so the Model Comparison tab's "Market Implied" column can show where the model and the market disagree on the shape of the match, not just the price of one outcome. If you only type one side of Over/Under 2.5 or BTTS (say just Over, or just BTTS Yes), that price still helps the λ fit: its margin is estimated from the 1X2 margin rather than the price being ignored. Only the fit (and the scorelines built from it) uses that estimate — the Over/Under and BTTS tiles, the market notes and the Match Log MKT badge still need both sides, so type both when you want the exact de-vig.

      What is Signal Check?

      A 🟢/🟡/🔴/⚪ tier for the specific bet category behind your current Best Bet (e.g. "Home Win"), computed from your own logged history using a Wilson score interval lower bound rather than a raw hit rate — so a 2-for-2 record doesn't get overrated. It needs 5+ resolved picks in that category before it rates anything; below that it shows ⚪ UNPROVEN.

      What is Calibration (the Brier score)?

      The 📊 Calibration button opens a running Brier score (0–1 scale, lower is better) split by outcome — Home Win, Draw, Away Win, Over 2.5, Under 2.5, BTTS Yes, BTTS No — comparing your all-time average against your last 10 resolved matches for each, so you can see which specific side is improving or slipping instead of one blended number per market. Where a market has 5+ resolved picks on each side, it also splits Home Win / Draw / Away Win into an Elo ON vs. Elo OFF comparison — a higher Elo ON score means the Elo adjustment is hurting that market's calibration, a lower one means there's room to lean on it more. This is independent of Signal Check: it grades the model's raw probabilities directly, rather than just whether the top pick won.

      How does the Match Log work?

      Hit "+ Add to Match Log" on the Verdict tab after calculating to save a prediction. Once the match is played, enter the real final score — it's marked ✅ HIT or ❌ MISS against the Best Bet pick, and rolls into the Hit Rate shown in the stats bar. Best Bet, the Match Log, and the Model Comparison tab all grade against the same fixed Over/Under 2.5 line, so a logged "Over 2.5" always stays comparable.

      What's the Season Tally (League/Cup tally) field?

      The MP / GF / GA boxes in the Core Team Stats section — matches played, goals for, goals against, typed straight off the league standings table (home rows for Home, away rows for Away — or away rows for both when Neutral Venue is on), season-to-date. It's the main model's only input now; there's no trim or per-match entry to do by hand. Each box holds up to two digits and auto-advances as soon as it's full (MP → GF → GA), and after Home's GA the cursor hops straight to Away's MP — same as the Form Check tally below it. Backspace on an empty box steps back. A blank tally just means the model falls back to the pure league baseline until you fill it in — nothing errors or needs clearing first.

      What is Form Check, and what do the 📋 Form: Agree / 📋 Form: Diverge badges mean?

      Form Check compares Best Bet's pick against each side's general (unsplit home/away) recent form — the raw tally you type on the Team Form card as mp-gf-ga (e.g. 6-11-8; not always 5 matches, mp is read straight off whatever's typed). It's fully automatic — no research step, nothing to pick — it recomputes live the moment both sides have a valid tally, and shows its own verdict right there on its own card: "✅ Agrees" or "⚠ Diverges", naming whichever market it favors instead. On that card, Form's own pick turns green (▲) once its probability is above 60%. The same read is also echoed right under Match Verdict itself, on the Verdict tab's own card, the moment it has a valid read: a green "✅ Form confirms" note when it agrees — two independent reads landing on the same pick — or an amber "⚠ Form diverges" note otherwise. No note at all (in either place) means Form Check had no valid tally on one or both sides.

      Form Check itself never logs anything — there's no button dedicated to it. But whichever way a match DOES get logged (the ordinary "+ Add to Match Log" button), the entry is tagged with whatever Form Check's own read was at that exact moment: a green "📋 Form: Agree" badge if it favored the same pick, or an amber "📋 Form: Diverge" badge if it favored something else. No badge at all means Form Check had no valid tally on one or both sides when the match was logged. The entry's meta line also shows Form Check's top scoreline (teal "Form 1–1") and the bookies' de-vigged top scoreline (gold "Mkt 2–1") next to the main λ, each only when that read existed at log time.

      What's the little time field between the team names for, and does it change the model?

      That's the match's kickoff time — entirely optional, and it never touches the model: λ, Best Bet, Form Check, none of it is read from or changed by what's typed there. It exists purely for the Match Log. Once a pick carrying a kickoff time is logged, it shows up as a small "🕐 15:00" chip next to the team names, and — on the live Match Log specifically, not the Archive — entries with a kickoff time are sorted latest-first, above any entry that doesn't have one; entries without a time keep their normal most-recently-logged-first order among themselves. Handy on a day with several fixtures logged out of order, so the Match Log reads top-to-bottom with the day's next-up fixture at the top. It's saved with the rest of the fixture inputs, so "↺ Clear Fields" blanks it and "↺ Reload Fixture" on a past entry restores whatever was typed for that one.

      What happens to the Match Log after 24 hours?

      It archives automatically. The first time the app checks after the calendar date changes — on load, or when you come back to an open tab — everything logged "today" moves into a dated archive bucket, and the Match Log starts fresh for the new day's fixtures. Nothing is deleted: open 🗄 Archive to browse every past day (with its HIT/MISS split) and export it as CSV or JSON. The ⬇ CSV / ⬇ JSON buttons next to "Clear all" export everything at once — today's log plus the full archive — as one file for spreadsheet or offline analysis.

      Does PitchPulse store or send my data anywhere?

      No. Everything — inputs, theme, your Match Log, and its archive — lives in your browser's local storage. There's no backend and no data ever leaves your device unless you tap an export button yourself.

      Can I install PitchPulse as an app?

      Yes — served over HTTPS, PitchPulse is an installable PWA. Use the "Install App" button in the header, or your browser's own install prompt, and it'll keep working offline afterward.

      Disclaimer

      PitchPulse produces a probability estimate from recent form — not a guarantee. It doesn't account for injuries, motivation, or opponent quality beyond the last-5-match window (an Elo rating pair, entered on the Team Form card, can correct for a cross-league quality gap, but there's no read on things like a relegation six-pointer or a dead rubber), and Signal Check / Calibration are only as meaningful as how many matches you've logged and resolved. Please gamble responsibly. — by Victor Korir

      📬 Contact the Developer

      Questions about the model, licensing, or custom builds? Reach out directly: