Debt Avalanche Calculator — Optimize Your Debt Payoff Order & Save Interest

Use our Debt Avalanche Calculator to prioritize high-APR balances, minimize total interest, and estimate your exact debt-free date. Add multiple debts, set minimums, and apply extra payments.

Budget & Strategy

Tell the calculator how much you can pay each month and how to prioritize debts.

Debts

Name Balance APR (%) Minimum Notes Actions

How to Use This Calculator

Start by listing every balance you want to pay off, including minimum payments and APRs. Then declare how much you can comfortably pay each month. Choose Avalanche to attack the highest APR first, or Snowball to pay off the smallest balance for motivation.

  • Add a row for every debt with an accurate balance, APR, and minimum payment so the model knows where the money must go.
  • Set a monthly budget that includes every minimum payment plus any extra you can contribute.
  • Select the payoff strategy that fits your goals (Avalanche minimizes interest; Snowball delivers quick wins).
  • Click Calculate to see the debt-free date, interest total, and a month-by-month schedule.

Methodology

The engine simulates each month, paying every open debt its minimum, then pouring the remaining budget into the prioritized account. Interest is charged monthly (APR / 12). The schedule stops once every balance reaches zero or after 600 months to keep the table manageable.

Results use standard amortization formulas. They are estimates; actual statements can differ due to timing, fees, and daily compounding. Always confirm with your lender.

Full original guide (expanded)

Ad placement

Ad Placement (300×600)

Why this calculator is different

  • Exact month-by-month avalanche vs snowball modeling.
  • Inline WCAG-compliant validation and accessible tooltips.
  • Clear, printable schedule for professional planning.
Formulas

The avalanche engine applies the minimum payment to every debt, then assigns the remaining budget to the prioritized account. Interest accrues monthly.

For a given debt \(i\) with monthly rate \(r_i = \frac{\text{APR}_i}{12}\) and balance \(B_{i,t}\):

Monthly interest: \(I_{i,t} = B_{i,t} r_i\)

Principal reduction: \(P_{i,t} = \max(0, \text{payment}_{i,t} - I_{i,t})\)

Next balance: \(B_{i,t+1} = \max(0, B_{i,t} + I_{i,t} - \text{payment}_{i,t})\)

Strategy order: Avalanche sorts by APR descending; Snowball by balance ascending.

Variable guide
  • P = principal (currency)
  • r = periodic interest rate (APR ÷ 12)
  • n = total payments (months)
  • M = periodic payment
Citations

Paying down debt — consumerfinance.gov · Accessed 2026-01-19
https://www.consumerfinance.gov/

Getting Out of Debt — ftc.gov · Accessed 2026-01-19
https://www.ftc.gov/

Home — calcdomain.com · Accessed 2026-01-19
https://calcdomain.com/

Finance — calcdomain.com · Accessed 2026-01-19
https://calcdomain.com/finance

Loans & Debt — calcdomain.com · Accessed 2026-01-19
https://calcdomain.com/subcategories/finance-loans-debt

Credit Card Payoff — calcdomain.com · Accessed 2026-01-19
https://calcdomain.com/credit-card-payoff-calculator

Loan Amortization — calcdomain.com · Accessed 2026-01-19
https://calcdomain.com/loan-amortization

APR Calculator — calcdomain.com · Accessed 2026-01-19
https://calcdomain.com/apr

Changelog
  • v0.1.0-draft — Initial audit spec draft generated from HTML extraction (review required).
  • v0.1.0-draft — Verify formulas match the calculator engine and convert any text-only formulas to LaTeX.
  • v0.1.0-draft — Confirm sources are authoritative and relevant to the calculator methodology.
