Mortgage Payoff Calculator

Mortgage Payoff Calculator

This mortgage payoff calculator helps evaluate how adding extra payments or biweekly payments can save on interest and shorten mortgage term.

If you know the remaining loan term

Use this calculator if the term length of the remaining loan is known and there is information on the original loan – good for new loans or preexisting loans that have never been supplemented with any external payments.

i
Modify the values and click the Calculate button to use
$
years
%
years months
Repayment options:
$
Click Calculate to see results

If you don't know the remaining loan term

Use this calculator if the term length of the remaining loan is not known. The unpaid principal balance, interest rate, and monthly payment values can be found in the monthly or quarterly mortgage statement.

i
Modify the values and click the Calculate button to use
$
$
%
Repayment options:
$
Click Calculate to see results
'; // Savings boxes if (r.interestSavings > 0) { html += '
'; html += '
Interest savings
' + formatCurrency(r.interestSavings) + '
'; html += '
Original: ' + formatCurrency(r.originalRemainingInterest) + '
With payoff: ' + formatCurrency(r.newRemainingInterest) + '
Pay ' + iSavPct + '% less on interest
'; html += '
Time savings
' + formatMonths(r.timeSavingsMonths) + '
'; html += '
Original: ' + formatMonths(r.originalSchedule.length) + '
With payoff: ' + formatMonths(r.newSchedule.length) + '
Payoff ' + tSavPct + '% faster
'; html += '
'; } // Comparison table html += ''; var rows = [ ['Monthly pay', formatCurrencyDecimal(r.originalMonthlyPayment), formatCurrencyDecimal(r.newMonthlyPayment)], ['Total payments', formatCurrencyDecimal(r.originalTotalPayments), formatCurrencyDecimal(r.newTotalPayments)], ['Total interest', formatCurrencyDecimal(r.originalTotalInterest), formatCurrencyDecimal(r.newTotalInterest)], ['Remaining payments', formatCurrencyDecimal(r.originalRemainingPayments), formatCurrencyDecimal(r.newRemainingPayments)], ['Remaining interest', formatCurrencyDecimal(r.originalRemainingInterest), formatCurrencyDecimal(r.newRemainingInterest)], ['Payoff in', formatMonths(r.originalSchedule.length), formatMonths(r.newSchedule.length)] ]; for (var i = 0; i < rows.length; i++) { html += ''; } html += '
OriginalWith payoff
' + rows[i][0] + '' + rows[i][1] + '' + rows[i][2] + '
'; // Chart canvas html += ''; // Amortization toggle html += '
â–¶View Amortization Table
Click to expand
'; html += ''; html += '
'; container.innerHTML = html; // Store result for PDF export window['result_' + containerId] = r; // Draw chart setTimeout(function() { drawChart(canvasId, r); }, 50); } // ===== AMORTIZATION TABLE ===== function renderAmortTable(original, withPayoff, tableId) { var maxLen = Math.max(original.length, withPayoff.length); var totalYears = Math.ceil(maxLen / 12); var html = '
'; html += ''; html += ''; for (var yi = 0; yi < totalYears; yi++) { var year = yi + 1; var si = yi * 12, ei = Math.min(si + 12, maxLen); var oe = original.slice(si, ei), ne = withPayoff.slice(si, ei); var oInt = sumField(oe, 'interest'), oPrin = sumField(oe, 'principal'); var oBal = oe.length > 0 ? oe[oe.length - 1].balance : 0; var nInt = sumField(ne, 'interest'), nPrin = sumField(ne, 'principal'); var nBal = ne.length > 0 ? ne[ne.length - 1].balance : 0; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; // Hidden month rows for (var mi = si; mi < ei; mi++) { var o = original[mi], n = withPayoff[mi]; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; } } html += '
Original (without payoff)With payoff
InterestPrincipalBalanceInterestPrincipalBalance
Year ' + year + '' + (oe.length > 0 ? formatCurrencyDecimal(oInt) : '-') + '' + (oe.length > 0 ? formatCurrencyDecimal(oPrin) : '-') + '' + (oe.length > 0 ? formatCurrencyDecimal(oBal) : '-') + '' + (ne.length > 0 ? formatCurrencyDecimal(nInt) : '-') + '' + (ne.length > 0 ? formatCurrencyDecimal(nPrin) : '-') + '' + (ne.length > 0 ? formatCurrencyDecimal(nBal) : '-') + '
'; return html; } function toggleYearRow(row, si, ei, tableId) { var chevron = row.querySelector('.mpc-chevron'); var isOpen = chevron.classList.contains('open'); var table = document.getElementById(tableId); var year = row.querySelector('td').textContent.trim().replace('Year ', ''); var monthRows = table.querySelectorAll('tr.mpc-month-row[data-year="' + year.replace(/\D/g, '') + '"]'); monthRows.forEach(function(mr) { mr.style.display = isOpen ? 'none' : ''; }); chevron.classList.toggle('open'); } function toggleAmort(id, toggle) { var content = document.getElementById(id); var arrow = toggle.querySelector('span:first-child'); var text = toggle.querySelector('span:last-child'); if (content.style.display === 'none') { content.style.display = 'block'; arrow.textContent = 'â–¼'; text.textContent = 'Click to collapse'; } else { content.style.display = 'none'; arrow.textContent = 'â–¶'; text.textContent = 'Click to expand'; } } // ===== DRAW CHART ===== function drawChart(canvasId, r) { var canvas = document.getElementById(canvasId); if (!canvas) return; var ctx = canvas.getContext('2d'); var w = canvas.width, h = canvas.height; ctx.clearRect(0, 0, w, h); var pad = { top: 30, right: 20, bottom: 50, left: 70 }; var cW = w - pad.left - pad.right, cH = h - pad.top - pad.bottom; var maxM = Math.max(r.originalSchedule.length, r.newSchedule.length); var maxB = r.loanAmount; ctx.strokeStyle = '#ddd'; ctx.lineWidth = 1; ctx.beginPath(); ctx.moveTo(pad.left, pad.top); ctx.lineTo(pad.left, pad.top + cH); ctx.lineTo(pad.left + cW, pad.top + cH); ctx.stroke(); ctx.fillStyle = '#666'; ctx.font = '11px Arial'; ctx.textAlign = 'right'; for (var i = 0; i <= 4; i++) { var val = (maxB / 4) * (4 - i), y = pad.top + (cH / 4) * i; ctx.fillText('$' + Math.round(val / 1000) + 'K', pad.left - 8, y + 4); ctx.strokeStyle = '#eee'; ctx.beginPath(); ctx.moveTo(pad.left, y); ctx.lineTo(pad.left + cW, y); ctx.stroke(); } ctx.textAlign = 'center'; var yInt = Math.max(1, Math.floor(maxM / 12 / 5)); for (var yr = 0; yr <= Math.ceil(maxM / 12); yr += yInt) { ctx.fillText(yr + 'yr', pad.left + (yr * 12 / maxM) * cW, pad.top + cH + 20); } // Original line ctx.strokeStyle = '#3d7a2a'; ctx.lineWidth = 2; ctx.beginPath(); r.originalSchedule.forEach(function(e, i) { var x = pad.left + (i / maxM) * cW, y2 = pad.top + cH - (e.balance / maxB) * cH; if (i === 0) ctx.moveTo(x, y2); else ctx.lineTo(x, y2); }); ctx.stroke(); // New line ctx.strokeStyle = '#5a9a3a'; ctx.lineWidth = 2; ctx.beginPath(); r.newSchedule.forEach(function(e, i) { var x = pad.left + (i / maxM) * cW, y2 = pad.top + cH - (e.balance / maxB) * cH; if (i === 0) ctx.moveTo(x, y2); else ctx.lineTo(x, y2); }); ctx.stroke(); // Legend ctx.font = '12px Arial'; ctx.textAlign = 'left'; var lx = pad.left + 20; ctx.fillStyle = '#3d7a2a'; ctx.fillRect(lx, pad.top + 5, 20, 3); ctx.fillStyle = '#333'; ctx.fillText('Original Balance', lx + 28, pad.top + 10); ctx.fillStyle = '#5a9a3a'; ctx.fillRect(lx, pad.top + 22, 20, 3); ctx.fillStyle = '#333'; ctx.fillText('With Payoff', lx + 28, pad.top + 27); } // ===== CLEAR FORMS ===== function clearKnown() { document.getElementById('k-loanAmount').value = ''; document.getElementById('k-loanTerm').value = ''; document.getElementById('k-interestRate').value = ''; document.getElementById('k-remYears').value = ''; document.getElementById('k-remMonths').value = ''; document.getElementById('k-extraAmount').value = ''; document.getElementById('k-results').innerHTML = '
Click Calculate to see results
'; } function clearUnknown() { document.getElementById('u-principal').value = ''; document.getElementById('u-monthlyPayment').value = ''; document.getElementById('u-interestRate').value = ''; document.getElementById('u-extraAmount').value = ''; document.getElementById('u-results').innerHTML = '
Click Calculate to see results
'; } // ===== PDF EXPORT ===== function exportPDF(containerId, title) { var r = window['result_' + containerId]; if (!r) return; var jsPDF = window.jspdf.jsPDF; var doc = new jsPDF(); doc.setFontSize(18); doc.setFont('helvetica', 'bold'); doc.text('Mortgage Payoff Calculator Results', 105, 20, { align: 'center' }); doc.setFontSize(12); doc.setTextColor(90, 154, 58); doc.text(title, 105, 30, { align: 'center' }); doc.setFontSize(14); doc.text('Payoff in ' + formatMonths(r.newPayoffMonths), 105, 42, { align: 'center' }); doc.setTextColor(0, 0, 0); doc.setFontSize(11); doc.setFont('helvetica', 'normal'); var y = 55; if (r.interestSavings > 0) { doc.text('Interest Savings: ' + formatCurrency(r.interestSavings), 20, y); y += 7; doc.text('Time Savings: ' + formatMonths(r.timeSavingsMonths), 20, y); y += 12; } doc.setFont('helvetica', 'bold'); doc.text('Comparison', 20, y); y += 8; doc.text('', 25, y); doc.text('Original', 120, y); doc.text('With Payoff', 165, y); y += 7; doc.setFont('helvetica', 'normal'); var pdfRows = [ ['Monthly pay', formatCurrencyDecimal(r.originalMonthlyPayment), formatCurrencyDecimal(r.newMonthlyPayment)], ['Total payments', formatCurrencyDecimal(r.originalTotalPayments), formatCurrencyDecimal(r.newTotalPayments)], ['Total interest', formatCurrencyDecimal(r.originalTotalInterest), formatCurrencyDecimal(r.newTotalInterest)], ['Remaining payments', formatCurrencyDecimal(r.originalRemainingPayments), formatCurrencyDecimal(r.newRemainingPayments)], ['Remaining interest', formatCurrencyDecimal(r.originalRemainingInterest), formatCurrencyDecimal(r.newRemainingInterest)], ['Payoff in', formatMonths(r.originalSchedule.length), formatMonths(r.newSchedule.length)] ]; pdfRows.forEach(function(row) { doc.text(row[0], 25, y); doc.text(row[1], 120, y); doc.text(row[2], 165, y); y += 7; }); doc.setFontSize(9); doc.setTextColor(150, 150, 150); doc.text('Generated on ' + new Date().toLocaleString(), 105, 280, { align: 'center' }); doc.save('mortgage-payoff-' + new Date().toISOString().split('T')[0] + '.pdf'); }