Tax Bracket Analyzer for Investors

Enter Your Tax Information (2025 Tax Year)

Your total income before deductions.

Gains from assets held over 1 year.

Eligible dividends taxed at capital gains rates.

Adjustments to income (above-the-line).

Your Tax Analysis (2025 Tax Year)

Gross Income: --
Total Deductions: --
Taxable Income: --
Total Tax Liability: --
Marginal Tax Rate: --
Effective Tax Rate: --

Income Breakdown by Tax Bracket

2025 Federal Income Tax Brackets (Estimated)

These are estimated tax brackets for the 2025 tax year, for illustrative purposes. Always consult official IRS resources or a tax professional for accurate tax planning.

Single Filers

Tax Rate Taxable Income

Married Filing Jointly

Tax Rate Taxable Income

Head of Household

Tax Rate Taxable Income

Please enter valid non-negative numeric values for all fields.

"; showTab('results'); return; } // Calculate total deductions (Standard Deduction + Other Deductions) let totalDeductions = standardDeductions[filingStatus] + otherDeductions; // Ensure deductions don't exceed gross income totalDeductions = Math.min(totalDeductions, grossIncome); // Calculate Adjusted Gross Income (AGI) - simplified for this tool let AGI = grossIncome - otherDeductions; AGI = Math.max(0, AGI); // AGI cannot be negative // Calculate ordinary taxable income before capital gains/qualified dividends let ordinaryTaxableIncome = AGI - standardDeductions[filingStatus]; ordinaryTaxableIncome = Math.max(0, ordinaryTaxableIncome); // Taxable income cannot be negative // Separate ordinary income and capital gains/qualified dividends // For simplicity, assume all qualified dividends are treated as long-term capital gains for tax rate purposes let combinedCapitalGainsIncome = longTermCapitalGains + qualifiedDividends; let totalTaxableIncome = ordinaryTaxableIncome + combinedCapitalGainsIncome; // --- Calculate Tax Liability --- let totalTaxLiability = 0; let marginalRate = 0; let effectiveRate = 0; let bracketBreakdownHtml = ''; // First, calculate tax on ordinary income using progressive brackets let remainingOrdinaryIncome = ordinaryTaxableIncome; let ordinaryTaxDetails = []; for (const bracket of taxBrackets[filingStatus]) { if (remainingOrdinaryIncome <= 0) break; const bracketMin = bracket.min; const bracketMax = bracket.max; const bracketRate = bracket.rate; let taxedInBracket = 0; if (remainingOrdinaryIncome > bracketMax && bracketMax !== Infinity) { taxedInBracket = (bracketMax - Math.max(bracketMin, 0)) + 1; // Amount within the bracket range } else { taxedInBracket = remainingOrdinaryIncome - Math.max(bracketMin, 0); } taxedInBracket = Math.max(0, taxedInBracket); // Ensure not negative const taxInThisBracket = taxedInBracket * bracketRate; totalTaxLiability += taxInThisBracket; remainingOrdinaryIncome -= taxedInBracket; if (taxedInBracket > 0) { ordinaryTaxDetails.push({ type: 'Ordinary Income', amount: taxedInBracket, rate: bracketRate, tax: taxInThisBracket, bracketRange: `\$${bracket.min.toLocaleString()} - \$${bracket.max === Infinity ? 'and up' : bracket.max.toLocaleString()}` }); } // The marginal rate is the rate of the highest bracket income falls into if (ordinaryTaxableIncome > bracketMin) { marginalRate = bracketRate; } } // Now, calculate tax on Long-Term Capital Gains / Qualified Dividends let remainingCapitalGains = combinedCapitalGainsIncome; let capitalGainsTaxDetails = []; // The income for capital gains rates is *total* taxable income, including ordinary income. // The 0%, 15%, 20% thresholds are based on your overall taxable income. // Find the capital gains brackets relevant to the filing status const currentCapitalGainsBrackets = capitalGainsBrackets[filingStatus]; for (const cgBracket of currentCapitalGainsBrackets) { if (remainingCapitalGains <= 0) break; const cgRate = cgBracket.rate; const cgMaxThreshold = cgBracket.max; // This is the total income threshold for this CG rate // Determine how much of the combined capital gains income falls into this CG bracket // This logic is simplified; a true calculation requires more complex integration with ordinary income. // For this tool, we will assume capital gains are taxed *on top* of ordinary income. // This means we need to find which CG bracket the *total* taxable income (ordinary + CG) falls into. let effectiveIncomeForCgBracket = totalTaxableIncome; if (cgMaxThreshold !== Infinity && effectiveIncomeForCgBracket > cgMaxThreshold) { // The amount of capital gains taxed at this rate is limited by the threshold const amountAtThisRate = Math.min(remainingCapitalGains, cgMaxThreshold - (totalTaxableIncome - remainingCapitalGains)); if (amountAtThisRate > 0) { const tax = amountAtThisRate * cgRate; totalTaxLiability += tax; remainingCapitalGains -= amountAtThisRate; capitalGainsTaxDetails.push({ type: 'Long-Term Capital Gains/Qualified Dividends', amount: amountAtThisRate, rate: cgRate, tax: tax, bracketRange: `Total Income up to \$${cgMaxThreshold.toLocaleString()}` }); marginalRate = Math.max(marginalRate, cgRate); // Update marginal rate if higher } } else if (remainingCapitalGains > 0) { // All remaining capital gains fall into this bracket or higher const tax = remainingCapitalGains * cgRate; totalTaxLiability += tax; capitalGainsTaxDetails.push({ type: 'Long-Term Capital Gains/Qualified Dividends', amount: remainingCapitalGains, rate: cgRate, tax: tax, bracketRange: `Total Income \$${(cgBracket.max === Infinity ? 'and up' : (cgBracket.max + 1).toLocaleString())}` }); marginalRate = Math.max(marginalRate, cgRate); // Update marginal rate if higher remainingCapitalGains = 0; // All accounted for } } // Calculate Effective Tax Rate if (totalTaxableIncome > 0) { effectiveRate = (totalTaxLiability / totalTaxableIncome) * 100; } else { effectiveRate = 0; } // --- Display Results --- displayElements.displayGrossIncome.textContent = `$${grossIncome.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; displayElements.displayTotalDeductions.textContent = `$${totalDeductions.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; displayElements.displayTaxableIncome.textContent = `$${totalTaxableIncome.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; displayElements.displayTaxLiability.textContent = `$${totalTaxLiability.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; displayElements.displayMarginalRate.textContent = `${(marginalRate * 100).toFixed(0)}%`; displayElements.displayEffectiveRate.textContent = `${effectiveRate.toFixed(2)}%`; // Build Bracket Breakdown HTML bracketBreakdownHtml += `

