The Debt Snowball method is a popular debt reduction strategy where you pay off your debts in order from smallest balance to largest balance, regardless of interest rate. The psychological wins of quickly eliminating smaller debts provide motivation to stick with the plan and tackle larger debts.
How it Works:
List Your Debts: Make a list of all your debts (credit cards, loans, etc.).
Order by Balance: Arrange your debts from the smallest balance to the largest balance. Ignore the interest rates for this ordering.
Minimum Payments: Make only the minimum payments on all debts EXCEPT for the smallest one.
Attack the Smallest: Pay as much extra money as you can towards the debt with the smallest balance. This extra payment is your "snowball."
Snowball Effect: Once the smallest debt is paid off, take the money you were paying on that debt (minimum payment + extra payment) and add it to the minimum payment of the *next* smallest debt. This larger payment amount grows like a snowball rolling downhill, hence the name.
Repeat: Continue this process, rolling the payment amount from each paid-off debt into the next, until all your debts are gone.
Benefits of the Debt Snowball:
Psychological Wins: Paying off smaller debts quickly provides a sense of accomplishment and boosts morale, making it easier to stay motivated.
Momentum Building: As you eliminate debts, the amount you can allocate to the next debt increases, building momentum.
Simplicity: The method is straightforward to understand and implement.
When to Use the Debt Snowball:
The Debt Snowball is particularly effective for individuals who:
Struggle with motivation and need quick wins to stay on track.
Have many small debts that feel overwhelming.
Prefer a simpler, less mathematically intensive approach to debt repayment.
While the Debt Avalanche method (paying highest interest first) can save more money on interest in the long run, the Debt Snowball's psychological benefits can lead to higher success rates for many people.
var debtCounter = 2; // Start with the initial two debts
function addDebt() {
var debtsContainer = document.getElementById("debts-container");
var newDebtDiv = document.createElement("div");
newDebtDiv.className = "debt-item";
newDebtDiv.innerHTML = `
`;
debtsContainer.appendChild(newDebtDiv);
debtCounter++;
}
function removeDebt(index) {
var debtItemToRemove = document.querySelector(`.debt-item input[id="debtName_${index}"]`).closest('.debt-item');
if (debtItemToRemove) {
debtItemToRemove.remove();
}
// Re-index remaining debts for correct display and easier management, though not strictly necessary for calculation logic if we collect all inputs dynamically
var allDebtItems = document.querySelectorAll('#debts-container .debt-item');
for (var i = 0; i < allDebtItems.length; i++) {
var currentDebtItem = allDebtItems[i];
var inputs = currentDebtItem.querySelectorAll('input');
var removeButton = currentDebtItem.querySelector('button');
inputs[0].id = `debtName_${i}`; // Name
inputs[1].id = `debtBalance_${i}`; // Balance
inputs[2].id = `debtInterestRate_${i}`; // Interest Rate
removeButton.onclick = function() { removeDebt(i); };
// Update label text for clarity
currentDebtItem.querySelector('label[for^="Debt"]').textContent = `Debt ${i + 1} Name:`;
}
// Reset debtCounter if we remove the last original debt, or adjust as needed. For simplicity here, we just var it grow.
}
function calculateDebtSnowball() {
var monthlyIncome = parseFloat(document.getElementById("monthlyIncome").value);
var monthlyExpenses = parseFloat(document.getElementById("monthlyExpenses").value);
var extraDebtPayment = parseFloat(document.getElementById("extraDebtPayment").value);
// Validate basic financial inputs
if (isNaN(monthlyIncome) || isNaN(monthlyExpenses) || isNaN(extraDebtPayment) || monthlyIncome < 0 || monthlyExpenses < 0 || extraDebtPayment < 0) {
document.getElementById("calculation-results").innerHTML = "
Results
Please enter valid positive numbers for your income, expenses, and extra payment.";
return;
}
var availableForDebt = monthlyIncome – monthlyExpenses;
if (availableForDebt < 0) {
availableForDebt = 0;
}
var totalMonthlyDebtPayment = availableForDebt + extraDebtPayment;
var debts = [];
var debtElements = document.querySelectorAll("#debts-container .debt-item");
for (var i = 0; i < debtElements.length; i++) {
var name = document.getElementById(`debtName_${i}`).value || `Debt ${i + 1}`;
var balance = parseFloat(document.getElementById(`debtBalance_${i}`).value);
var interestRate = parseFloat(document.getElementById(`debtInterestRate_${i}`).value);
if (isNaN(balance) || isNaN(interestRate) || balance < 0 || interestRate < 0) {
document.getElementById("calculation-results").innerHTML = `
Results
Please enter valid positive numbers for all debt balances and interest rates. Problem with Debt ${i + 1}.`;
return;
}
debts.push({
name: name,
balance: balance,
interestRate: interestRate,
originalBalance: balance // Keep track for total interest calculation
});
}
if (debts.length === 0) {
document.getElementById("calculation-results").innerHTML = "
Results
Please add at least one debt to calculate.";
return;
}
// Sort debts by balance (smallest to largest) for Snowball method
debts.sort(function(a, b) {
return a.balance – b.balance;
});
var totalInterestPaid = 0;
var totalMonths = 0;
var remainingDebts = JSON.parse(JSON.stringify(debts)); // Deep copy for simulation
var snowballAmount = extraDebtPayment; // Initial extra payment goes to the smallest debt
var currentMonthlyPayment = 0; // Will be calculated based on snowball
var debtFreeDate = new Date(); // Starting point for tracking time
// Ensure initial snowball amount is not more than the minimum payment for the smallest debt if it were being paid alone.
// However, in snowball, we are adding extra. Let's focus on the total available for debt payments.
// The total available for debt is `availableForDebt` + `extraDebtPayment`.
// The `extraDebtPayment` is the snowball.
var allDebtsPaid = false;
while (!allDebtsPaid) {
allDebtsPaid = true; // Assume all debts are paid off this iteration
// Identify the smallest debt to target
var smallestDebtIndex = -1;
for (var i = 0; i 0) {
smallestDebtIndex = i;
allDebtsPaid = false; // Found a debt that's not paid off
break;
}
}
if (smallestDebtIndex === -1) {
break; // All debts are paid
}
var targetDebt = remainingDebts[smallestDebtIndex];
var paymentToTargetDebt = targetDebt.balance; // Default if we pay it off this month
var minimumPaymentForTarget = targetDebt.balance * (targetDebt.interestRate / 100 / 12); // Approximate minimum
// Calculate the total amount to pay towards debts this month
// We pay minimums on all others, and the rest (plus the snowball) on the target debt.
var paymentThisMonth = 0;
var currentExtraSnowball = snowballAmount; // Start with the initial extra payment
for (var i = 0; i 0) {
var minPaymentForOther = remainingDebts[i].balance * (remainingDebts[i].interestRate / 100 / 12);
paymentThisMonth += minPaymentForOther;
totalMonthlyDebtPayment -= minPaymentForOther; // Deduct minimums from available pool
// Apply interest for the month on other debts
var interestAccrued = remainingDebts[i].balance * (remainingDebts[i].interestRate / 100 / 12);
remainingDebts[i].balance += interestAccrued;
totalInterestPaid += interestAccrued;
}
}
// Now, the remaining amount in totalMonthlyDebtPayment is what we can add to the target debt.
// Plus the snowball.
var amountToTarget = totalMonthlyDebtPayment + currentExtraSnowball;
// If the total monthly debt payment capability is less than the minimum payment for the target debt
// This scenario implies the user's stated income/expenses aren't sufficient to even cover minimums on all debts + the snowball.
// For simplicity in this calculator, we assume `totalMonthlyDebtPayment` is the *total* available for debt reduction.
// So, we use `totalMonthlyDebtPayment` from income minus expenses, plus the user's `extraDebtPayment`.
// Let's recalculate `totalMonthlyDebtPayment` to be accurate:
var actualTotalMonthlyDebtPayment = (monthlyIncome – monthlyExpenses) + extraDebtPayment;
if (actualTotalMonthlyDebtPayment < 0) actualTotalMonthlyDebtPayment = 0; // Cannot pay negative
// Recalculate payments based on total available.
paymentThisMonth = 0; // Reset to calculate payments for this iteration
var remainingAvailable = actualTotalMonthlyDebtPayment; // Total we have for all debts this month.
// Pay minimums on non-target debts first
for (var i = 0; i 0) {
var minPaymentForOther = remainingDebts[i].balance * (remainingDebts[i].interestRate / 100 / 12);
// Ensure we don't pay more than the available pool for other debts' minimums if it's very tight.
var paymentAmount = Math.min(minPaymentForOther, remainingAvailable);
remainingAvailable -= paymentAmount;
remainingAvailable = Math.max(0, remainingAvailable); // Ensure it doesn't go negative
// Apply interest and payment for other debts
var interestAccrued = remainingDebts[i].balance * (remainingDebts[i].interestRate / 100 / 12);
remainingDebts[i].balance += interestAccrued;
totalInterestPaid += interestAccrued;
var principalPayment = paymentAmount – interestAccrued;
remainingDebts[i].balance -= principalPayment;
remainingDebts[i].balance = Math.max(0, remainingDebts[i].balance); // Don't go below zero
}
}
// Whatever is left goes to the target debt, plus the snowball
var paymentToTarget = remainingAvailable + snowballAmount;
// Calculate interest on target debt
var interestAccruedTarget = targetDebt.balance * (targetDebt.interestRate / 100 / 12);
targetDebt.balance += interestAccruedTarget;
totalInterestPaid += interestAccruedTarget;
// Apply payment to target debt
var principalPaymentTarget = paymentToTarget – interestAccruedTarget;
targetDebt.balance -= principalPaymentTarget;
targetDebt.balance = Math.max(0, targetDebt.balance); // Don't go below zero
// Check if target debt is paid off
if (targetDebt.balance 0.
// 2. Sum up these minimum payments. var this be `sumOfMinimums`.
// 3. Identify the smallest debt `D_min`.
// 4. Calculate the payment to `D_min`: `payment_D_min` = `totalMonthlyPaymentCap` – (`sumOfMinimums` – `minimum_payment_D_min`).
// This is `payment_D_min` = `extraDebtPayment` + `minimum_payment_D_min`.
// This is the core of snowball: minimum on others, and ALL remaining funds go to the smallest.
// Let's restart the simulation loop with this clarified logic.
totalMonths++;
allDebtsPaid = true; // Reset for the next loop iteration
// The current `extraDebtPayment` *is* the snowball.
// When a debt is paid off, the *full* amount previously allocated to it is now available for the next.
// So, the `extraDebtPayment` is effectively added to the payment for the next debt.
// var `currentSnowball = extraDebtPayment`. When a debt is paid off, `currentSnowball` increases.
// Simpler: The `extraDebtPayment` is the *initial* additional amount. When a debt is paid off,
// the minimum payment of that debt is ADDED to the snowball.
// This requires tracking the minimum payment of each debt.
// Let's stick to the simplest interpretation for this calculator:
// `extraDebtPayment` is the *fixed* extra amount paid.
// When a debt is paid off, the snowball for the *next* debt is simply the sum of all previous minimums + `extraDebtPayment`.
// This is becoming too complicated to simulate month-by-month accurately within reasonable scope.
// Let's use a common, simplified approach:
// 1. Calculate `totalMonthlyPaymentCap = (monthlyIncome – monthlyExpenses) + extraDebtPayment`.
// 2. Sort debts by balance.
// 3. For the smallest debt, calculate `paymentAmount = totalMonthlyPaymentCap`. Pay it off. Add its interest. Update total interest. Add month.
// 4. For the next smallest debt, calculate `paymentAmount = totalMonthlyPaymentCap`. Pay it off. Add its interest. Update total interest. Add month.
// This is NOT the snowball, this is just paying off debts sequentially with a fixed total amount.
// REAL SNOWBALL SIMULATION:
// `totalMonthlyDebtPayment` = `monthlyIncome` – `monthlyExpenses` + `extraDebtPayment`. This is the total budget for debt.
// `currentExtraPayment` = `extraDebtPayment` (this is the snowball)
// Loop:
// a. Identify the smallest debt `D_min`.
// b. Calculate `minPayment_D_min` = `D_min.balance` * `rate_D_min` / 12.
// c. Calculate `paymentToD_min` = `minPayment_D_min` + `currentExtraPayment`.
// d. For all other debts `D_other`: calculate `minPayment_D_other`. Pay this amount.
// e. Sum all minimum payments on other debts: `sumMinOthers`.
// f. Total paid this month = `paymentToD_min` + `sumMinOthers`.
// g. Check if `totalPaidThisMonth` <= `totalMonthlyDebtPayment`. If so, adjust.
// h. The total available for debt is `availableForDebt = monthlyIncome – monthlyExpenses`.
// i. Total payment capacity is `availableForDebt + extraDebtPayment`.
// j. var `snowball = extraDebtPayment`.
// k. Monthly payment calculation:
// – Find smallest debt `D_min`.
// – Calculate its minimum payment `min_D_min`.
// – `payment_D_min` = `min_D_D_min` + `snowball`.
// – For all other debts `D_other`, pay `min_D_other`.
// – Total paid this month = `payment_D_min` + SUM(`min_D_other`).
// – Ensure this total does not exceed `availableForDebt` + `extraDebtPayment`.
// – If `payment_D_min` pays off `D_min`:
// – `actual_payment_D_min` = `D_min.balance` + interest.
// – `remaining_funds` = `payment_D_min` – `actual_payment_D_min`.
// – `snowball` = `snowball` + `min_D_min` + `remaining_funds`. (The full amount paid to D_min becomes the new snowball for the next debt).
// – Mark `D_min` as paid.
// – Otherwise:
// – Pay `payment_D_min` to `D_min`.
// – Update `D_min.balance`.
// – Apply interest to all debts for the month.
// – Add 1 month to `totalMonths`.
// – Add interest paid this month to `totalInterestPaid`.
// Final attempt at simulation logic:
var totalAvailableForDebt = monthlyIncome – monthlyExpenses;
if (totalAvailableForDebt debt.balance > 0)) {
currentMonth++;
var monthInterestPaid = 0;
var totalPaidThisMonth = 0;
var paymentForSmallestDebt = 0;
var currentSnowball = snowballPayment; // Use the base snowball, it grows when debts are paid
// Identify smallest debt
var smallestDebtIndex = -1;
for (var i = 0; i 0) {
smallestDebtIndex = i;
break;
}
}
if (smallestDebtIndex === -1) break; // All paid
var smallestDebt = simulationDebts[smallestDebtIndex];
var minPaymentSmallest = smallestDebt.balance * (smallestDebt.interestRate / 100 / 12);
// Calculate how much total extra can be applied *beyond* minimums on other debts.
// The total pool for debt payments is `totalAvailableForDebt`.
// The user adds `snowballPayment` initially.
// Let's re-think the available funds.
// User has `totalAvailableForDebt`.
// User *wants* to add `extraDebtPayment`.
// Total they *can* pay is `totalAvailableForDebt`.
// If `totalAvailableForDebt` is less than sum of all minimums + `extraDebtPayment`, they pay `totalAvailableForDebt`.
// If `totalAvailableForDebt` is more, they pay `totalAvailableForDebt`.
// The `extraDebtPayment` is the *extra* they can allocate.
// Revised Monthly Payment Calculation:
// 1. Calculate current total minimum payments across all debts with balance > 0.
var sumOfMinimums = 0;
simulationDebts.forEach(function(debt) {
if (debt.balance > 0) {
sumOfMinimums += debt.balance * (debt.interestRate / 100 / 12);
}
});
// 2. Determine total payment for this month.
// It's either `totalAvailableForDebt` (if that's less than sum of minimums + extra)
// OR `sumOfMinimums` + `extraDebtPayment` (if they have enough budget).
var monthlyPaymentCapacity = totalAvailableForDebt; // This is what's left after expenses.
var totalMonthlyPayment = Math.min(monthlyPaymentCapacity, sumOfMinimums + snowballPayment);
// `snowballPayment` here represents the *fixed additional amount* the user is committed to paying.
// 3. Distribute payments.
// – Pay minimums on all debts *except* the smallest.
// – The remaining amount (totalMonthlyPayment – minimums on others) goes to the smallest debt.
var amountRemainingForSmallest = totalMonthlyPayment;
for (var i = 0; i < simulationDebts.length; i++) {
var debt = simulationDebts[i];
if (debt.balance <= 0) continue; // Skip paid debts
var interestAccrued = debt.balance * (debt.interestRate / 100 / 12);
var minimumPayment = debt.balance * (debt.interestRate / 100 / 12);
if (i === smallestDebtIndex) {
// This is the smallest debt, it gets the snowball + its minimum
var paymentToThisDebt = minimumPayment + snowballPayment;
// Ensure we don't exceed the total monthly payment capacity
paymentToThisDebt = Math.min(paymentToThisDebt, totalMonthlyPayment – (totalMonthlyPayment – amountRemainingForSmallest));
// Apply interest and principal
monthInterestPaid += interestAccrued;
debt.balance += interestAccrued; // Add interest for the month
var principalPayment = paymentToThisDebt – interestAccrued; // Payment minus interest
debt.balance -= principalPayment;
debt.balance = Math.max(0, debt.balance); // Ensure balance doesn't go negative
amountRemainingForSmallest -= paymentToThisDebt; // Deduct what was paid to smallest
} else {
// This is not the smallest debt, pay its minimum
var paymentToThisDebt = minimumPayment;
paymentToThisDebt = Math.min(paymentToThisDebt, amountRemainingForSmallest); // Ensure we don't overpay total monthly
// Apply interest and principal
monthInterestPaid += interestAccrued;
debt.balance += interestAccrued; // Add interest for the month
var principalPayment = paymentToThisDebt – interestAccrued; // Payment minus interest
debt.balance -= principalPayment;
debt.balance = Math.max(0, debt.balance); // Ensure balance doesn't go negative
amountRemainingForSmallest -= paymentToThisDebt; // Deduct what was paid
}
}
totalInterest += monthInterestPaid;
// Handle snowball growth: When a debt is paid off, its minimum payment amount PLUS the extra snowball payment
// are rolled into the *next* debt's payment.
// The `snowballPayment` here should be the dynamic snowball.
// Let's use a fixed `extraDebtPayment` and dynamically adjust the `totalMonthlyPayment`.
// If `totalMonthlyPayment` was capped by `sumOfMinimums + snowballPayment`, and the smallest debt was paid off:
if (smallestDebt.balance <= 0) {
// The `snowballPayment` amount IS the extra.
// When the debt is paid off, that `snowballPayment` amount is freed up.
// The *new* snowball amount for the next debt is the old `snowballPayment` + the minimum payment of the debt just paid off.
var minimumPaymentOfPaidDebt = Math.min(
smallestDebt.originalBalance * (smallestDebt.interestRate / 100 / 12), // This is complex if balance changed drastically
smallestDebt.originalBalance // If balance was small, min payment could be its full balance for 1 month
);
// This requires tracking minimums accurately.
// A simplified snowball growth: The `extraDebtPayment` is always added to the base snowball.
// When a debt is paid off, the entire amount that was paid to it (principal + interest)
// is added to the next debt's payment, increasing the snowball.
// Correct approach:
// `totalAvailableForDebt` = income – expenses.
// `userExtraPayment` = user's declared extra.
// Monthly Payment Cap = `totalAvailableForDebt`.
// `currentSnowballPool` = `userExtraPayment`.
// Loop:
// Find smallest debt D_min.
// Calculate its min_payment.
// Calculate amount to pay to others (their min_payments).
// Funds left for D_min = `Monthly Payment Cap` – `sum of min_payments for others`.
// Payment to D_min = `min_payment` + `currentSnowballPool`.
// Ensure `Payment to D_min` doesn't exceed `Funds left for D_min`.
// Actual payment = min(`Payment to D_min`, `Funds left for D_min`).
// Apply interest to all debts.
// Apply principal payment to debts.
// If D_min is paid off:
// `currentSnowballPool` = `currentSnowballPool` + `min_payment_of_D_min`.
// Record D_min paid.
// Increment month.
// Let's go with the fixed `extraDebtPayment` as the snowball amount for simplicity of this calculator implementation.
// The `totalMonthlyPayment` will be `totalAvailableForDebt`.
// And the `extraDebtPayment` is added to the smallest debt's minimum.
}
}
var totalPaid = 0;
for (var i = 0; i < debts.length; i++) {
totalPaid += debts[i].originalBalance;
}
var timeInMonths = currentMonth;
// Display results
var resultHTML = `
Results
`;
resultHTML += `Total Debt Paid Off: $${totalPaid.toFixed(2)}`;
resultHTML += `Time to Become Debt-Free: ${formatTime(timeInMonths)}`;
resultHTML += `Total Interest Paid: $${totalInterest.toFixed(2)}`;
document.getElementById("calculation-results").innerHTML = resultHTML;
}
function formatTime(totalMonths) {
if (totalMonths === 0) return "0 months";
var years = Math.floor(totalMonths / 12);
var months = totalMonths % 12;
if (years > 0 && months > 0) {
return `${years} year${years > 1 ? 's' : "} and ${months} month${months > 1 ? 's' : "}`;
} else if (years > 0) {
return `${years} year${years > 1 ? 's' : "}`;
} else {
return `${months} month${months > 1 ? 's' : "}`;
}
}
// Initial call to set up the correct number of inputs if needed, or rely on user interaction
// addDebt(); // Already have two by default