A marketing-mix model that runs in a browser tab. Drop in a weekly spend-per-channel history and it measures what each channel actually contributed, what the next dollar earns, and where to shift budget — using real MMM techniques (adstock carryover, diminishing-returns saturation, least-squares fit), all computed locally.
No install. No Python or R. No AI. No uploads — your spend data never leaves the page.
▶ Open the tool · or download index.html (right-click → Save Link As) and open it offline.
Serious MMM tools — Meta's Robyn, Google's Meridian, PyMC-Marketing — are excellent and also a project: a language runtime, a dependency tree, and a data scientist to drive them. Most marketing teams never get past that setup cost, so they keep making budget-split decisions off last-click attribution, which systematically undercredits anything that isn't a bottom-funnel click.
This is the 80% version you can actually run on a Tuesday: one file, one CSV, an answer in about ten seconds — and every number shows the formula that produced it.
-
Open the live tool (or download
index.html). -
Click Try it: sample 52-week account to see the whole thing work before touching your own data.
-
Build your CSV — a wide table, one row per week:
week,meta_spend,google_spend,tv_spend,revenue 2025-08-25,2036.97,2317.94,0.00,13408.40 2025-09-01,2160.64,2294.00,0.00,14792.42Weekly spend totals come from each platform's export; revenue or conversions come from your store or CRM. Most teams assemble this in a spreadsheet in about ten minutes. Daily rows work too — they're aggregated to weeks automatically, so you need roughly six months of days for a usable fit.
-
Drop it in, confirm the auto-detected results column, and read the verdicts.
| Output | What it tells you |
|---|---|
| Model fit (R² + holdout error) | Whether to trust the split at all — the tool grades itself trustworthy / directional / too weak to act on before showing conclusions |
| Base vs. media-driven | How much of your revenue would happen anyway versus what advertising actually moved |
| Contribution per channel | Modeled share of results each channel drove — not last-click credit |
| Next-dollar return | What one more dollar per week earns at your current spend level, carryover included — the number that should drive budget decisions |
| Carryover % | How much of a week's impact lands in later weeks (why TV and video look worse than they are in last-click) |
| Verdicts | INVEST / HOLD / TRIM / UNPROVEN per channel, with the reasoning and the formula trace |
| Reallocation | Concrete shifts sized at a safe 10%-per-round step, with the expected weekly gain |
For each channel, spend is transformed twice before fitting:
- Adstock (carryover):
z_t = spend_t + θ · z_(t−1)— this week's advertising keeps working next week. θ is searched over a grid per channel. - Saturation (diminishing returns):
effect = β · z^αwith α ≤ 1 — the tenth thousand dollars does less than the first. - Fit: θ and α are chosen per channel by coordinate descent on R², then β is solved by ordinary least squares with a non-negativity pass (channels that come back negative are dropped rather than reported as harmful). θ is searched over {0, 0.2, 0.4, 0.6, 0.8}, so the reported carryover is quantised to those steps.
- Marginal return:
β·α·z̄^(α−1) ÷ (1−θ)— the derivative at your typical spend level, which is what "should I add budget here?" actually depends on.
Every one of these numbers is visible per channel under show the math.
This is a lite model, and it says so in the UI:
- Flat spend can't be measured. A channel funded at the same amount every week has nothing to correlate against; it's marked UNPROVEN, not given a fake number.
- It reads correlation. A channel that only runs during your peak season will look better than it is.
- Overlapping channels blur. Brand search and retargeting chase the same demand signal, and the model may split credit between them arbitrarily.
- It needs history. Six-plus months of weekly rows; below ~24 periods the tool warns you explicitly.
- The base/media split can break. If your channels are credited with more than your total results — which happens when there's little organic baseline, or spend tracks results too closely — the tool says so and marks the split unusable rather than showing an impossible number. Channel rankings stay valid.
Use it for direction and budget splits, then confirm with holdout tests (turn a channel off in one region for a few weeks) — not as courtroom-grade attribution.
PULSE//AUDIT is the bottom-up half: it audits an individual ad account (Meta, Google, TikTok, LinkedIn) and tells you which ads and keywords to scale, pause, or kill. Same philosophy — one file, no AI, transparent rules. Use MMM to set the budget split across channels; use AUDIT to spend each channel's budget well.
Issues and PRs welcome. It's one dependency-free HTML file: fitModel() holds the adstock/saturation/OLS logic, runModel() turns coefficients into verdicts. Good contributions: ridge regularization for correlated channels, seasonality/holiday controls, a scenario slider for "what if I moved 20%?", confidence intervals via bootstrap.