Group-purchase campaign modelling

Budget spectrum

Result across online budget levels. Green curve = optimal mix at each budget; dot = your current mix. Drag slider to rebalance channels to the optimal mix at that total.
€0
Optimal-mix resultConfidence bandYour mix
Slider keeps your channel ratios and scales them. The button applies the optimizer's split instead. If your dot sits below the curve, a better split of the same money exists. Diminishing returns: doubling spend does not double registrations once search volume / audience saturates.

Funnel

Expected outcome with your current channel mix.
Online channels — budget × efficiency, saturation ceiling, uplift received
ChannelBudget €CPC €%Reg%AccMax regs (cap)Offline boost %Confidence→ Regs
Max regs (cap) = ceiling of registrations the channel can produce however high the budget; drives diminishing returns (0 = linear, no cap). Offline boost % = uplift this channel receives from ATL + community; auto-filled from the sum of all multipliers, yellow when you override a channel by hand (ATL edits then leave it alone).
Community — direct reach, plus a small multiplier of their own
ChannelCost €Reach%Reg%AccMultiplier %Confidence→ Regs
Community channels produce their own registrations (reach × %reg) and add their multiplier % to the offline boost every online channel receives.
ATL — no direct registrations, multiplier on online channels
ChannelCost €Multiplier %Confidence
Each ATL channel lifts online registrations by its multiplier % while it runs; total effect = sum of ATL + community multipliers. Fill the top field to set every ATL channel at once. Multipliers start as priors and tighten once actuals measure real lift.
Saved locally in this browser. Shared profile save arrives with hosting.
Campaign actuals — log after each campaign
ChannelSpend €Clicks / reachRegistrationsAcceptsInstalls
Clicks/reach lets us derive real CPC and %reg. ATL rows: spend only. Stored in this browser for now — shared database (Cloudflare D1) comes with hosting phase, form stays identical.
Stored campaigns — benchmarks
UseCampaignDateSpendRegsCPA regAcc %Install %Weight
Sorted newest first. Use checkbox picks which campaigns feed the benchmark; Weight = recency × volume (newer + bigger campaigns dominate, 1-year half-life). New scenarios auto-prefill from this weighted average of the ticked campaigns — “Prefill” re-applies it, “Adopt” copies a single campaign. All values are measured; apply your own prudence haircut where volume was thin. On the spectrum tab, “Show actuals on curve” plots each ticked campaign at its online spend vs actual result — check whether reality sits on the model line.

A user manual and a look under the hood: what the numbers mean, how the graph works, what sets the sweet spot, and where the starting values come from.

What this tool does

It models a group-purchase campaign end to end: you spend money across channels, that produces registrations, a share of those accept the offer, a share of those install, and installs earn revenue. The tool answers three questions: how many installs/€ will this plan produce, what is the best split of the budget across channels, and where does adding more budget stop paying for itself (the sweet spot). Everything runs in your browser — no data leaves the page.

Quick start

1
Pick a market in the top-left dropdown (or create a new one). It loads that market's channels and starting rates.
2
Simple vs Expert (top-right). Simple shows the KPIs, graph and funnel. Expert also shows the per-channel editors where you set budgets, CPCs, rates and caps.
3
Read the graph. The green curve is the best result achievable at each online-budget level; the dark dot is your current plan. If your dot sits below the curve, a better split of the same money exists.
4
Drag the slider to scale your plan up or down (it keeps your channel ratios). Or click Optimize mix to snap to the best split at that total.
5
Save the scenario (named, reloadable). After the campaign runs, log the real numbers in Campaign actuals — the next scenario prefills from what actually happened.

The three channel groups

GroupExamplesOwn registrations?Offline boost?
Online (paid)Google, Bing, Facebook/InstagramYes — budget-driven, saturatingReceives it
Online (direct)"Online other"Yes — fixed, not budget-drivenReceives it
CommunityDM, Mailing list, DTDYes — reach × %regAdds a small multiplier
ATLRadio, Addressable TV, DOOH, Print, Programmatic…NoAdds a multiplier

ATL and community channels don't (or barely) register people directly — they lift the online channels. That lift is the "offline boost", explained below.

The funnel — how spend becomes result

Every channel's registrations flow through the same waterfall. Accept rate and install rate are the two conversion steps after registration:

registrations = channel regs × (1 + offline boost%/100) acceptants = registrations × accept rate (e.g. 6%) installs = acceptants × install rate (e.g. 60%) revenue = installs × € per install (e.g. €1200) cost = online spend + community cost + ATL cost result = revenue − cost

The KPI strip and the Funnel card on the planner show exactly these five steps for your current mix.

Diminishing returns — the saturation curve

An online channel does not return registrations forever in a straight line. Search volume and audience size cap it. Each channel has a Max regs (cap) — the most it can ever produce. Registrations follow a saturating curve:

efficiency = %reg ÷ CPC (registrations per euro at low spend) half = cap ÷ efficiency (spend needed to reach half the cap) regs(spend) = cap × spend ÷ (spend + half)

