DEV Community

Keelsheet
Keelsheet

Posted on

The math behind US quarterly estimated taxes, in 50 lines of JavaScript

If you take contract work in the US, nobody withholds tax from your invoices. The IRS still expects you to pay during the year, in four installments, using Form 1040-ES. Most explanations of "how much" stop at a rule of thumb like "save 25 to 30%". That is a fine starting point, but the actual math is short enough to write as code, and writing it is the fastest way to understand where your money goes.

This post builds a small estimator for the simple case: a single filer with one sole-proprietor business, the standard deduction, and no W-2 job. It uses the 2026 federal figures, which are the ones that matter for the payment due January 15, 2027.

Here is the whole thing. We'll walk through it piece by piece below.

// 2026 federal figures, single filer (IRS Rev. Proc. 2025-32, SSA)
const Y = {
  ssWageBase: 184500,
  stdDeduction: 16100,
  brackets: [[12400, 0.10], [50400, 0.12], [105700, 0.22], [201775, 0.24],
             [256225, 0.32], [640600, 0.35], [Infinity, 0.37]],
};
const cents = (n) => Math.round(n * 100) / 100;

function seTax(profit) {
  const base = Math.max(0, profit) * 0.9235;  // net earnings from SE
  if (base < 400) return 0;                   // below $400: no SE tax
  const socialSecurity = Math.min(base, Y.ssWageBase) * 0.124;
  const medicare = base * 0.029;
  return socialSecurity + medicare;
}

function incomeTax(taxable) {
  let tax = 0, floor = 0;
  for (const [top, rate] of Y.brackets) {
    if (taxable > floor) tax += (Math.min(taxable, top) - floor) * rate;
    floor = top;
  }
  return tax;
}

function estimate({ profit, otherIncome = 0, adjustments = 0, qbiAdjustments = 0, credits = 0 }) {
  const se = seTax(profit);
  const agi = profit + otherIncome - se / 2 - adjustments;
  const beforeQbi = Math.max(0, agi - Y.stdDeduction);
  const qbi = Math.max(0, profit - se / 2 - qbiAdjustments);
  let qbiDed = Math.min(0.2 * qbi, 0.2 * beforeQbi);
  if (qbi >= 1000) qbiDed = Math.max(qbiDed, 400); // 2026+ minimum
  qbiDed = Math.min(qbiDed, beforeQbi);
  const taxable = beforeQbi - qbiDed;
  const total = Math.max(0, incomeTax(taxable) - credits) + se;
  return { se: cents(se), taxable: cents(taxable), total: cents(total) };
}

function safeHarbor(thisYearTax, lastYearTax, lastYearAgi) {
  const priorPct = lastYearAgi > 150000 ? 1.10 : 1.00;
  return Math.min(thisYearTax * 0.90, lastYearTax * priorPct);
}

function plan(input) {
  const est = estimate(input);
  const target = input.lastYearTax == null ? est.total
    : safeHarbor(est.total, input.lastYearTax, input.lastYearAgi);
  return { ...est, target: cents(target), perQuarter: cents(target / 4) };
}
console.log(plan({ profit: 90000 }));
console.log(plan({ profit: 90000, lastYearTax: 16000, lastYearAgi: 70000 }));
Enter fullscreen mode Exit fullscreen mode

Save it as tax.mjs and run node tax.mjs. No dependencies.

Step 1: Self-employment tax

As an employee, you pay 7.65% for Social Security and Medicare and your employer pays the other 7.65%. When you work for yourself, you pay both halves: 15.3%, split into 12.4% Social Security and 2.9% Medicare (IRS Topic 554).

Two details trip people up:

  1. It applies to 92.35% of profit, not 100%. Schedule SE first reduces your net profit by 7.65%, which mirrors the fact that an employer's share isn't counted as an employee's wages. That's the * 0.9235.
  2. Social Security has a cap. The 12.4% part only applies up to the Social Security wage base: $184,500 for 2026. SSA has set the 2027 base at $192,300. Medicare's 2.9% has no cap.

If net earnings from self-employment are under $400, there's no SE tax at all, which is the early return.

const socialSecurity = Math.min(base, Y.ssWageBase) * 0.124;
const medicare = base * 0.029;
Enter fullscreen mode Exit fullscreen mode

For $90,000 of profit: $90,000 x 0.9235 = $83,115, and $83,115 x 15.3% = $12,716.60. That's about 14.1% of profit before income tax even enters the picture.

Step 2: Adjusted gross income, with half of SE tax removed

You can deduct half of your SE tax when figuring income tax. It's an adjustment to income, so you get it whether or not you itemize.

const agi = profit + otherIncome - se / 2 - adjustments;
Enter fullscreen mode Exit fullscreen mode

adjustments is for things like SEP-IRA contributions or self-employed health insurance. For our example with no other income: $90,000 - $6,358.30 = $83,641.70. The business-related part of those (SEP contributions and self-employed health insurance tied to this business) also reduces QBI, so pass it again as qbiAdjustments; personal items like student-loan interest go only in adjustments. Thanks to a reader for catching this.

Step 3: Standard deduction and the QBI deduction

Subtract the 2026 standard deduction for a single filer, $16,100, and you have $67,541.70.

Then there's the qualified business income (QBI) deduction, which many quick calculators skip. Eligible sole proprietors can deduct up to 20% of their qualified business income, limited to 20% of taxable income before this deduction (IRS QBI deduction page). Starting in 2026 there's also a $400 minimum deduction for active businesses with at least $1,000 of QBI.

let qbiDed = Math.min(0.2 * qbi, 0.2 * beforeQbi);
if (qbi >= 1000) qbiDed = Math.max(qbiDed, 400);
Enter fullscreen mode Exit fullscreen mode