Ordinary Income Breakdown:

`; if (ordinaryTaxDetails.length === 0 && ordinaryTaxableIncome === 0) { bracketBreakdownHtml += `

No ordinary taxable income.

`; } else { ordinaryTaxDetails.forEach(detail => { bracketBreakdownHtml += `

Taxed \$${detail.amount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} at ${(detail.rate * 100).toFixed(0)}% (${detail.bracketRange}) = \$${detail.tax.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}

`; }); } if (combinedCapitalGainsIncome > 0) { bracketBreakdownHtml += `

Long-Term Capital Gains / Qualified Dividends Breakdown:

`; if (capitalGainsTaxDetails.length > 0) { capitalGainsTaxDetails.forEach(detail => { bracketBreakdownHtml += `

Taxed \$${detail.amount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })} at ${(detail.rate * 100).toFixed(0)}% (${detail.bracketRange}) = \$${detail.tax.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}

`; }); } else { bracketBreakdownHtml += `

No long-term capital gains/qualified dividends taxed or all fell into 0% bracket.

`; } } displayElements.bracketBreakdown.innerHTML = bracketBreakdownHtml; showTab('results'); // Switch to results tab after calculation } // --- PDF Download Functionality --- controlButtons.downloadPdf.addEventListener('click', function() { try { const { jsPDF } = window.jspdf; const doc = new jsPDF('p', 'mm', 'a4'); const margin = 20; let yPos = margin; // Title doc.setFont("helvetica", "bold"); doc.setFontSize(20); doc.setTextColor(37, 99, 235); /* Blue */ doc.text('Tax Bracket Analysis Report for Investors', doc.internal.pageSize.width / 2, yPos, { align: 'center' }); yPos += 15; // Tax Year and Disclaimer doc.setFont("helvetica", "normal"); doc.setFontSize(10); doc.setTextColor(107, 114, 128); /* Gray */ doc.text('Analysis for 2025 Tax Year (Estimated Brackets)', doc.internal.pageSize.width / 2, yPos, { align: 'center' }); yPos += 7; doc.text('Disclaimer: This report is an estimation for illustrative purposes only and should not be considered tax advice.', doc.internal.pageSize.width / 2, yPos, { align: 'center' }); yPos += 15; // Input Parameters doc.setFont("helvetica", "bold"); doc.setFontSize(14); doc.setTextColor(75, 85, 99); /* Dark Gray */ doc.text('Input Parameters', margin, yPos); yPos += 8; const inputData = [ ["Gross Income", `$${parseFloat(inputElements.grossIncome.value).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`], ["Filing Status", inputElements.filingStatus.options[inputElements.filingStatus.selectedIndex].text], ["Long-Term Capital Gains", `$${parseFloat(inputElements.longTermCapitalGains.value).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`], ["Qualified Dividends", `$${parseFloat(inputElements.qualifiedDividends.value).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`], ["Other Deductions", `$${parseFloat(inputElements.otherDeductions.value).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`] ]; doc.autoTable({ startY: yPos, head: [['Parameter', 'Value']], body: inputData, theme: 'grid', styles: { fontSize: 10, cellPadding: 3, textColor: '#333', lineColor: '#eee', lineWidth: 0.1 }, headStyles: { fillColor: '#eef2ff', textColor: '#374151', fontStyle: 'bold' }, margin: { left: margin, right: margin } }); yPos = doc.autoTable.previous.finalY + 15; // Calculation Results doc.setFont("helvetica", "bold"); doc.setFontSize(14); doc.setTextColor(75, 85, 99); doc.text('Calculation Results', margin, yPos); yPos += 8; const resultsData = [ ["Gross Income", displayElements.displayGrossIncome.textContent], ["Total Deductions", displayElements.displayTotalDeductions.textContent], ["Taxable Income", displayElements.displayTaxableIncome.textContent], ["Total Tax Liability", displayElements.displayTaxLiability.textContent], ["Marginal Tax Rate", displayElements.displayMarginalRate.textContent], ["Effective Tax Rate", displayElements.displayEffectiveRate.textContent] ]; doc.autoTable({ startY: yPos, head: [['Metric', 'Value']], body: resultsData, theme: 'grid', styles: { fontSize: 10, cellPadding: 3, textColor: '#333', lineColor: '#eee', lineWidth: 0.1 }, headStyles: { fillColor: '#eef2ff', textColor: '#374151', fontStyle: 'bold' }, margin: { left: margin, right: margin } }); yPos = doc.autoTable.previous.finalY + 15; // Income Breakdown by Tax Bracket doc.setFont("helvetica", "bold"); doc.setFontSize(14); doc.setTextColor(75, 85, 99); doc.text('Income Breakdown by Tax Bracket', margin, yPos); yPos += 8; // Capture the bracket breakdown HTML content const bracketBreakdownHtml = displayElements.bracketBreakdown.innerHTML; // For autoTable, we need structured data, so parse the breakdown. // This is a simplified parsing. For complex dynamic content, consider html2canvas or more robust PDF generation. const breakdownLines = bracketBreakdownHtml.split('

').filter(line => line.trim() !== '').map(line => { const text = line.replace(/<[^>]*>/g, '').trim(); // Remove HTML tags return [text]; }); doc.autoTable({ startY: yPos, body: breakdownLines, theme: 'plain', // Use plain theme for breakdown text styles: { fontSize: 9, cellPadding: 2, textColor: '#333' }, columnStyles: { 0: { cellWidth: 'wrap' } }, margin: { left: margin, right: margin } }); yPos = doc.autoTable.previous.finalY + 15; // Add Tax Brackets Table doc.addPage(); // Start new page for brackets if content is long yPos = margin; // Reset Y position for new page doc.setFont("helvetica", "bold"); doc.setFontSize(16); doc.setTextColor(37, 99, 235); doc.text('2025 Federal Income Tax Brackets (Estimated)', doc.internal.pageSize.width / 2, yPos, { align: 'center' }); yPos += 15; // Single Filers Table doc.setFont("helvetica", "bold"); doc.setFontSize(12); doc.setTextColor(75, 85, 99); doc.text('Single Filers', margin, yPos); yPos += 8; const singleBracketData = taxBrackets["single"].map(b => [ `${(b.rate * 100).toFixed(0)}%`, b.max === Infinity ? `\$${b.min.toLocaleString()} or more` : `\$${b.min.toLocaleString()} to \$${b.max.toLocaleString()}` ]); doc.autoTable({ startY: yPos, head: [['Tax Rate', 'Taxable Income']], body: singleBracketData, theme: 'grid', styles: { fontSize: 9, cellPadding: 2, textColor: '#333', lineColor: '#eee', lineWidth: 0.1 }, headStyles: { fillColor: '#eef2ff', textColor: '#374151', fontStyle: 'bold' }, margin: { left: margin, right: margin } }); yPos = doc.autoTable.previous.finalY + 10; // Married Filing Jointly Table doc.setFont("helvetica", "bold"); doc.setFontSize(12); doc.setTextColor(75, 85, 99); doc.text('Married Filing Jointly', margin, yPos); yPos += 8; const marriedJointlyBracketData = taxBrackets["marriedJointly"].map(b => [ `${(b.rate * 100).toFixed(0)}%`, b.max === Infinity ? `\$${b.min.toLocaleString()} or more` : `\$${b.min.toLocaleString()} to \$${b.max.toLocaleString()}` ]); doc.autoTable({ startY: yPos, head: [['Tax Rate', 'Taxable Income']], body: marriedJointlyBracketData, theme: 'grid', styles: { fontSize: 9, cellPadding: 2, textColor: '#333', lineColor: '#eee', lineWidth: 0.1 }, headStyles: { fillColor: '#eef2ff', textColor: '#374151', fontStyle: 'bold' }, margin: { left: margin, right: margin } }); yPos = doc.autoTable.previous.finalY + 10; // Head of Household Table doc.setFont("helvetica", "bold"); doc.setFontSize(12); doc.setTextColor(75, 85, 99); doc.text('Head of Household', margin, yPos); yPos += 8; const headOfHouseholdBracketData = taxBrackets["headOfHousehold"].map(b => [ `${(b.rate * 100).toFixed(0)}%`, b.max === Infinity ? `\$${b.min.toLocaleString()} or more` : `\$${b.min.toLocaleString()} to \$${b.max.toLocaleString()}` ]); doc.autoTable({ startY: yPos, head: [['Tax Rate', 'Taxable Income']], body: headOfHouseholdBracketData, theme: 'grid', styles: { fontSize: 9, cellPadding: 2, textColor: '#333', lineColor: '#eee', lineWidth: 0.1 }, headStyles: { fillColor: '#eef2ff', textColor: '#374151', fontStyle: 'bold' }, margin: { left: margin, right: margin } }); yPos = doc.autoTable.previous.finalY + 10; doc.save('Tax_Bracket_Analysis_Report.pdf'); } catch (error) { console.error("Error generating PDF:", error); // In a real application, you'd show a user-friendly modal here instead of just logging. // For example: // const errorModal = document.createElement('div'); // errorModal.textContent = "Failed to generate PDF. Please try again or check console for details."; // errorModal.style.cssText = "position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:red;color:white;padding:15px;border-radius:8px;z-index:9999;"; // document.body.appendChild(errorModal); // setTimeout(() => document.body.removeChild(errorModal), 5000); } }); // Initial population of tax bracket tables populateTaxBracketTables(); // Initial tab display showTab('inputs'); });
Scroll to Top