DEV Community

SEN LLC
SEN LLC

Posted on

A Loan Calculator With 元利均等 vs 元金均等 and Prepayment Simulation

A Loan Calculator With 元利均等 vs 元金均等 and Prepayment Simulation

Japanese mortgages let you choose between 元利均等 (equal total monthly payment) and 元金均等 (equal principal portion). The first has a constant monthly amount; the second has a decreasing amount as interest falls with the balance. The total interest differs significantly — equal principal pays less interest overall but has higher initial payments. A calculator that shows both side by side clarifies the trade-off.

Mortgage calculators are everywhere but most only support the equal-installment method. Equal-principal is less common outside Japan but is the standard choice for people who can afford larger early payments to save interest.

🔗 Live demo: https://sen.ltd/portfolio/loan-calc/
📦 GitHub: https://github.com/sen-ltd/loan-calc

Screenshot

Features:

  • Two payment methods: equal installment vs equal principal
  • Monthly payment + total interest + total paid
  • Full amortization schedule
  • Prepayment simulation (extra monthly → new term + interest saved)
  • Principal vs interest stacked bar chart
  • Japanese / English UI (Japanese mortgage focus)
  • Zero dependencies, 39 tests

The standard amortization formula

For equal monthly payments on a fixed-rate loan:

export function monthlyPayment(principal, apr, months) {
  const r = apr / 12 / 100;  // monthly rate
  if (r === 0) return principal / months;  // zero-interest edge case
  return principal * r / (1 - Math.pow(1 + r, -months));
}
Enter fullscreen mode Exit fullscreen mode

This is the classic annuity formula. The payment covers interest on the current balance plus a portion of principal. Early payments are mostly interest; late payments are mostly principal.

A $100,000 loan at 5% over 30 years:

  • Monthly payment: $536.82
  • Total paid: $193,256
  • Total interest: $93,256

The borrower pays almost as much interest as principal over the full term. At higher rates or longer terms, interest can exceed principal.

元金均等: equal principal

In equal-principal, the principal portion is fixed and the interest portion decreases each month:

function equalPrincipalSchedule(principal, apr, months) {
  const r = apr / 12 / 100;
  const principalPortion = principal / months;
  const schedule = [];
  let balance = principal;
  for (let m = 1; m <= months; m++) {
    const interestPortion = balance * r;
    const payment = principalPortion + interestPortion;
    balance -= principalPortion;
    schedule.push({ month: m, payment, principalPaid: principalPortion, interestPaid: interestPortion, balance: Math.max(0, balance) });
  }
  return schedule;
}
Enter fullscreen mode Exit fullscreen mode

First payment is largest (full interest on full principal + fixed principal). Each subsequent payment is smaller because interest decreases with the shrinking balance.

For the same $100,000 / 5% / 30 year loan:

  • First payment: $694.44 ($416.67 interest + $277.78 principal)
  • Last payment: $278.94 ($1.16 interest + $277.78 principal)
  • Total interest: $75,208 (vs $93,256 for equal installment — $18,048 savings)

The trade-off: you pay more in the early years. If your income is expected to grow, equal-installment might fit your budget better. If you can afford the early burden, equal-principal saves money.

Prepayment simulation

Paying extra each month shortens the loan. How much extra saves how much interest?

export function earlyPayoff(schedule, extraMonthly) {
  const r = schedule[0].interestPaid / getOriginalPrincipal(schedule);
  let balance = getOriginalPrincipal(schedule);
  const monthlyBase = schedule[0].payment;
  const monthlyWithExtra = monthlyBase + extraMonthly;

  let newMonths = 0;
  let newTotalInterest = 0;
  while (balance > 0) {
    const interest = balance * r;
    const principal = Math.min(monthlyWithExtra - interest, balance);
    newMonths++;
    newTotalInterest += interest;
    balance -= principal;
  }

  const originalInterest = schedule.reduce((sum, row) => sum + row.interestPaid, 0);
  return {
    newTerm: newMonths,
    interestSaved: originalInterest - newTotalInterest,
  };
}
Enter fullscreen mode Exit fullscreen mode

The loop advances month by month until the balance is paid off. Extra payments compound: each $100 extra today reduces the balance, which reduces the interest charged next month, which lets more of the next payment go to principal, which reduces the balance further.

For a $100k / 5% / 30-year loan with $100/month extra:

  • Original term: 360 months (30 years)
  • New term: ~264 months (22 years)
  • Interest saved: ~$31,000

Zero-interest edge case

Fun fact: monthlyPayment has a division by zero when apr=0:

// principal * r / (1 - (1 + r)^-n)
// r = 0 → 0 / (1 - 1) = 0 / 0
Enter fullscreen mode Exit fullscreen mode

Handle it explicitly: if interest is 0, the monthly payment is just principal / months. The math still works but JavaScript gives NaN for the formula, so an explicit branch keeps the output clean.

Series

This is entry #98 in my 100+ public portfolio series.

Top comments (0)