Here QBI is $90,000 - $6,358.30 = $83,641.70, so 20% of it is $16,728.34. The taxable-income limit is 20% x $67,541.70 = $13,508.34, which is smaller, so that's the deduction. Taxable income: $54,033.36.

Above $201,750 of taxable income (2026, single), extra limits kick in for some businesses. The code doesn't model those, so treat results up there as rough.

Step 4: Brackets

US brackets are marginal. Each rate applies only to the slice of income inside its band. The loop walks the bands and taxes each slice:

for (const [top, rate] of Y.brackets) {
  if (taxable > floor) tax += (Math.min(taxable, top) - floor) * rate;
  floor = top;
}
Enter fullscreen mode Exit fullscreen mode

With the 2026 single brackets (Rev. Proc. 2025-32):

  • 10% on the first $12,400 = $1,240.00
  • 12% on $12,400 to $50,400 = $4,560.00
  • 22% on $50,400 to $54,033.36 = $799.34

Income tax: $6,599.34. Credits come off this number, not off taxable income.

Add SE tax and the total federal bill is $19,315.93, about 21.5% of profit. Divide by four and you get $4,828.98 per quarter. That's the first line of output.

Step 5: Safe harbor, the part that actually saves you stress

Here is what most people don't know: you don't have to predict this year perfectly. You generally avoid the underpayment penalty if your withholding plus on-time estimated payments cover the smaller of:

  • 90% of this year's tax, or
  • 100% of last year's tax (110% if last year's AGI was over $150,000, or $75,000 if married filing separately).

That's from IRS Publication 505. Last year's tax is a known number, so it gives you a fixed target. You still owe the full tax, but you pay the rest in April with your return instead of paying a penalty.

function safeHarbor(thisYearTax, lastYearTax, lastYearAgi) {
  const priorPct = lastYearAgi > 150000 ? 1.10 : 1.00;
  return Math.min(thisYearTax * 0.90, lastYearTax * priorPct);
}
Enter fullscreen mode Exit fullscreen mode

In the second example, last year's total tax was $16,000 on $70,000 of AGI. 90% of this year is $17,384.34; 100% of last year is $16,000. The smaller one wins, so the target is $16,000, or $4,000 per quarter. The remaining $3,315.93 is due when you file.

A related rule: if you'll owe less than $1,000 after withholding and credits, the penalty doesn't apply at all.

Step 6: The quarterly split

The IRS treats your required annual payment as four equal installments, so target / 4 is the default. For 2026 income the due dates were April 15, June 15 and September 15, 2026, with the last one on January 15, 2027.

If your income is lumpy, say most of it lands in Q4, equal payments can overpay early. Form 2210's annualized income installment method lets you match payments to when you earned the money. That's beyond 50 lines.

What this deliberately leaves out

Being honest about scope matters more than the code:

  • State income tax. Zero in some states, significant in others.
  • W-2 wages. A day job uses up part of the Social Security wage base first and adds withholding. Withholding counts toward safe harbor, and it's treated as paid evenly through the year.
  • Other filing statuses, capital gains, the 0.9% Additional Medicare Tax above $200,000, AMT, and the QBI limits above the threshold.
  • Rounding. IRS forms round to whole dollars; this rounds to cents.

If any of those apply to you, the structure still holds. You just need more inputs.

Takeaways

  • SE tax is roughly 14.1% of profit by itself. Plan for it separately from income tax.
  • Half of SE tax and (for many) the QBI deduction both reduce income tax. Skip them and you'll overestimate.
  • Safe harbor turns a guessing game into a known number. Use last year's return.

If you'd rather not run code, we put the same math in a free in-browser calculator: keelsheet.com/calculators/quarterly-estimated-tax.

We make Keelsheet, a spreadsheet version of this.

Not tax advice. Check your numbers against Form 1040-ES or with a tax professional.

Top comments (2)

Collapse
 
arhancanli profile image
Arhan Canli •

Checked the $90,000 example by hand and it holds: SE tax 12,716.60, taxable 54,033.36, income tax 6,599.34, total 19,315.93 (the cent difference is rounding). One place the code drifts from Form 8995: qbi only subtracts half the SE tax, but QBI is also reduced by the deductible SE health insurance and by retirement contributions attributable to the business, and adjustments never reaches it.

In the example it doesn't show, because the taxable-income cap binds. It starts to matter once other income pushes that cap up. I ran profit 90,000, otherIncome 30,000, a 20,000 SEP-IRA contribution: the code takes a 15,508.34 QBI deduction, but 20% of (83,641.70 - 20,000) is 12,728.34. Income tax goes from 8,359.34 to 8,970.94, so the quarterly target is about $150 too low.

Fix is one line: const qbi = Math.max(0, profit - se / 2 - adjustments), if adjustments only holds those business-attributable items. Does the Keelsheet calculator separate those from personal adjustments like student-loan interest?

Collapse
 
keelsheet profile image
Keelsheet •

Good catch, and thanks for checking it by hand. You're right: Form 8995 reduces QBI by the deductible part of SE tax, self-employed health insurance, and retirement contributions attributable to the business, and the snippet only takes out half the SE tax. We reran your case and get the same numbers: a 15,508.34 deduction vs 12,728.34, income tax 8,359.34 vs 8,970.94, so roughly $150 a quarter short.

To your question: yes, the calculator on our site keeps them apart. Total adjustments go in one field, and a second field asks how much of that is business-related, and only that part comes off QBI. So student-loan interest still lowers AGI but leaves QBI alone. The post dropped that to stay at 50 lines, which was the wrong place to cut. We're updating the snippet to take a separate input:

const qbi = Math.max(0, profit - se / 2 - qbiAdjustments);
Enter fullscreen mode Exit fullscreen mode