Calculators & Utilities

Free Calorie Calculator Widget

BMR, TDEE, and cutting/bulking calorie targets with the Mifflin-St Jeor equation.

A free calorie calculator widget that estimates BMR and daily maintenance calories (TDEE) from age, sex, height, weight, and activity level using the Mifflin-St Jeor equation, plus cutting and bulking targets. Great for fitness coaches, gym sites, and health blogs. Install by pasting one script tag wherever you want the calculator to appear.

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/calorie-calculator.js" async></script>
<!-- Free Calorie Calculator widget by haynes lab — https://hayneslab.dev/tools/widgets/calorie-calculator -->
Inline (self-contained)html
<!-- Free Calorie Calculator widget by haynes lab — https://hayneslab.dev/tools/widgets/calorie-calculator -->
<script>
/*!
 * haynes lab — Calorie Calculator widget (free)
 * Docs & embed code: https://hayneslab.dev/tools/widgets/calorie-calculator
 */
(function () {
  'use strict';

  var PREFIX = 'hlw-calorie-calculator';
  var STYLE_ID = 'hlw-calorie-calculator-styles';
  var instanceCount = 0;

  var ACTIVITY_LEVELS = [
    { factor: 1.2, label: 'Sedentary (little or no exercise)' },
    { factor: 1.375, label: 'Light (exercise 1–3 days/week)' },
    { factor: 1.55, label: 'Moderate (exercise 3–5 days/week)' },
    { factor: 1.725, label: 'Active (exercise 6–7 days/week)' },
    { factor: 1.9, label: 'Very active (physical job or 2x training)' }
  ];

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

  function injectStyles() {
    if (document.getElementById(STYLE_ID)) return;
    var css =
      '.' + PREFIX + ', .' + PREFIX + ' * { box-sizing: border-box; }' +
      '.' + PREFIX + ' { position: relative; max-width: 460px; margin: 0 auto; padding: 24px 24px 40px;' +
      ' 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 16px; font-size: 18px; font-weight: 600; color: #111827; }' +
      '.' + PREFIX + '__toggle { display: flex; gap: 4px; padding: 4px; margin-bottom: 16px;' +
      ' background: #f3f4f6; border-radius: 8px; }' +
      '.' + PREFIX + '__unit { flex: 1; padding: 7px 0; border: 0; border-radius: 6px; background: transparent;' +
      ' color: #6b7280; font-size: 14px; font-weight: 500; cursor: pointer; }' +
      '.' + PREFIX + '__unit[aria-pressed="true"] { background: #ffffff; color: var(--hlw-accent);' +
      ' box-shadow: 0 1px 2px rgba(0,0,0,0.08); }' +
      '.' + PREFIX + '__unit:focus-visible { outline: 2px solid var(--hlw-accent); outline-offset: 2px; }' +
      '.' + PREFIX + '__fields { display: flex; gap: 12px; margin-bottom: 12px; }' +
      '.' + PREFIX + '__field { flex: 1; min-width: 0; }' +
      '.' + PREFIX + '__label { display: block; margin-bottom: 4px; font-size: 13px; font-weight: 500; color: #4b5563; }' +
      '.' + PREFIX + '__input { width: 100%; padding: 8px 10px; font-size: 15px; color: #111827;' +
      ' border: 1px solid #d1d5db; border-radius: 8px; background: #ffffff;' +
      ' font-family: inherit; }' +
      '.' + PREFIX + '__input:focus { outline: 2px solid var(--hlw-accent); outline-offset: 1px; border-color: var(--hlw-accent); }' +
      '.' + PREFIX + '__results { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-top: 16px; }' +
      '.' + PREFIX + '__stat { padding: 12px; border: 1px solid #e5e7eb; border-radius: 10px;' +
      ' background: #f9fafb; text-align: center; }' +
      '.' + PREFIX + '__stat--main { border-color: var(--hlw-accent); background: #ffffff; }' +
      '.' + PREFIX + '__stat-value { font-size: 24px; font-weight: 700; line-height: 1.1; color: #111827; }' +
      '.' + PREFIX + '__stat--main .' + PREFIX + '__stat-value { color: var(--hlw-accent); }' +
      '.' + PREFIX + '__stat-label { margin-top: 4px; font-size: 12px; font-weight: 500;' +
      ' text-transform: uppercase; letter-spacing: 0.04em; color: #6b7280; }' +
      '.' + PREFIX + '__hint { margin-top: 12px; font-size: 13px; color: #9ca3af; text-align: center; }' +
      '.' + 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: 420px) { .' + PREFIX + '__stat-value { font-size: 20px; } }';
    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 build(script) {
    var units = script.getAttribute('data-hl-units') === 'imperial' ? 'imperial' : 'metric';
    var accent = script.getAttribute('data-hl-accent') || '#f97316';

    injectStyles();

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

    root.appendChild(el('h3', PREFIX + '__title', 'Calorie Calculator'));

    instanceCount += 1;
    var uid = instanceCount;

    function labeledField(labelText, input) {
      var field = el('div', PREFIX + '__field');
      var label = el('label', PREFIX + '__label', labelText);
      var id = input.id;
      label.setAttribute('for', id);
      field.appendChild(label);
      field.appendChild(input);
      field._label = label;
      return field;
    }

    function numberInput(name) {
      var input = el('input', PREFIX + '__input');
      input.type = 'number';
      input.min = '0';
      input.step = 'any';
      input.setAttribute('inputmode', 'decimal');
      input.id = PREFIX + '-' + name + '-' + uid;
      return input;
    }

    // Unit toggle
    var toggle = el('div', PREFIX + '__toggle');
    toggle.setAttribute('role', 'group');
    toggle.setAttribute('aria-label', 'Unit system');
    var metricBtn = el('button', PREFIX + '__unit', 'Metric (cm / kg)');
    metricBtn.type = 'button';
    var imperialBtn = el('button', PREFIX + '__unit', 'Imperial (in / lb)');
    imperialBtn.type = 'button';
    toggle.appendChild(metricBtn);
    toggle.appendChild(imperialBtn);
    root.appendChild(toggle);

    // Age + sex row
    var row1 = el('div', PREFIX + '__fields');
    var ageInput = numberInput('age');
    var ageField = labeledField('Age', ageInput);

    var sexField = el('div', PREFIX + '__field');
    sexField.appendChild(el('span', PREFIX + '__label', 'Sex'));
    var sexToggle = el('div', PREFIX + '__toggle');
    sexToggle.style.marginBottom = '0';
    sexToggle.setAttribute('role', 'group');
    sexToggle.setAttribute('aria-label', 'Sex');
    var maleBtn = el('button', PREFIX + '__unit', 'Male');
    maleBtn.type = 'button';
    maleBtn.setAttribute('aria-pressed', 'true');
    var femaleBtn = el('button', PREFIX + '__unit', 'Female');
    femaleBtn.type = 'button';
    femaleBtn.setAttribute('aria-pressed', 'false');
    sexToggle.appendChild(maleBtn);
    sexToggle.appendChild(femaleBtn);
    sexField.appendChild(sexToggle);
    row1.appendChild(ageField);
    row1.appendChild(sexField);
    root.appendChild(row1);

    // Height + weight row
    var row2 = el('div', PREFIX + '__fields');
    var heightInput = numberInput('height');
    var heightField = labeledField('Height (cm)', heightInput);
    var weightInput = numberInput('weight');
    var weightField = labeledField('Weight (kg)', weightInput);
    row2.appendChild(heightField);
    row2.appendChild(weightField);
    root.appendChild(row2);

    // Activity level
    var activityField = el('div', PREFIX + '__field');
    var activityLabel = el('label', PREFIX + '__label', 'Activity level');
    var activitySelect = el('select', PREFIX + '__input');
    activitySelect.id = PREFIX + '-activity-' + uid;
    activityLabel.setAttribute('for', activitySelect.id);
    ACTIVITY_LEVELS.forEach(function (level, i) {
      var option = el('option', null, level.label);
      option.value = String(i);
      activitySelect.appendChild(option);
    });
    activitySelect.value = '0';
    activityField.appendChild(activityLabel);
    activityField.appendChild(activitySelect);
    root.appendChild(activityField);

    // Results
    var hint = el('div', PREFIX + '__hint', 'Enter your details to see your daily calorie targets');
    root.appendChild(hint);

    var results = el('div', PREFIX + '__results');
    results.setAttribute('aria-live', 'polite');
    function statBox(labelText, main) {
      var box = el('div', PREFIX + '__stat' + (main ? ' ' + PREFIX + '__stat--main' : ''));
      var value = el('div', PREFIX + '__stat-value', '—');
      box.appendChild(value);
      box.appendChild(el('div', PREFIX + '__stat-label', labelText));
      results.appendChild(box);
      return value;
    }
    var bmrValue = statBox('BMR', false);
    var tdeeValue = statBox('Maintenance', true);
    var cutValue = statBox('Cutting', false);
    var bulkValue = statBox('Bulking', false);
    results.style.display = 'none';
    root.appendChild(results);

    function currentUnits() {
      return imperialBtn.getAttribute('aria-pressed') === 'true' ? 'imperial' : 'metric';
    }

    function update() {
      var age = parseFloat(ageInput.value);
      var h = parseFloat(heightInput.value);
      var w = parseFloat(weightInput.value);
      if (!isFinite(age) || !isFinite(h) || !isFinite(w) || age <= 0 || h <= 0 || w <= 0) {
        results.style.display = 'none';
        hint.style.display = '';
        return;
      }
      // Mifflin-St Jeor expects cm and kg.
      var cm = currentUnits() === 'imperial' ? h * 2.54 : h;
      var kg = currentUnits() === 'imperial' ? w / 2.20462 : w;
      var male = maleBtn.getAttribute('aria-pressed') === 'true';
      var bmr = 10 * kg + 6.25 * cm - 5 * age + (male ? 5 : -161);
      var factor = ACTIVITY_LEVELS[parseInt(activitySelect.value, 10) || 0].factor;
      var tdee = bmr * factor;
      bmrValue.textContent = Math.round(bmr).toLocaleString();
      tdeeValue.textContent = Math.round(tdee).toLocaleString();
      cutValue.textContent = Math.round(Math.max(tdee - 500, 1200)).toLocaleString();
      bulkValue.textContent = Math.round(tdee + 300).toLocaleString();
      results.style.display = '';
      hint.style.display = 'none';
    }

    function setSex(male) {
      maleBtn.setAttribute('aria-pressed', male ? 'true' : 'false');
      femaleBtn.setAttribute('aria-pressed', male ? 'false' : 'true');
      update();
    }

    function setUnits(next) {
      if (next === currentUnits()) return;
      // Convert existing values so toggling doesn't lose the user's numbers.
      var h = parseFloat(heightInput.value);
      var w = parseFloat(weightInput.value);
      if (next === 'imperial') {
        if (isFinite(h) && h > 0) heightInput.value = (h / 2.54).toFixed(1);
        if (isFinite(w) && w > 0) weightInput.value = (w * 2.20462).toFixed(1);
        heightField._label.textContent = 'Height (in)';
        weightField._label.textContent = 'Weight (lb)';
      } else {
        if (isFinite(h) && h > 0) heightInput.value = (h * 2.54).toFixed(1);
        if (isFinite(w) && w > 0) weightInput.value = (w / 2.20462).toFixed(1);
        heightField._label.textContent = 'Height (cm)';
        weightField._label.textContent = 'Weight (kg)';
      }
      metricBtn.setAttribute('aria-pressed', next === 'metric' ? 'true' : 'false');
      imperialBtn.setAttribute('aria-pressed', next === 'imperial' ? 'true' : 'false');
      update();
    }

    metricBtn.addEventListener('click', function () { setUnits('metric'); });
    imperialBtn.addEventListener('click', function () { setUnits('imperial'); });
    maleBtn.addEventListener('click', function () { setSex(true); });
    femaleBtn.addEventListener('click', function () { setSex(false); });
    ageInput.addEventListener('input', update);
    heightInput.addEventListener('input', update);
    weightInput.addEventListener('input', update);
    activitySelect.addEventListener('change', update);

    // Apply initial unit labels/state.
    if (units === 'imperial') {
      heightField._label.textContent = 'Height (in)';
      weightField._label.textContent = 'Weight (lb)';
    }
    metricBtn.setAttribute('aria-pressed', units === 'metric' ? 'true' : 'false');
    imperialBtn.setAttribute('aria-pressed', units === 'imperial' ? 'true' : 'false');

    var credit = el('a', PREFIX + '__credit');
    credit.appendChild(logoSvg());
    credit.appendChild(document.createTextNode('Powered by haynes lab'));
    credit.href = 'https://hayneslab.dev/tools/widgets/calorie-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 calorie-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-units"metric"Initial unit system for height and weight: "metric" (cm/kg) or "imperial" (in/lb); visitors can toggle either way.
data-hl-accent"#f97316"Hex color for the maintenance result, active toggles, and focus states.

Frequently asked questions

How do I install the calorie calculator?

Paste the script tag snippet into your page HTML where you want the calculator to appear. It renders inline right after the tag and computes BMR, maintenance calories (TDEE), and cutting/bulking targets live as visitors type.

Can I customize the units and colors?

Yes. Use data-hl-units to start in "metric" (cm/kg) or "imperial" (in/lb) — visitors can switch and their numbers convert automatically — and data-hl-accent to match the highlight color to your brand.

Does it work on WordPress, Shopify, or Squarespace?

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

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