Every loan app shows an EMI (equated monthly installment). The formula looks intimidating, but it's simple once you see where it comes from. Here's the math, an implementation, and a month-by-month breakdown.

The formula

EMI = P × r × (1 + r)^n / ((1 + r)^n − 1) 
  • P = principal (loan amount)
  • r = monthly interest rate (annual rate ÷ 12 ÷ 100)
  • n = number of monthly payments

The result is a fixed payment where the balance reaches exactly zero after n payments.

Implementation

function calculateEmi(principal, annualRatePercent, years) { const n = years * 12; const r = annualRatePercent / 12 / 100; // Edge case: 0% interest would divide by zero in the formula if (r === 0) return principal / n; const factor = Math.pow(1 + r, n); return (principal * r * factor) / (factor - 1); } console.log(calculateEmi(1_000_000, 9, 20).toFixed(2)); // ≈ 8997.9 per month 

On ₹10,00,000 at 9% for 20 years, you pay about ₹21.6 lakh in total, so more than half of that is interest.

Where the money actually goes: amortization

Each month, interest is charged on the remaining balance, and whatever is left of the EMI reduces the principal.

function amortize(principal, annualRatePercent, years) { const r = annualRatePercent / 12 / 100; const n = years * 12; const emi = calculateEmi(principal, annualRatePercent, years); let balance = principal; const rows = []; for (let month = 1; month <= n; month++) { const interest = balance * r; const principalPaid = emi - interest; balance -= principalPaid; rows.push({ month, interest, principalPaid, balance: Math.max(balance, 0) }); } return rows; } 

In the first month of the example above, about ₹7,500 goes to interest and only about ₹1,500 reduces the loan. This is why early prepayments save so much: they cut the balance that interest is calculated on.

Edge cases to handle

  • Zero rate: guard against division by zero, as above.
  • Floating-point drift: the final balance may be a tiny non-zero number. Clamp it, and round only for display.
  • Invalid input: negative or empty values should return an error, not NaN.
  • Real lenders differ slightly: rounding rules, processing fees, and daily vs. monthly compounding can change the figure by a few rupees.

Try it without writing code

If you just want the numbers, there's a free EMI calculator and a loan comparison calculator on Erapse, a site I run. They're handy for sanity-checking your own implementation against.

What would you add to a loan calculator? Prepayment simulation? Floating-rate changes?