Verified by Ugo Candido Last Updated: 2026-01-19 Version 0.1.0-draft
Version 1.5.0
+ round2(safe).toFixed(2); } }; const fmtMonth = (date) => new Intl.DateTimeFormat('en-US', { month: 'short', year: 'numeric' }).format(date); function uniqueId() { if (window.crypto && crypto.randomUUID) return crypto.randomUUID(); return 'row-' + Math.random().toString(36).slice(2, 10); } function createDebtRow(debt = {}) { const rowId = uniqueId(); const tr = document.createElement('tr'); tr.dataset.rowId = rowId; tr.innerHTML = `
`; attachRowEvents(tr); els.debtsBody.appendChild(tr); } function attachRowEvents(row) { row.querySelectorAll('input').forEach((input) => { input.addEventListener('input', debouncedUpdate); input.addEventListener('blur', () => { const info = buildRowInfo(row); validateRow(info); }); }); row.addEventListener('click', (event) => { const action = event.target.closest('button[data-act]'); if (!action) return; const info = buildRowInfo(row); if (action.dataset.act === 'del') { row.remove(); const debouncedUpdate = debounce(update, 100); document.querySelectorAll('#inputsCard input, #inputsCard select, #inputsCard textarea') .forEach((el) => { el.addEventListener('input', debouncedUpdate); el.addEventListener('change', debouncedUpdate); }); update(); } else if (action.dataset.act === 'dup') { addDebtRow(info.values); update(); } }); } function buildRowInfo(row) { const get = (field) => row.querySelector(`[data-field="${field}"]`); const balance = parseFloat(get('balance').value); const apr = parseFloat(get('apr').value); const min = parseFloat(get('min').value); return { row, values: { name: (get('name').value || '').trim(), balance: Number.isFinite(balance) ? balance : NaN, apr: Number.isFinite(apr) ? apr : NaN, min: Number.isFinite(min) ? min : NaN, note: (get('note').value || '').trim() } }; } function resetRowErrors() { els.debtsBody.querySelectorAll('p[data-error]').forEach((el) => { el.classList.add('hidden'); el.textContent = ''; }); } function validateRow(rowInfo) { const { row, values } = rowInfo; const { balance, apr, min } = values; let ok = true; const setMsg = (field, msg) => { const errEl = row.querySelector(`[data-error="${field}"]`); if (errEl) { if (msg) { errEl.textContent = msg; errEl.classList.remove('hidden'); } else { errEl.classList.add('hidden'); } } }; if (!Number.isFinite(balance) || balance <= 0) { ok = false; setMsg('balance', 'Enter a positive balance.'); } else { setMsg('balance', ''); } if (!Number.isFinite(apr) || apr < 0) { ok = false; setMsg('apr', 'APR must be 0 or higher.'); } else { setMsg('apr', ''); } if (!Number.isFinite(min) || min < 0) { ok = false; setMsg('min', 'Minimum must be 0 or greater.'); } else if (Number.isFinite(balance) && min > balance) { ok = false; setMsg('min', 'Minimum cannot exceed the balance.'); } else { setMsg('min', ''); } return ok; } function parseInputs() { const rows = Array.from(els.debtsBody.querySelectorAll('tr')).map(buildRowInfo); const budget = parseFloat(els.monthlyBudget.value); return { rows, budget, strategy: els.strategy.value }; } function showBudgetError(message) { if (!message) { els.budgetError.classList.add('hidden'); els.budgetError.textContent = ''; return; } els.budgetError.textContent = message; els.budgetError.classList.remove('hidden'); } function validate(inputs) { const errors = []; let allRowsValid = true; inputs.rows.forEach((rowInfo) => { if (!validateRow(rowInfo)) allRowsValid = false; }); const positiveDebts = inputs.rows.filter(r => Number.isFinite(r.values.balance) && r.values.balance > 0); if (positiveDebts.length === 0) { errors.push('Add at least one debt with a positive balance.'); } if (!Number.isFinite(inputs.budget) || inputs.budget <= 0) { errors.push('Monthly budget must be greater than 0.'); showBudgetError('Enter a monthly budget greater than 0.'); } else { showBudgetError(''); } if (!allowedStrategies.has(inputs.strategy)) { errors.push('Payment strategy is invalid.'); } const minSum = positiveDebts.reduce((sum, row) => sum + row.values.min, 0); inputs.minSum = minSum; if (Number.isFinite(inputs.budget) && inputs.budget < minSum) { errors.push('Monthly budget must cover the sum of minimum payments.'); const minMsg = fmtCurrency(minSum, currency); showBudgetError(`Budget must cover the minimums (${minMsg}).`); } else if (errors.indexOf('Monthly budget must cover the sum of minimum payments.') === -1) { if (!Number.isFinite(inputs.budget) || inputs.budget <= 0) { // already handled } else { showBudgetError(''); } } inputs.cleanedDebts = positiveDebts.map((row) => row.values); return { ok: errors.length === 0 && allRowsValid, errors }; } function compute(inputs) { const ds = inputs.cleanedDebts.map((d) => ({ balance: +d.balance, apr: +d.apr, min: +d.min })); const monthlyRates = ds.map(d => d.apr / 100 / 12); const sumMins = ds.reduce((s, d) => s + d.min, 0); if (inputs.budget < sumMins) { throw new Error('Budget is less than the sum of minimums. Increase your budget.'); } const schedule = []; let month = 0; let totalInterest = 0; let totalPaid = 0; const order = () => { if (inputs.strategy === 'snowball') { ds.sort((a, b) => a.balance - b.balance || b.apr - a.apr); } else { ds.sort((a, b) => b.apr - a.apr || a.balance - b.balance); } }; let guard = 0; order(); while (ds.some(d => d.balance > 0) && guard < 1200) { guard++; month++; let budgetLeft = inputs.budget; ds.forEach((debt) => { if (debt.balance <= 0) return; const payment = Math.min(debt.min, debt.balance); debt.balance -= payment; budgetLeft -= payment; totalPaid += payment; }); order(); const target = ds.find(d => d.balance > 0); if (target && budgetLeft > 0) { const extra = Math.min(budgetLeft, target.balance); target.balance -= extra; budgetLeft -= extra; totalPaid += extra; } let interestThisMonth = 0; ds.forEach((debt, idx) => { if (debt.balance <= 0) return; const interest = debt.balance * monthlyRates[idx]; debt.balance += interest; interestThisMonth += interest; }); totalInterest += interestThisMonth; const totalBalance = ds.reduce((sum, debt) => sum + Math.max(0, debt.balance), 0); schedule.push({ month, payment: round2(inputs.budget - budgetLeft), interest: round2(interestThisMonth), principal: round2((inputs.budget - budgetLeft) - interestThisMonth), balance: round2(totalBalance) }); ds.forEach((debt) => { if (debt.balance < 0.005) debt.balance = 0; }); if (schedule.length > scheduleCap) break; } return { months: month, totalInterest: round2(totalInterest), totalPaid: round2(totalPaid), schedule }; } function format(outputs, inputs) { if (!outputs) return null; const today = new Date(); const debtfree = new Date(today.getFullYear(), today.getMonth() + outputs.months, 1); return { debtFreeLabel: fmtMonth(debtfree), monthsLabel: outputs.months.toString(), interestLabel: fmtCurrency(outputs.totalInterest, currency), totalPaidLabel: fmtCurrency(outputs.totalPaid, currency), budgetLabel: fmtCurrency(inputs.budget, currency), minSumLabel: fmtCurrency(inputs.minSum || 0, currency), schedule: outputs.schedule, currency }; } function render(formatted, errors) { if (!formatted) { els.totalMonthly.textContent = '—'; els.kpiMonths.textContent = '—'; els.kpiInterest.textContent = fmtCurrency(0, currency); els.kpiTotal.textContent = fmtCurrency(0, currency); els.kpiBudget.textContent = fmtCurrency(0, currency); els.kpiMinSum.textContent = fmtCurrency(0, currency); currentSchedule = []; els.downloadCsv.disabled = true; els.scheduleBody.innerHTML = ''; renderError(errors); return; } els.totalMonthly.textContent = formatted.debtFreeLabel; els.kpiMonths.textContent = formatted.monthsLabel; els.kpiInterest.textContent = formatted.interestLabel; els.kpiTotal.textContent = formatted.totalPaidLabel; els.kpiBudget.textContent = formatted.budgetLabel; els.kpiMinSum.textContent = formatted.minSumLabel; currentSchedule = formatted.schedule; els.downloadCsv.disabled = currentSchedule.length === 0; if (isScheduleVisible()) { renderTable(formatted.currency); } renderError(errors); } function renderError(errors) { if (!errors || errors.length === 0) { els.errorBox.style.display = 'none'; els.errorBox.textContent = ''; return; } els.errorBox.style.display = 'block'; els.errorBox.textContent = errors.join(' '); } function renderTable(cur) { els.scheduleBody.innerHTML = ''; if (!currentSchedule.length) return; const fragment = document.createDocumentFragment(); const now = new Date(); currentSchedule.slice(0, scheduleCap).forEach((row) => { const when = new Date(now.getFullYear(), now.getMonth() + row.month, 1); const tr = document.createElement('tr'); tr.innerHTML = ` ${row.month} ${fmtMonth(when)} ${fmtCurrency(row.payment, cur)} ${fmtCurrency(row.interest, cur)} ${fmtCurrency(row.principal, cur)} ${fmtCurrency(row.balance, cur)}`; fragment.appendChild(tr); }); els.scheduleBody.appendChild(fragment); } function isScheduleVisible() { return els.scheduleWrap.style.display === 'block'; } function update() { const inputs = parseInputs(); const validation = validate(inputs); if (!validation.ok) { render(null, validation.errors); return; } try { const outputs = compute(inputs); const formatted = format(outputs, inputs); render(formatted, []); } catch (err) { render(null, [err.message || 'Calculation failed for the current inputs.']); } } function clearDebts() { els.debtsBody.innerHTML = ''; } function addDebtRow(data) { createDebtRow(data); } function seedRows(items) { clearDebts(); items.forEach((item) => createDebtRow(item)); } function loadDefaults() { els.monthlyBudget.value = defaultBudget; els.strategy.value = defaultStrategy; seedRows(sampleDebts); els.scheduleWrap.style.display = 'none'; els.toggleSchedule.textContent = 'View Amortization Schedule'; els.downloadCsv.disabled = true; } el.addEventListener('change', debouncedUpdate); }); els.calcBtn.addEventListener('click', update); els.resetBtn.addEventListener('click', () => { loadDefaults(); resetRowErrors(); update(); }); els.addDebt.addEventListener('click', () => { addDebtRow({}); update(); }); els.loadSample.addEventListener('click', () => { loadDefaults(); update(); }); els.toggleSchedule.addEventListener('click', () => { const showing = isScheduleVisible(); if (showing) { els.scheduleWrap.style.display = 'none'; els.toggleSchedule.textContent = 'View Amortization Schedule'; } else { els.scheduleWrap.style.display = 'block'; els.toggleSchedule.textContent = 'Hide Amortization Schedule'; renderTable(currency); } }); els.downloadCsv.addEventListener('click', () => { if (currentSchedule.length === 0) return; let csv = '#,Date,Total Payment,Interest,Principal,Balance\n'; const now = new Date(); currentSchedule.forEach((row) => { const when = new Date(now.getFullYear(), now.getMonth() + row.month, 1); csv += `${row.month},${fmtMonth(when)},${row.payment},${row.interest},${row.principal},${row.balance}\n`; }); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'debt-avalanche-schedule.csv'; a.click(); URL.revokeObjectURL(url); }); els.budgetTipBtn.addEventListener('click', () => { const expanded = els.budgetTipBtn.getAttribute('aria-expanded') === 'true'; els.budgetTipBtn.setAttribute('aria-expanded', String(!expanded)); const help = els.budgetHelp; if (!help) return; if (expanded) { help.classList.add('hidden'); } else { help.classList.remove('hidden'); } }); loadDefaults(); update(); })();