Finance & Investing

Free Loan Payment Calculator Widget

Interactive loan calculator with extra-payment interest savings.

A free, embeddable loan payment calculator for auto loans, personal loans, and other fixed-rate borrowing. Visitors enter an amount, APR, and term in months, then see the monthly payment, total interest, and payoff time — plus how much interest an extra monthly payment saves. Install it by pasting a single script tag anywhere on your site.

Live demo

Embed this widget

Paste either snippet into your page — a Custom HTML block on WordPress, Shopify, or Squarespace works too. Both snippets update live as you customize, so what you copy is exactly what you configured.

Hosted (recommended)html
<script src="https://hayneslab.dev/widgets/loan-payment-calculator.js" data-hl-extra="100" data-hl-title="Auto Loan Calculator" async></script>
<!-- Free Loan Payment Calculator widget by haynes lab — https://hayneslab.dev/tools/widgets/loan-payment-calculator -->
Inline (self-contained)html
<!-- Free Loan Payment Calculator widget by haynes lab — https://hayneslab.dev/tools/widgets/loan-payment-calculator -->
<script data-hl-extra="100" data-hl-title="Auto Loan Calculator">
/*!
 * haynes lab — Loan Payment Calculator widget (free)
 * Docs & embed code: https://hayneslab.dev/tools/widgets/loan-payment-calculator
 */
