Quick math: $100,000 divided by 12 is $8,333. So your monthly paycheck is $8,333, right?
No. Not even close. Run the real numbers for a single filer in 2026 with no state tax and you land at $6,598 a month. Almost $1,750 vanishes before you ever see it, and most people can't name where it went.
I got tired of hand-waving this, so I built a take-home pay calculator in plain JavaScript. No frameworks, no API calls, just the actual tax math. This post walks through every deduction type, one at a time, with working code. By the end you'll have a function you can drop into any project.
All figures below use 2026 tax law. Tax rules change yearly, so treat this as a learning exercise and double check current numbers before making real decisions.
The hook: where did $20,820 go?
Let's trace a $100,000 salary for a single filer, step by step. Spoiler: four separate hands reach into the paycheck, and they don't all take the same way.
| Deduction | Amount |
|---|---|
| Federal income tax | $13,170 |
| Social Security | $6,200 |
| Medicare | $1,450 |
| Total | $20,820 |
Annual take-home: $79,180. Monthly: $6,598. Biweekly: $3,045.
Notice what's missing from the naive "$8,333" math. It's not one big tax. It's a stack of different systems, each with its own rules. That's why a calculator needs more than one formula. Let's build them in order.
Part 1: Federal income tax and the marginal rate trap
This is the part everyone gets wrong. The single most misunderstood idea in personal finance: your tax bracket is not your tax rate.
The 2026 federal brackets for single filers look like this:
| Rate | Taxable income up to |
|---|---|
| 10% | $12,400 |
| 12% | $50,400 |
| 22% | $105,700 |
| 24% | $201,775 |
| 32% | $256,225 |
| 35% | $640,600 |
| 37% | above that |
Someone earning $100,000 hears "you're in the 22% bracket" and panics, thinking they owe $22,000. Wrong. Brackets are marginal. Each slice of income is taxed at its own rate, like filling buckets.
But wait, we don't even start at $100,000. First comes the standard deduction: $16,100 for single filers in 2026 ($32,200 married joint, $24,150 head of household, $16,100 married separate). That money is never taxed at all. So:
Taxable income = $100,000 - $16,100 = $83,900
Now fill the buckets:
- First $12,400 at 10% = $1,240
- Next $38,000 ($12,400 to $50,400) at 12% = $4,560
- Last $33,500 ($50,400 to $83,900) at 22% = $7,370
Total federal tax: $13,170. The effective rate is 13.2%, even though the "bracket" says 22%. That gap between marginal and effective is the whole story of income tax, and your calculator has to model buckets, not a single percentage.
Here's the code. It walks the bracket table and taxes each slice:
// 2026 federal brackets, single filer: [cumulative cap, rate]
const FEDERAL_BRACKETS_SINGLE = [
[12400, 0.10],
[50400, 0.12],
[105700, 0.22],
[201775, 0.24],
[256225, 0.32],
[640600, 0.35],
[Infinity, 0.37],
];
function federalTax(taxableIncome) {
let tax = 0;
let prevCap = 0;
for (const [cap, rate] of FEDERAL_BRACKETS_SINGLE) {
if (taxableIncome <= prevCap) break;
tax += (Math.min(taxableIncome, cap) - prevCap) * rate;
prevCap = cap;
}
return tax;
}
federalTax(83900) returns 13170. Exactly the number in the table above. The Infinity cap means the top bracket catches everything above $640,600 without special casing.
One design note: I only built the single-filer table here. Married joint, head of household, and married separate each get their own bracket tables in a production calculator. Same function, different data. Keep the brackets as data, not as if-statements, and adding filing statuses is a config change instead of a rewrite.
Part 2: FICA, the tax with a ceiling and a floor problem
FICA is two taxes wearing a trench coat: Social Security and Medicare. They behave very differently, which is why they need separate lines in the code.
Social Security: 6.2%, but only up to $184,500. Earn $184,500 and you pay $11,439. Earn $500,000 and you still pay $11,439. The wage base caps it. This is regressive by design, and it's the reason high earners see their effective FICA rate drop as income climbs.
Medicare: 1.45% on every dollar, no cap. Plus an extra 0.9% on wages above $200,000 (single) or $250,000 (married joint). No cap on the base tax means this one is straightforward to model.
For our $100,000 example, neither edge case triggers:
- Social Security: $100,000 × 6.2% = $6,200
- Medicare: $100,000 × 1.45% = $1,450
But watch what happens at $250,000: Social Security stops at $184,500 × 6.2% = $11,439, while Medicare keeps climbing and picks up the extra 0.9% on the $50,000 above $200,000. Your calculator has to handle both behaviors, so model them as separate functions even though pay stubs lump them as "FICA":
const SS_RATE = 0.062;
const SS_WAGE_BASE = 184500;
const MEDICARE_RATE = 0.0145;
const MEDICARE_EXTRA_RATE = 0.009;
const MEDICARE_EXTRA_THRESHOLD = { single: 200000, marriedJoint: 250000 };
function ficaTaxes(wages, filingStatus = "single") {
const socialSecurity = Math.min(wages, SS_WAGE_BASE) * SS_RATE;
let medicare = wages * MEDICARE_RATE;
const threshold = MEDICARE_EXTRA_THRESHOLD[filingStatus] ?? 200000;
if (wages > threshold) {
medicare += (wages - threshold) * MEDICARE_EXTRA_RATE;
}
return { socialSecurity, medicare };
}
Math.min(wages, SS_WAGE_BASE) is the entire wage-base concept in one expression. I like code that reads like the rule it implements.
Part 3: Pre-tax deductions, the quiet tax cut
Here's where it gets interesting for anyone optimizing. Money you put into a 401(k) or pay toward health insurance premiums comes out before taxes are calculated. Every pre-tax dollar skips federal income tax AND FICA.
Say you contribute $10,000 to your 401(k) on that $100,000 salary. Your taxable wages drop to $90,000:
- Federal taxable: $90,000 - $16,100 = $73,900, and the tax falls to about $10,970 (run the bucket function, it works)
- Social Security: $90,000 × 6.2% = $5,580 (you saved $620 here too)
- Medicare: $90,000 × 1.45% = $1,305
That $10,000 contribution cost you roughly $7,000 in take-home pay, not $10,000. The tax code subsidized the rest. This is the single most useful insight a take-home calculator can show someone, and it's why pre-tax inputs deserve first-class treatment in the UI, not a footnote.
Post-tax deductions (Roth 401(k), union dues, after-tax insurance) are simpler: they reduce your paycheck but not your taxable income. Model them as a flat subtraction at the end.
Part 4: State tax, the wildcard
State income tax ranges from zero (Texas, Florida, Washington, and friends) to over 13% (California, top bracket). Real state systems have their own brackets, but for a calculator that stays maintainable, a flat user-entered rate is the pragmatic choice. Let the user type their effective state rate; precision here matters less than not pretending seven states with no income tax all charge something.
In our $100,000 example we assumed no state tax. Add 5% and you lose another $5,000 a year, about $417 a month. State tax is often the difference between a calculator that feels right and one that feels wrong to the user, so don't skip the input even if the math is simple.
Putting it together
Here's the complete function. It chains everything above: pre-tax deductions reduce the wage base first, then the standard deduction, then federal buckets, then FICA, then state:
const STANDARD_DEDUCTIONS = {
single: 16100,
marriedJoint: 32200,
headOfHousehold: 24150,
marriedSeparate: 16100,
};
function takeHomePay({
salary,
filingStatus = "single",
preTaxDeductions = 0,
stateTaxRate = 0,
}) {
const stdDeduction = STANDARD_DEDUCTIONS[filingStatus];
// Pre-tax items come off the top, THEN the standard deduction
const ficaWages = Math.max(0, salary - preTaxDeductions);
const federalTaxable = Math.max(0, ficaWages - stdDeduction);
const federal = federalTax(federalTaxable);
const { socialSecurity, medicare } = ficaTaxes(ficaWages, filingStatus);
const state = ficaWages * stateTaxRate;
const annual = salary - preTaxDeductions - federal - socialSecurity - medicare - state;
return {
federalTaxable,
federal,
socialSecurity,
medicare,
state,
annualTakeHome: annual,
monthly: annual / 12,
biweekly: annual / 26,
};
}
// Verified against hand calculation:
const r = takeHomePay({ salary: 100000, filingStatus: "single" });
// r.federalTaxable -> 83900
// r.federal -> 13170.00
// r.socialSecurity -> 6200.00
// r.medicare -> 1450.00
// r.annualTakeHome -> 79180.00
// r.monthly -> 6598.33
// r.biweekly -> 3045.38
Test output from node (every figure matches the hand calculation above):
// Federal taxable: 83900
// Federal tax: 13170.00
// Social Security: 6200.00
// Medicare: 1450.00
// Annual take-home: 79180.00
// Monthly: 6598.33
// Biweekly: 3045.38
// SS at $250K (capped): 11439.00 = 184500 x 6.2%
The last line is the wage-base cap kicking in at $250,000, proof the edge case works.
What I'd add next
This is a solid core, but a production calculator has a few more things on the list: per-state bracket tables instead of a flat rate, the additional Medicare tax threshold for married filers ($250,000), local taxes for the unlucky few who owe them, and overtime/bonus withholding quirks. Each one is a small extension of the same pattern: a rule, a data table, a function.
The architecture lesson is the real takeaway. Tax math is a pile of independent rules that happen to apply to the same paycheck. Model each rule as its own function with its own data table, chain them in the right order, and the calculator stays correct as rules change. Bury the rates in nested if-statements and every January becomes a rewrite.
Try it live: https://xanm.com/take-home-pay-calculator/
Educational illustration only, not tax advice. Figures use 2026 federal tax law; verify current rules before making financial decisions.