At spend = half you get half the cap; as spend climbs, you approach the cap but never pass it. This is what bends the green curve. Set the cap to 0 for a channel with no ceiling (registrations then grow linearly — useful only when you're nowhere near saturation).

Example (DE Google preset): CPC €1.56, %reg 7.5%, cap 1400. efficiency = 0.075/1.56 ≈ 0.048 regs/€; half ≈ 1400/0.048 ≈ €29,000. So ~€29k of Google spend gets you ~700 regs, and doubling to €58k gets ~930, not 1,400.

The budget-spectrum graph

The chart sweeps online budget from €0 to the slider maximum and, at each level, computes the best achievable result. Four things to read:

  • Green curve Result if the budget at that level were split optimally across channels (the efficient frontier).
  • Band The pessimistic-to-optimistic range, driven by channel confidence (see below).
  • Dark dot + line Your current plan: your online budget and its result. Below the curve = your split is not optimal yet.
  • Gold dots Past campaigns (from Actuals), plotted at their online spend vs real result — a reality check against the model.

What decides the sweet spot

The labelled peak on the green curve is the sweet spot: the online-budget level where result (revenue − cost) is highest. It exists because of the tension between the two curves:

revenue rises but flattens → saturation: each extra € buys fewer regs cost rises in a straight line → every € spent is a full € sweet spot = where one more euro of budget stops adding more than one euro of result (marginal revenue per € = marginal cost per €)

Before the peak, an extra euro of well-placed budget returns more than a euro of result — keep spending. After the peak, saturation means the same euro returns less than a euro — you're buying registrations that cost more than they earn. The peak is the profit-maximising budget, not the maximum-registrations budget.

The optimizer — best split at any budget

"Optimize mix" (and the green curve itself) use a greedy allocation. It hands out the budget in ~300 small steps; each step goes to whichever channel currently returns the most registrations for the next euro (its marginal return):

marginal(spend) = cap × half ÷ (spend + half)²

As a channel fills up, its marginal return drops, so the next euros move to a channel that's still cheap. The result equalises marginal returns across channels — the classic "water-filling" split. The direct channel ("Online other") has no budget lever, so the optimizer never funds it. The slider is different: it keeps your ratios and just scales them, so your manual choices survive until you explicitly click Optimize.

Offline boost (multipliers)

ATL and community channels lift every online channel's registrations while they run. The total boost is simply the sum of all their multipliers:

offline boost % = Σ(ATL multipliers) + Σ(community multipliers)

It's applied to each online channel as × (1 + boost/100). Each online row auto-fills with this total; type your own number in a row to override just that channel (the cell turns yellow and stops auto-updating). "Reset multipliers to average" flattens ATL to their average and clears overrides. This model is additive and uniform across online channels — a deliberate simplification; when actuals show real lift, the priors tighten.

Confidence & the uncertainty band

Every channel carries a confidence level that sets how wide its band is. The band on the graph and the KPI ranges come from flexing each channel up and down by its confidence:

band(dir) = 1 + dir × confidence ± extra uncertainty dir = −1 pessimistic · 0 expected · +1 optimistic
Confidence± widthMeaning
HIGH±10%measured, stable
MED±25%some evidence
LOW±40%prior / guess

Thin evidence → wide band. When you log real campaign data, matched channels are bumped up a level (low→med→high), so the band narrows as the tool learns.

How the starting numbers are loaded

On open, the tool loads a market and builds a fresh scenario in three layers:

1
Preset skeleton. The market's channel list and prior rates (the DE preset is seeded from the HPDE 2026 sheet: Google €15k / €1.56 / 7.5% / cap 1,400; Facebook €25k / €0.48 / 1.45%; €1,200 per install; 60% install rate; etc.). A new custom market starts from a zeroed template.
2
Seeded history. If no campaigns are logged yet for that market, one reference campaign is seeded (DE: "SO paid 12m" — Google €22k / 14,103 clicks / 2,186 subs, etc.) so the model has real data to lean on from the first minute.
3
Prefill from measured actuals. A fresh scenario then overwrites its rates with the recency×volume-weighted average of your logged campaigns — derived straight from measured numbers, never from previously guessed scenarios (that would compound guesses).

The derivation from a logged campaign is direct arithmetic:

CPC = spend ÷ clicks %reg = registrations ÷ clicks accept rate = accepts ÷ registrations install rate = installs ÷ accepts

Recency × volume weighting

When several campaigns feed the benchmark, newer and bigger ones count more. Each campaign's weight is its registration volume decayed by age on a one-year half-life:

weight = 0.5 ^ (age_in_days ÷ 365) × registrations (then normalised to 100% across ticked campaigns)

A campaign from today counts full; one from a year ago counts half. The Weight column in the Actuals tab shows each campaign's share; untick a campaign to exclude it from the prefill.

Where your data lives

Markets, saved scenarios and logged campaigns are stored in this browser only (localStorage) — nothing is uploaded. That's why the data is per-device for now. The save/load points are isolated so a hosted version can swap them for a shared database (Supabase/Cloudflare) without changing anything you see here.