(function () {
  'use strict';

  var PREFIX = 'hlw-loan-payment-calculator';
  var STYLE_ID = 'hlw-loan-payment-calculator-styles';

  function findScript() {
    return document.currentScript ||
      document.querySelector('script[src*="widgets/loan-payment-calculator.js"]');
  }

  function injectStyles() {
    if (document.getElementById(STYLE_ID)) return;
    var css =
      '.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
      '.' + PREFIX + ' { position: relative; max-width: 560px; margin: 0 auto; padding: 28px 28px 36px;' +
      ' font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;' +
      ' font-size: 16px; line-height: 1.5; color: #1f2937; background: #ffffff;' +
      ' border: 1px solid #e5e7eb; border-radius: 12px; }' +
      '.' + PREFIX + '__title { margin: 0 0 20px; font-size: 19px; font-weight: 700; color: #111827; text-align: center; }' +
      '.' + PREFIX + '__fields { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 20px; }' +
      '.' + PREFIX + '__field { display: flex; flex-direction: column; gap: 5px; }' +
      '.' + PREFIX + '__label { font-size: 13px; font-weight: 600; color: #374151; }' +
      '.' + PREFIX + '__input { width: 100%; padding: 9px 12px; font: inherit; color: #111827;' +
      ' background: #ffffff; border: 1px solid #d1d5db; border-radius: 8px; }' +
      '.' + PREFIX + '__input:focus { outline: 2px solid var(--hlw-accent); outline-offset: 1px; border-color: var(--hlw-accent); }' +
      '.' + PREFIX + '__results { padding: 18px 20px; background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 10px; }' +
      '.' + PREFIX + '__payment { text-align: center; margin-bottom: 14px; padding-bottom: 14px; border-bottom: 1px solid #e5e7eb; }' +
      '.' + PREFIX + '__payment-value { display: block; font-size: 34px; font-weight: 700; color: var(--hlw-accent);' +
      ' font-variant-numeric: tabular-nums; line-height: 1.1; }' +
      '.' + PREFIX + '__payment-label { font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em; color: #6b7280; }' +
      '.' + PREFIX + '__row { display: flex; justify-content: space-between; gap: 12px; padding: 5px 0; font-size: 14px; }' +
      '.' + PREFIX + '__row-label { color: #6b7280; }' +
      '.' + PREFIX + '__row-value { font-weight: 600; color: #111827; font-variant-numeric: tabular-nums; text-align: right; }' +
      '.' + PREFIX + '__row--savings .' + PREFIX + '__row-value { color: #059669; }' +
      '.' + PREFIX + '__credit { position: absolute; right: 10px; bottom: 6px; font-size: 11px;' +
      ' display: inline-flex; align-items: center; gap: 4px; color: #9ca3af; text-decoration: none; }' +
      '.' + PREFIX + '__credit svg { display: block; }' +
      '.' + PREFIX + '__credit:hover { color: #6b7280; text-decoration: underline; }' +
      '@media (max-width: 480px) { .' + PREFIX + ' { padding: 20px 18px 34px; }' +
      ' .' + PREFIX + '__fields { grid-template-columns: 1fr; } }';
    var style = document.createElement('style');
    style.id = STYLE_ID;
    style.textContent = css;
    document.head.appendChild(style);
  }

  function el(tag, className, text) {
    var node = document.createElement(tag);
    if (className) node.className = className;
    if (text != null) node.textContent = text;
    return node;
  }

  function logoSvg() {
    var svgNS = 'http://www.w3.org/2000/svg';
    var svg = document.createElementNS(svgNS, 'svg');
    svg.setAttribute('viewBox', '0 0 32 32');
    svg.setAttribute('width', '13');
    svg.setAttribute('height', '13');
    svg.setAttribute('aria-hidden', 'true');
    svg.setAttribute('focusable', 'false');
    function circle(cx, cy, r, attrs) {
      var c = document.createElementNS(svgNS, 'circle');
      c.setAttribute('cx', cx);
      c.setAttribute('cy', cy);
      c.setAttribute('r', r);
      for (var key in attrs) c.setAttribute(key, attrs[key]);
      svg.appendChild(c);
    }
    circle(16, 16, 16, { fill: '#0f0a0a' });
    circle(16, 16, 12, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.3 });
    circle(16, 16, 9, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1, opacity: 0.5 });
    circle(16, 16, 6, { fill: 'none', stroke: '#fff5f5', 'stroke-width': 1.2, opacity: 0.9 });
    circle(16, 16, 3, { fill: '#06b6d4' });
    circle(24.5, 7.5, 2, { fill: '#f97316' });
    circle(7, 16, 1.8, { fill: '#f97316' });
    circle(20, 20, 1.5, { fill: '#f97316' });
    return svg;
  }

  function num(value, fallback) {
    var n = parseFloat(value);
    return isNaN(n) ? fallback : n;
  }

  function build(script) {
    var title = script.getAttribute('data-hl-title');
    if (title === null) title = 'Loan Payment Calculator';
    var currency = script.getAttribute('data-hl-currency') || '$';
    var accent = script.getAttribute('data-hl-accent') || '#f97316';
    var defaults = {
      amount: num(script.getAttribute('data-hl-amount'), 25000),
      rate: num(script.getAttribute('data-hl-rate'), 7),
      term: num(script.getAttribute('data-hl-term'), 60),
      extra: num(script.getAttribute('data-hl-extra'), 0)
    };

    injectStyles();

    var root = el('div', PREFIX);
    root.style.setProperty('--hlw-accent', accent);

    if (title) root.appendChild(el('h3', PREFIX + '__title', title));

    var fields = el('div', PREFIX + '__fields');
    var inputs = {};
    var idBase = PREFIX + '-' + Math.random().toString(36).slice(2, 8) + '-';
    [
      { key: 'amount', label: 'Loan amount', step: 'any', min: '0' },
      { key: 'rate', label: 'Interest rate (% APR)', step: 'any', min: '0' },
      { key: 'term', label: 'Term (months)', step: '1', min: '1' },
      { key: 'extra', label: 'Extra monthly payment', step: 'any', min: '0' }
    ].forEach(function (f) {
      var field = el('div', PREFIX + '__field');
      var label = el('label', PREFIX + '__label', f.label);
      var input = el('input', PREFIX + '__input');
      input.type = 'number';
      input.min = f.min;
      input.step = f.step;
      input.value = String(defaults[f.key]);
      input.id = idBase + f.key;
      label.setAttribute('for', input.id);
      field.appendChild(label);
      field.appendChild(input);
      fields.appendChild(field);
      inputs[f.key] = input;
      input.addEventListener('input', update);
    });
    root.appendChild(fields);

    var results = el('div', PREFIX + '__results');
    results.setAttribute('aria-live', 'polite');
    var paymentBox = el('div', PREFIX + '__payment');
    var paymentValue = el('span', PREFIX + '__payment-value');
    paymentBox.appendChild(paymentValue);
    paymentBox.appendChild(el('span', PREFIX + '__payment-label', 'Monthly payment'));
    results.appendChild(paymentBox);
    var rows = {};
    [
      ['interest', 'Total interest'],
      ['payoff', 'Payoff time'],
      ['extraPayoff', 'Payoff with extra payments'],
      ['saved', 'Interest saved by extra payments']
    ].forEach(function (r) {
      var row = el('div', PREFIX + '__row' + (r[0] === 'saved' ? ' ' + PREFIX + '__row--savings' : ''));
      row.appendChild(el('span', PREFIX + '__row-label', r[1]));
      var value = el('span', PREFIX + '__row-value');
      row.appendChild(value);
      results.appendChild(row);
      rows[r[0]] = { row: row, value: value };
    });
    root.appendChild(results);

    function money(n) {
      return currency + n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });
    }

    function formatMonths(months) {
      var years = Math.floor(months / 12);
      var rem = months % 12;
      if (!years) return rem + ' mo';
      if (!rem) return years + (years === 1 ? ' yr' : ' yrs');
      return years + (years === 1 ? ' yr ' : ' yrs ') + rem + ' mo';
    }

    // Months to pay off `balance` at monthly rate `r` paying `payment` per month.
    function monthsToPayoff(balance, r, payment) {
      if (payment <= balance * r) return Infinity; // never pays off
      if (r === 0) return Math.ceil(balance / payment);
      return Math.ceil(Math.log(payment / (payment - r * balance)) / Math.log(1 + r));
    }

    function update() {
      var amount = Math.max(0, num(inputs.amount.value, 0));
      var rate = Math.max(0, num(inputs.rate.value, 0));
      var term = Math.max(1, Math.round(num(inputs.term.value, 0)));
      var extra = Math.max(0, num(inputs.extra.value, 0));
      var r = rate / 100 / 12;

      if (amount <= 0) {
        paymentValue.textContent = '—';
        ['interest', 'payoff', 'extraPayoff', 'saved'].forEach(function (k) {
          rows[k].row.style.display = 'none';
        });
        return;
      }

      var payment = r === 0 ? amount / term : amount * r / (1 - Math.pow(1 + r, -term));
      var totalInterest = payment * term - amount;
      paymentValue.textContent = money(payment);
      rows.interest.value.textContent = money(totalInterest);
      rows.interest.row.style.display = '';
      rows.payoff.value.textContent = formatMonths(term);
      rows.payoff.row.style.display = '';

      if (extra > 0) {
        var months = monthsToPayoff(amount, r, payment + extra);
        if (isFinite(months)) {
          var interestWithExtra = (payment + extra) * months - amount;
          // Final month is a partial payment; approximate by capping at remaining balance.
          if (interestWithExtra < 0) interestWithExtra = 0;
          rows.extraPayoff.value.textContent = formatMonths(months) +
            (months < term ? ' (' + formatMonths(term - months) + ' sooner)' : '');
          rows.extraPayoff.row.style.display = '';
          rows.saved.value.textContent = money(Math.max(0, totalInterest - interestWithExtra));
          rows.saved.row.style.display = '';
        } else {
          rows.extraPayoff.row.style.display = 'none';
          rows.saved.row.style.display = 'none';
        }
      } else {
        rows.extraPayoff.row.style.display = 'none';
        rows.saved.row.style.display = 'none';
      }
    }

    update();

    var credit = el('a', PREFIX + '__credit');
    credit.appendChild(logoSvg());
    credit.appendChild(document.createTextNode('Powered by haynes lab'));
    credit.href = 'https://hayneslab.dev/tools/widgets/loan-payment-calculator';
    credit.target = '_blank';
    credit.rel = 'noopener';
    root.appendChild(credit);

    if (script.parentNode && script.parentNode.nodeName !== 'HEAD') {
      script.insertAdjacentElement('afterend', root);
    } else {
      document.body.appendChild(root);
    }
  }

  function init() {
    var script = findScript();
    if (!script) {
      console.warn('[haynes lab loan-payment-calculator] Could not locate its own script tag. Nothing rendered.');
      return;
    }
    build(script);
  }

  if (document.body) {
    init();
  } else {
    document.addEventListener('DOMContentLoaded', init);
  }
})();

