If you've ever built a salary calculator, you know the deceptively simple question "what will I actually be paid?" hides a small mountain of conditional logic. Now try answering it for two countries. The UK's PAYE system and the US withholding system look similar on the surface — both deduct tax before the money hits your bank account — but underneath they model income in fundamentally different ways. As developers, the differences matter because they change how we structure our data, our brackets, and our edge cases.
Try it live: curious how UK PAYE works out on a real salary? The free UK Salary After Tax Calculator at FreeKalc runs all of this logic instantly — no signup needed.
The mental model: two philosophies
UK PAYE (Pay As You Earn) is cumulative. HMRC gives you a personal allowance (£12,570 for 2026/27) spread evenly across the year. Each pay period, your employer looks at your total earnings for the tax year so far, your total allowance used, and your total tax paid — then adjusts. Overpay in one month? PAYE self-corrects next month automatically. From a code perspective, this is a stateful, running-total algorithm.
US withholding is per-period estimation. The IRS publishes percentage-method tables, and your employer applies them to each paycheck as if every pay period were identical, with no memory of earlier periods. Reconciliation happens once a year when you file a tax return. From a code perspective, it's a stateless, pure function: same input → same output, every time.
That one distinction — stateful vs stateless — drives most of the interesting code below.
Modelling UK income tax in JavaScript
UK income tax in England (2026/27 rates) is a stack of bands applied to taxable income above the personal allowance:
const UK_BANDS = [
{ limit: 37700, rate: 0.20 }, // basic rate
{ limit: 125140, rate: 0.40 }, // higher rate
{ limit: Infinity, rate: 0.45 }// additional rate
];
const PERSONAL_ALLOWANCE = 12570;
function ukIncomeTax(annualSalary) {
let taxable = Math.max(0, adjustedForAllowanceTaper(annualSalary) - PERSONAL_ALLOWANCE);
let tax = 0;
let prevLimit = 0;
for (const band of UK_BANDS) {
const width = Math.min(taxable, band.limit - prevLimit);
if (width <= 0) break;
tax += width * band.rate;
taxable -= width;
prevLimit = band.limit;
}
return tax;
}
javascript
Notice the first trap for developers: the personal allowance tapers. Earn over £100,000 and you lose £1 of allowance for every £2 above it, hitting zero at £125,140. That's the adjustedForAllowanceTaper call, and it creates the infamous 60% effective marginal rate between £100k and £125,140 (40% income tax + lost allowance). Skip it and your calculator is wrong for exactly the high earners most likely to double-check your numbers.
function adjustedForAllowanceTaper(annualSalary) {
// returns the effective personal allowance after taper
if (annualSalary <= 100000) return PERSONAL_ALLOWANCE;
const reduction = Math.min(PERSONAL_ALLOWANCE, (annualSalary - 100000) / 2);
return PERSONAL_ALLOWANCE - reduction;
}
Wait — my ukIncomeTax signature passes salary into the taper function but then subtracts the allowance inside. Cleaner to have the taper function return the effective allowance, as above, and subtract that. Small refactor, worth doing in real code.
## Don't forget National Insurance
The UK's sneaky second tax: National Insurance Contributions. Employees pay 8% on earnings between £12,570 and £50,270, then 2% above that. Crucially, NI is calculated per pay period, not cumulatively — unlike income tax. Two colleagues earning the same annual salary but paid monthly vs weekly can pay different NI. Your data model needs to know the pay frequency:
function ukNationalInsurance(annualSalary, periodsPerYear = 12) {
const perPeriod = annualSalary / periodsPerYear;
const lower = 12570 / periodsPerYear;
const upper = 50270 / periodsPerYear;
let ni = 0;
if (perPeriod > lower) ni += (Math.min(perPeriod, upper) - lower) * 0.08;
if (perPeriod > upper) ni += (perPeriod - upper) * 0.02;
return ni * periodsPerYear;
}
## Modelling US federal withholding
The US system is stateless per paycheck. Using the IRS percentage method (simplified — real tables have more rows):
const US_BRACKETS_SINGLE_2026 = [
{ limit: 11925, rate: 0.10 },
{ limit: 48475, rate: 0.12 },
{ limit: 103350, rate: 0.22 },
{ limit: 197300, rate: 0.24 },
{ limit: 250525, rate: 0.32 },
{ limit: 626350, rate: 0.35 },
{ limit: Infinity, rate: 0.37 }
];
const STANDARD_DEDUCTION_SINGLE = 15000; // 2026, approx
function usFederalWithholding(annualSalary, periodsPerYear = 26) {
const taxable = Math.max(0, annualSalary - STANDARD_DEDUCTION_SINGLE);
const tax = applyBrackets(taxable, US_BRACKETS_SINGLE_2026);
return tax; // annualized; divide by periods for per-paycheck
}
But here's the architectural difference that bites: the US has layers the UK doesn't. Federal is only the start. You also need:
- State income tax — 0% in Texas/Florida, 13.3% top rate in California. That's 50 different bracket tables, or at minimum a state lookup.
- FICA — Social Security 6.2% up to the wage base ($184,500 for 2026), Medicare 1.45% uncapped, plus 0.9% additional Medicare over $200k.
const SS_WAGE_BASE = 184500;
function usFica(annualSalary) {
const socialSecurity = Math.min(annualSalary, SS_WAGE_BASE) * 0.062;
const medicare = annualSalary * 0.0145;
const additional = Math.max(0, annualSalary - 200000) * 0.009;
return socialSecurity + medicare + additional;
}
So a UK take-home function needs: income tax + NI. A US one needs: federal + state + FICA. The US version has more dimensions (filing status, state, pre-tax deductions like 401(k)); the UK version has more temporal complexity (cumulative PAYE, allowance taper).
Edge cases that will break your calculator
- The £100k–£125k taper zone (UK). As shown above — effective 60% marginal rate. Test salaries at £99,999, £100,001, and £125,140 exactly.
- Marriage/filing status (US). "Single" vs "Married filing jointly" changes every bracket and the standard deduction. Never hardcode one.
- Pay frequency (UK NI). Monthly vs weekly changes National Insurance. Always ask.
- Pre-tax deductions (US). 401(k) contributions reduce federal taxable income and FICA differently (401(k) is still subject to FICA). One input, two different treatment paths.
- Scotland. Yes, really — Scotland sets its own income tax bands (starter, basic, intermediate, advanced, top). If your UK calculator ignores Scotland, a chunk of users get wrong answers.
- Rounding. HMRC rounds per pay period; cumulative rounding differences of a few pence per year are normal and expected. Don't chase exact penny-matching with HMRC's own calculator — it's a known discrepancy, not a bug.
Takeaways for your code
- Model the UK as cumulative state (year-to-date totals) and the US as a stateless function — the tax systems literally suggest your architecture.
- Keep bracket tables as data, not code. Rates change every year; a config array you swap out beats a rewrite.
- The allowance taper and FICA wage base are the two most commonly skipped details. They're also the ones financially literate users check first.
Building this yourself is a great exercise in turning legislation into logic — conditionals, accumulators, and lookup tables doing real work. But if you just need the answer for an actual payslip, the UK Salary Calculator at FreeKalc already handles the bands, the taper, National Insurance, and Scottish rates. Free, no signup, and the math is all client-side — which, as we've just seen, is exactly how tax math should run.
Top comments (0)