A single-page tool that scores a described product feature against the Fogg Behavior Model (B = M · A ≥ k), plots where it lands relative to the action line, and proposes AI-generated fixes you can toggle to watch the point move in real time.
All scoring is done by your own LLM — bring your own Anthropic, OpenAI, or local Ollama key. No backend, no database, no server-side secrets. Credentials live only in sessionStorage and are cleared when the tab closes.
BJ Fogg's Behavior Model states that a behavior occurs only when Motivation, Ability, and a Prompt converge at the same moment:
B = MAP
The model is primarily descriptive. Fogg identifies a curved trade-off boundary — the action line — on a Motivation × Ability plane and argues that a prompt will only trigger a behavior when the user's position is above it. He does not publish a closed-form equation for this curve; the shape he draws is hyperbolic.
- BJ Fogg's Behavior Model — behaviormodel.org
- Fogg's Stanford Behavior Design Lab — behaviordesign.stanford.edu
- Tiny Habits (Fogg, 2019) — tinyhabits.com — the book that operationalises the model for product and habit design
- Hooked (Nir Eyal, 2014) — a complementary model focused on the trigger–action–reward–investment loop
- COM-B model (Michie et al., 2011) — a parallel behavioral framework from health psychology: Capability, Opportunity, Motivation → Behavior
- EAST framework (Behavioural Insights Team, 2014) — four conditions for behavior change: Easy, Attractive, Social, Timely
Because Fogg does not specify a formula, we operationalise the model as:
score = M × Aᵞ
passes = score ≥ k
With γ = 1 (the default), this simplifies to M × A ≥ k. The code lives in lib/fogg.ts:
export const score = (M, A, gamma = 1) => M * Math.pow(A, gamma);
export const passes = (M, A, k, gamma = 1) => score(M, A, gamma) >= k;Three natural ways to combine M and A:
| Form | Boundary shape | Zero on one axis |
|---|---|---|
M + A ≥ k |
Linear | Behavior can pass with zero motivation if ability is high enough — contradicts Fogg |
min(M, A) ≥ k |
L-shaped | Both factors must independently clear k — too strict |
M × A ≥ k |
Hyperbolic | Zero on either axis → zero score, regardless of the other |
The multiplicative form is the only one that produces a rectangular hyperbola — which is exactly what Fogg draws. It encodes two core claims of the model:
- Zero kills: if a user has no motivation, or the action requires superhuman effort, no prompt will ever work. The product is zero, not a penalty.
- Compensatory trade-off: high motivation can make hard actions achievable; extreme ease can partially offset weak motivation. But the exchange rate is convex — doubling ability does not double the score if motivation is already high.
The action line is defined as the curve where score = k exactly:
M = k / Aᵞ
Points above this curve satisfy M × Aᵞ > k and will respond to a prompt. Points below will not.
γ controls the curvature of the action line and how steeply it rises as ability decreases:
γ = 1(default): symmetric. Halving ability requires halving motivation to stay on the line.γ > 1: ability becomes the binding constraint faster. The curve steepens — useful for modelling habitual behaviors where even small friction causes dropout.γ < 1: the line flattens. Motivation dominates; ability contributes less.
We fix γ = 1 for v1 to keep scores interpretable and to avoid introducing a parameter the LLM cannot reliably estimate. It is exposed as a named constant so it can be surfaced as an advanced toggle in a future version.
Unlike the standard Fogg diagram — which shows a single fixed action line — we make k variable per action. The LLM estimates it based on the action's stakes:
| k range | Typical action |
|---|---|
| 0.05 – 0.15 | Trivial, reversible, low-stakes (one-tap reorder, notification dismiss) |
| 0.15 – 0.30 | Some commitment or unfamiliarity (sign-up, form submission) |
| 0.30 – 0.45 | Meaningful friction or exposure (payment, permission grant) |
| 0.45 – 0.60 | Risky, irreversible, or high-commitment (identity verification, wallet approval) |
After parsing, k is hard-clamped to [0.05, 0.60] to prevent degenerate curves: below 0.05 the action line sits so low that almost everything passes; above 0.60 it sits so high that nothing does.
When you toggle improvement suggestions, the tool projects a new position:
for (const suggestion of applied) {
if (suggestion.lever === "motivation") M = clamp(M + suggestion.delta, 0, 1);
else A = clamp(A + suggestion.delta, 0, 1);
}This is a linear first-order approximation: deltas are applied additively, one per lever, each clamped to [0, 1]. It is optimistic — real-world improvements rarely stack independently, and a delta of 0.15 on ability does not combine with another 0.15 ability delta to give 0.30 in practice. Treat the projected point as a directional signal, not a precise forecast.
M × A ≥ kis our quantitative interpretation of Fogg's qualitative framework, not a formula he published.- The model assumes M and A are independent. In practice, perceived ease can raise motivation (and vice versa), so the factors interact.
- All three numbers (M, A, k) are LLM estimates from a text description. They are structured heuristics, not measurements. Treat them as a prompt for discussion, not as ground truth.
- Suggestion deltas are estimated order-of-magnitude lifts. Real A/B results will differ; the projection is useful for comparing directions, not magnitudes.
The LLM estimates each variable for a typical target user at the moment they are prompted:
| Variable | Range | What drives it |
|---|---|---|
motivation |
0 – 1 | Desirability and immediacy of the outcome. High when payoff is concrete and felt now; low when abstract or delayed. |
ability |
0 – 1 | Ease of the action. Starts at 1 and is reduced for each of: time cost, money cost, mental effort, number of steps, physical effort, unfamiliarity. |
threshold k |
0.05 – 0.6 | Stakes of the action. Higher for irreversible, risky, or high-commitment behaviors. |
confidence |
0 – 1 | Specificity of the input description. Vague one-liners score below 0.45; precise step-by-step descriptions can reach above 0.75. |
It then proposes 2–4 concrete improvements targeting either the ability lever (remove friction) or the motivation lever (surface the payoff), each with an estimated delta of 0.05–0.35.
- Interactive action-line chart — recharts
ComposedChartwith a live "now" dot and an "after" projected dot that moves as you toggle suggestions - Three providers — Anthropic (with assistant prefill for JSON), OpenAI (json_object mode with retry), Ollama (local, CORS-aware)
- Draggable split pane — resizable two-column layout on desktop (≥ 860px), ratio persisted in
sessionStorage; stacks on mobile - Five built-in examples — from trivial one-tap reorders to six-step KYC flows and Web3 onboarding, covering the full range of the model
- Keyboard-accessible tooltips — every
(?)label is focusable and screen-reader-annotated - Full client-side privacy — no API routes, no server env vars, no telemetry. The only outbound request is directly to the chosen provider
| Layer | Choice |
|---|---|
| Framework | Next.js 15 (App Router, TypeScript) |
| Chart | recharts 3 — ComposedChart, Line, ReferenceDot |
| Validation | zod 3 — strict schema + threshold clamping |
| Styling | Plain inline styles + CSS custom properties (light theme only) |
| Deployment | Vercel (zero env config) |
Uses /v1/messages directly from the browser with the anthropic-dangerous-direct-browser-access: true header. JSON output is elicited via assistant-turn prefill ({) and parsed with fallback for models that don't support prefill.
Uses /v1/chat/completions with response_format: { type: "json_object" }. Retries once without response_format if the model returns 400.
Uses http://localhost:11434/api/chat with format: "json" and stream: false. A deployed page calling localhost is cross-origin — see Ollama + deployed URL below.
- Node.js 18+
- An API key for Anthropic or OpenAI, or a locally running Ollama instance
git clone https://github.com/barancan/fogg-score.git
cd fogg-score
npm install
npm run devOpen http://localhost:3000, select a provider, paste your key, pick a model, and connect.
npm run build
npm startOption A — CLI:
npm i -g vercel
vercelOption B — GitHub import: Push to GitHub → vercel.com → "Add New Project" → import → Deploy.
No environment variables are required. The build produces a fully static client bundle.
When the app is running on Vercel, a call to http://localhost:11434 is cross-origin and blocked by the browser unless Ollama was started with the right OLLAMA_ORIGINS value.
Start Ollama with:
OLLAMA_ORIGINS=https://your-app.vercel.app ollama serveFor local development against localhost:3000 at the same time:
OLLAMA_ORIGINS=http://localhost:3000,https://your-app.vercel.app ollama serve| What | Where it lives | When it's cleared |
|---|---|---|
| API key / base URL | sessionStorage only |
Tab close or "Disconnect" |
| Selected model | sessionStorage |
Tab close or "Disconnect" |
| Feature text | Component state (RAM) | Page reload |
| Split-pane ratio | sessionStorage |
Tab close or "Disconnect" |
No data is ever sent to any server other than the provider you choose. There are no Next.js API routes in this project. You can verify this in DevTools → Network: the only outbound request during analysis is to api.anthropic.com, api.openai.com, or your Ollama host.
app/
layout.tsx — HTML shell, imports globals.css
page.tsx — sessionStorage hydration, Onboarding ↔ Workspace switch
globals.css — CSS custom properties (design tokens), base resets
components/
Onboarding.tsx — Provider cards → credentials → model picker → connect
Workspace.tsx — Analysis state, provider call, desktop/mobile layout
ResizableSplit.tsx — Draggable divider, ratio persisted to sessionStorage
LeftPane.tsx — Textarea, example cycler, suggestion toggles, sticky footer
RightPane.tsx — Verdict banner, confidence chip, stats grid, chart
ActionLineChart.tsx — recharts ComposedChart: action line, now/after dots, connector
Tooltip.tsx — Keyboard-accessible (?) tooltip (aria-describedby)
lib/
fogg.ts — actionLine(), score(), passes(), project(), confidenceLabel()
schema.ts — zod schemas; threshold clamped to [0.05, 0.6] post-parse
prompt.ts — System prompt shared across all three providers
providers.ts — Anthropic / OpenAI / Ollama adapters (analyze + listModels)
storage.ts — sessionStorage get/set/clear for creds and split ratio
types.ts — Provider, Creds, AppError
ink #1a1a17 body text
muted #6b6a63 secondary text
faint #9b9a90 placeholder / disabled
border #e7e5dc dividers
panel #faf9f5 left pane background
paper #ffffff cards
page-bg #f1efe8 page background
pass #1D9E75 above the action line
fail #E24B4A below the action line
line #639922 the action line itself (the one bold element)
ability badge bg #E6F1FB text #0C447C
motivation badge bg #FAEEDA text #854F0B
Numbers (scores, deltas, k) render in ui-monospace. Everything else is system sans-serif. Light theme only.
MIT