DEV Community

Cover image for I Built a Free Debt Calculator with Pure HTML/CSS/JS
Leo Chang
Leo Chang

Posted on • Originally published at inisyght.online

I Built a Free Debt Calculator with Pure HTML/CSS/JS

I Built a Free Debt Calculator with Pure HTML/CSS/JS

A few weeks ago I realized something: every debt payoff calculator I found online either required sign-up, looked like it was designed in 2005, or only showed you one strategy at a time.

So I built my own. Zero dependencies. Zero frameworks. Zero backend.

Here's the full walkthrough of how it works under the hood.


The MVP Requirements

I wanted three things:

  1. Enter debts — name, balance, APR, minimum payment
  2. Compare both strategies — Snowball (smallest balance first) vs Avalanche (highest APR first) side by side
  3. See the math — total interest, payoff timeline, monthly schedule

And one non-negotiable: no data leaves your browser. No sign-up, no upload, no server.


The Architecture (It's Just HTML)

index.html
├── <head>        → styles + Chart.js CDN
├── <body>
│   ├── Input form (debt table + budget)
│   ├── Strategy toggle (Snowball / Avalanche)
│   ├── Results section
│   │   ├── Summary cards
│   │   ├── Chart.js payoff chart
│   │   ├── Milestone timeline
│   │   └── Payment schedule table
│   └── FAQ section
└── <script>      → all JS logic
Enter fullscreen mode Exit fullscreen mode

Single file. Deploy anywhere. Vercel in my case, but Netlify, GitHub Pages, or even a plain web server all work.


The JavaScript: Three Scenarios, One Engine

The core logic runs three scenarios on the same debt data:

function calculateAll(debts, monthlyBudget, strategy) {
  // Sort debts by strategy
  const sorted = [...debts].sort((a, b) => {
    if (strategy === 'snowball') return a.balance - b.balance;
    if (strategy === 'avalanche') return b.apr - a.apr;
    return 0; // minimum payment only
  });

  // Run the payoff simulation
  return simulatePayoff(sorted, monthlyBudget);
}
Enter fullscreen mode Exit fullscreen mode

The simulatePayoff function runs month by month:

  1. Calculate interest accrued on each debt
  2. Apply minimum payments
  3. Throw remaining budget at the target debt
  4. When a debt is paid off, roll its payment to the next target
  5. Record every month (balance, paid status, milestone)
function simulatePayoff(debts, monthlyBudget) {
  let month = 0;
  const schedule = [];

  while (debts.some(d => d.balance > 0) && month < 120) {
    month++;
    let remaining = monthlyBudget;

    // Accrue interest first
    debts.forEach(d => {
      d.balance += d.balance * (d.apr / 100 / 12);
    });

    // Find the target debt (first unpaid in sorted order)
    const target = debts.find(d => d.balance > 0);

    // Apply minimums
    debts.forEach(d => {
      const min = Math.min(d.minPayment, d.balance);
      d.balance -= min;
      remaining -= min;
    });

    // Throw remaining at target
    if (target && target.balance > 0 && remaining > 0) {
      const extra = Math.min(remaining, target.balance);
      target.balance -= extra;
    }

    schedule.push({ month, debts: debts.map(d => ({ ...d })) });
  }

  return schedule;
}
Enter fullscreen mode Exit fullscreen mode

The trick: run this three times — once for Minimum Only (no extra payments), once for Snowball, once for Avalanche. Then compare the results side by side.


The Chart: Chart.js with Dynamic Data

I used Chart.js (CDN-loaded) for the payoff timeline:

function renderChart(snowballData, avalancheData) {
  const ctx = document.getElementById('payoffChart').getContext('2d');

  new Chart(ctx, {
    type: 'line',
    data: {
      labels: snowballData.map(d => `Month ${d.month}`),
      datasets: [
        {
          label: 'Snowball',
          data: snowballData.map(d => d.totalRemaining),
          borderColor: '#059669',
          tension: 0.3
        },
        {
          label: 'Avalanche',
          data: avalancheData.map(d => d.totalRemaining),
          borderColor: '#2563EB',
          tension: 0.3
        }
      ]
    },
    options: {
      responsive: true,
      plugins: {
        legend: { position: 'top' }
      }
    }
  });
}
Enter fullscreen mode Exit fullscreen mode

Two lines. Same starting point. The visual difference in payoff speed is immediately obvious.


The Result: Snowball vs Avalanche in One View

After running all three scenarios, the user sees:

Metric Minimum Only Snowball Avalanche
Total Paid $28,124 $26,686 $26,496
Total Interest $3,424 $1,986 $1,796
Payoff Time 3 yrs 10 mo 1 yr 11 mo 1 yr 11 mo
Money Saved $1,438 $1,628

Both strategies cut payoff time by half compared to minimum payments.


Key Takeaways for Devs

  1. You don't need a framework. A single HTML file with vanilla JS can be a full product.
  2. Client-side calculations are underrated. No server costs, no privacy concerns, no sign-up friction.
  3. Side-by-side comparison sells itself. Showing the difference is more convincing than telling it.
  4. Deploy in 2 minutes. Vercel, Netlify, GitHub Pages — pick one, drag your file, done.

The entire project from idea to deployment took about 6 hours. Cost: $0.


Try It or See the Code

🔗 Live tool: https://inisyght.online/debt-calculator

The full source is a single HTML file. View source in your browser — all the logic is right there, commented and readable.


Built with ☕ and a stubborn refusal to sign up for yet another SaaS.

Top comments (0)