An interactive model of how AI affects employment by profession. Type a job; the app maps it to real US Bureau of Labor Statistics employment and wage data, asks Claude to estimate ~14 economic parameters, and runs a 30-year dynamic simulation showing employment, average income, AI capability, and market demand.
Professions are classified into three buckets — Mind (knowledge/code), Hand (physical work, gated by a robotics-arrival slider), and Heart (work where people specifically want a human) — each with its own automation ceilings and capability curve.
- Front end: a single self-contained
public/index.html(no build step). - Back end: one Netlify serverless function (
netlify/functions/estimate.mjs) that proxies to Claude, holds the system prompts server-side, rate limits per IP, and caches results in Netlify Blobs.
No Anthropic API key lives in the repo or the client. Either Netlify's AI Gateway injects
credentials at runtime (billed as Netlify credits), or you set your own ANTHROPIC_API_KEY
as a Netlify environment variable (billed to your Anthropic account).
npm install # restore dependencies (Anthropic SDK, Netlify Blobs)
npm install -g netlify-cli # if you don't already have it
netlify dev # local dev with the function running
netlify deploy --prod # deploy to your Netlify siteOr connect this GitHub repo to a Netlify site (Add new site → Import from Git) and let
Netlify build on push. The publish directory and functions path are already set in
netlify.toml, so no extra build configuration is needed.
For credentials, either enable AI Gateway (Site settings → AI Gateway) or set
ANTHROPIC_API_KEY as a Netlify environment variable.
.
├── public/index.html ← the app (single self-contained file)
├── netlify/functions/estimate.mjs ← serverless proxy: BLS lookup + parameter estimation
├── netlify.toml ← publish + functions config
├── package.json / package-lock.json ← deps
└── README.md
(node_modules/ is git-ignored — run npm install to restore it.)
When you type a profession, the app makes two parallel calls to a single serverless
function (/api/estimate), each going to Claude with a different system prompt. Results
are cached in Netlify Blobs, so each profession is only ever estimated once.
What's sent: your free-text profession plus a catalog of ~830 detailed occupations from the US Bureau of Labor Statistics OEWS survey, each row carrying its SOC code, title, US employment, and median annual wage. This catalog is baked into the app — it is the only hard data source; everything else is estimation.
What Claude returns:
| Field | Meaning |
|---|---|
codes |
One or more SOC codes whose union best matches what you typed (e.g. "plumber" → 47-2152) |
adjusted_estimate |
If your profession is a subset of a SOC bucket ("pediatric nurse" ⊂ "Registered Nurses"), Claude's estimate of the subset's headcount — required to be smaller than the bucket total |
adjusted_wage |
Optional wage correction when the specific profession plausibly earns differently from its bucket average |
confidence, basis |
How sure the match is, and one sentence of reasoning |
How the app uses it: employment is the sum of the matched codes' BLS headcounts (or the adjusted estimate, clamped to never exceed the bucket); wage is the employment-weighted median across the matched codes (or the adjusted wage, clamped to 0.4–2.5× the anchor). These two numbers pin the simulation's year-0 axes to reality. If BLS doesn't track the job (podcaster, influencer), the charts fall back to an index where 100 = today.
Claude is given the profession plus a calibration sheet containing fully worked examples (software engineer, lawyer, tax accountant, plumber, surgeon, warehouse picker, …) and asked to return one classification plus 14 numbers. Every number is range-clamped client-side, so a wild estimate can't break the model.
The classification — workKind: "mind" (knowledge/code — AI needs no body),
"hand" (physical work where the output is what's wanted, so a capable machine is an
acceptable substitute), or "heart" (people specifically want a human doing it —
performers, therapists, clergy). This sets a hard ceiling on how much of the core can
ever be automated: 100% for mind and hand, 0% for heart.
The 14 parameters:
| Parameter | Range | What it means |
|---|---|---|
currentAutomatable |
0.01–0.60 | Share of the job's tasks today's AI can already do |
productivityToday |
1–30× | How much faster AI is at those tasks today |
productivityCeiling |
2–100× | How much faster it eventually gets (low for physics-bound work) |
aiCostToday |
0.02–1.0 | Cost of an AI hour as a fraction of a human hour |
aiCostDecayRate |
0–45%/yr | Annual decline in that cost (fast for software, slow for robots) |
demandElasticity |
0–4 | % demand growth per 1% price drop (the market-expansion engine) |
laborCostShare |
0.2–0.95 | Fraction of the service's price that is human labor |
complementarity |
1–4× | How much AI speeds up the tasks the human keeps |
hiringAdjustmentYears |
0.5–5 | Years for the workforce to grow (credential/training friction) |
demandAbsorptionYears |
1–8 | Years for the market to respond to a price change |
supportFraction |
0–0.8 | Share of the job that is support work (admin, marketing, editing) rather than the core skill |
reachCeiling |
0–20 | Extra multiples of output one human can serve once support is offloaded (high for broadcastable work) |
robotArrivalYear |
0–30 | Hand only: years until dexterous robots meaningfully deploy |
robotRampYears |
1–15 | Hand only: how long deployment takes to spread |
Claude also returns four short narratives shown in the UI (rationale, commentary,
elasticityNote, incomeNote), anchored where possible to BLS 2023–33 employment
projections.
A few values are set by the app, not estimated: supportAICost = 0.04 (support work is
software-cheap for every job), the heart-core ceiling of 0, and for hand jobs a fixed
slow embodied-AI curve (3% today → 12% at year 5) — the robot-arrival slider, not the
capability curve, is the user's control there.
The simulation runs 360 steps over 30 years, so the step is
AI capability follows a Gompertz curve through two user-controlled anchor points —
the share of tasks AI can do today (
Gompertz gives the characteristic S-curve: slow start, steep middle, saturating tail.
The robot gate (hand jobs only) is a logistic centered on the arrival year — capability in the lab is worthless until machines actually deploy:
For mind and heart work
Everything ramps from today's baseline so year 0 shows no artificial shock. A ramp
progress variable rebases the capability gain to zero at
and drives productivity and complementarity linearly from their today values to their ceilings:
AI cost decays exponentially:
Support automation runs on its own fast software curve for every job (≈15% today, ≈50% by year 3, saturating near 95%):
and as support is offloaded, reach grows from 1.0 toward
The unit of analysis is one unit of output (one repair, one case, one performance),
split into core work (share
The first line is the heart of the model:
Cost, price, and the demand response:
$$\text{costPerUnit} = \frac{\text{humanCorePerUnit} + \text{aiCorePerUnit}}{\text{reachGain}}
- \text{humanSupportPerUnit} + \text{aiSupportPerUnit}$$
As AI cheapens production,
Wage per worker (an index; two opposing forces):
The first term is the human-exclusive share of the job, scaled up by leverage (AI
assistance plus offloaded support make each remaining human responsible for more value).
The second term is a price anchor, not payment for work performed: on the automated
share, the buyer's alternative is AI at cost
Real markets and workforces don't jump to equilibrium. Two state variables — market size
where
The scarcity premium closes the loop between those lags and pay. When demand outruns the workforce, the workers already in the job capture a transient premium:
with
Finally, the indexed series are scaled to real units: employment × the BLS headcount, income × the BLS-anchored wage (or left as an index of 100 = today when BLS has no wage for the occupation).
Employment falling while income rises means substitution is winning on headcount but survivors are leveraged. Both rising means market expansion dominates (elastic demand, cheap AI). An income hump in the middle years is the scarcity premium at work. A heart job grows along the demand curve with income nearly flat — its core can't be automated, so AI only cuts its support costs.
In netlify/functions/estimate.mjs:
- Model: the
model: "claude-sonnet-4-6"lines (one per branch). - Rate limit: the
RATE_LIMITconstant (default 30/min). - System prompts:
SOC_SYSandPARAMS_SYS— calibration anchors live inPARAMS_SYS.
In public/index.html:
- Simulation constants (
simulationSteps,anchorYear,layoffAdjustmentYears) sit near the top of the model<script>block. SCARCITY_PREMIUM_SENSITIVITY(default 0.5) sets how strongly a labor shortage lifts wages.- The BLS catalog lives in the
OCCarray.
Employment and wage figures come from the US Bureau of Labor Statistics Occupational Employment and Wage Statistics (OEWS) program — public-domain data.
- "Couldn't estimate that one": check the function logs (Logs → Functions →
estimate) — the error line names the profession and cause. If it's credentials, enable AI Gateway or setANTHROPIC_API_KEY; if it's Netlify credits, see Usage & billing → Account usage insights → AI inference. - "Too many lookups": per-IP rate limit hit. Wait a minute or raise
RATE_LIMIT.