</script>

Configuration

Every option is an attribute on the script tag — the same attributes the customizer above exposes. All attributes are optional unless marked required — defaults apply when omitted.

AttributeDefaultDescription
data-hl-amount"25000"Default principal amount pre-filled in the loan amount field.
data-hl-rate"7"Default annual interest rate (APR, in percent) pre-filled in the rate field.
data-hl-term"60"Default loan term in months.
data-hl-extra"0"Default extra amount paid each month on top of the scheduled payment; when above zero the widget shows the faster payoff and interest saved.
data-hl-currency"$"Currency symbol prepended to all money values in the results.
data-hl-accent"#f97316"Accent color used for the monthly payment figure and input focus states.
data-hl-title"Loan Payment Calculator"Heading shown at the top of the widget; set to an empty string to hide it.

Frequently asked questions

How do I install the loan payment calculator?

Paste the embed snippet wherever you want the calculator to appear on your page. The widget renders inline right after the script tag, pre-filled with your default loan amount, rate, and term, and updates live as visitors type.

Can I change the default values and colors?

Yes — set data-hl-amount, data-hl-rate, data-hl-term, and data-hl-extra to pre-fill the fields, data-hl-currency to change the currency symbol, data-hl-accent to match your brand color, and data-hl-title for a custom heading (or an empty string to hide it).

Does it work on WordPress, Shopify, or Squarespace?

Yes — it is a single vanilla JavaScript file with no dependencies, so it runs anywhere you can paste a script tag, including WordPress custom HTML blocks, Shopify theme sections, and Squarespace code blocks.

More free widgets

Need a custom widget?

We build custom widgets, integrations, and whole products. Tell us what your site needs and we'll scope it.

Get in Touch