<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Calculator on brege.org</title>
    <link>https://brege.org/tags/calculator/</link>
    <description>Recent content in Calculator on brege.org</description>
    <generator>Hugo</generator>
    <language>en</language>
    <copyright>Copyright (c) 2016-2026 Wyatt Brege</copyright>
    <lastBuildDate>Sun, 29 Mar 2026 17:53:44 -0400</lastBuildDate>
    <atom:link href="https://brege.org/tags/calculator/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>How much of one single food do you need to eat in a day to satisfy protein demands?</title>
      <link>https://brege.org/post/protein-calculator/</link>
      <pubDate>Thu, 17 Apr 2025 16:54:44 -0400</pubDate>
      <guid>https://brege.org/post/protein-calculator/</guid>
      <description>A live protein calculator prototype for common high protein food sources.</description>
      <content:encoded><![CDATA[<p>I have now celebrated one year of resistance training. One of the benefits of consistent, hard exercise is that it naturally steers you toward a healthier, more informed diet—and makes it easier to keep the undesirable effects of indulgence at bay, if you try.</p>
<p>Most anyone in the gym has heard the adage to eat &ldquo;1g of protein per pound of (goal) bodyweight per day.&rdquo; Research has shown this figure isn&rsquo;t accurate; it&rsquo;s more like 60–70% of that (and that&rsquo;s at <strong>peak</strong> resistance training intensity) for ideal muscle mass gains <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup> <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> <sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>. For example, if you weigh 175 lbs, this guideline states you should eat somewhere between</p>
<p>$$
\begin{aligned}
\frac{\textrm{protein}}{\textrm{day}}
&amp;= \left[0.35,0.75\right]
\cdot \left( \text{g} / \text{lbs} / \text{day} \right)
\cdot 175 \textrm{lbs}  \\
&amp;= ~ \left[60, 140\right] \textrm{g/day}
\end{aligned}
$$</p>
<p>where we have used the shorthand</p>
<p>$$
\text{g} / \text{lbs} / \text{day}
\coloneqq \frac{\text{protein (g)}}{\text{bodyweight (lbs)} \cdot \text{day}}
$$</p>
<p>But for nearly everyone landing on these pages, the goal is fat loss. Higher protein (and fiber) intake will help you feel fuller for longer and can help people new to fitness and diet control their cravings.</p>
<p>Following the guidelines is also made more tedious by the near-constant arithmetic at the grocery store. While it&rsquo;s good to check the labels to learn what&rsquo;s actually in your food, keeping a running total and hitting your target each day becomes a challenge. You wind up relying on apps, breaking meals into chunks: &ldquo;10 grams here, 25g scoop there&hellip; how much protein is in a palm-sized chunk of chicken breast again??&rdquo; And then there’s the protein in bread, grains, bars. It’s nearly impossible to balance all that against metabolic calories if you’re even <em>trying</em> a little.</p>
<h2 id="compositional-thinking-strategy">Compositional Thinking Strategy</h2>
<p><em>Here&rsquo;s my take:</em> <strong>Ratio counting.</strong> As a chef, there&rsquo;s a reason the imperial, fractional system works: ratio. It&rsquo;s easier for me to think about doubling, tripling, or halving depending on what I&rsquo;m buying and who I&rsquo;m cooking for. Our days are divided into twelves and therefore 2&rsquo;s, 3&rsquo;s, and 4&rsquo;s. Naturally, our meals are spaced that way too. The value of our <code>base10</code> system cannot be overstated, but I have a difficult time what eating or preparing 10% less of a meal or recipe means vs doing a quarter or half.</p>
<p>For the 175 lbs individual, the ranges of protein consumption needed are pinned by three characteristic quantities:</p>
<ul>
<li>$\textbf{MAX} = $ 100% efficiency in protein synthesis $\approx 1.6 \text{g} / \text{kg} / \text{day}$</li>
<li><strong>0.75</strong> $\coloneqq 0.75 \times \text{MAX} \approx 1.2 \text{g} / \text{kg} / \text{day}$</li>
<li>$\textbf{RDA} \coloneqq 0.5 \times \text{MAX} \approx 0.8 \text{g} / \text{kg} / \text{day}$</li>
</ul>
<p>where $\text{RDA}$ is the <strong>Recommended Daily Allowance</strong>, and $\text{MAX}$ is the <strong>Maximum Efficient Intake</strong>. The &ldquo;$0.75 \times \text{MAX} \approx 1.2 \text{lbs}$&rdquo; is roughly the target for most people who weigh 175 lbs (80 kg), on average. <sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<details> <summary><b>Tap here to see the ranges for this weight...</b></summary> 
<br/>
For reference, the ranges of protein consumption needed for the the 175 lbs individual are:
<p>$$
\text{&lt;65yo + RE:} \quad \left[ 0.8,\ 1.6 \right] \ \text{g} / \text{kg} / \text{day}
$$</p>
<p>$$
\text{⩾65yo + RE:} \quad \left[ 1.1,\ 1.4 \right] \ \text{g} / \text{kg} / \text{day}
$$</p>
<p>where $\text{RE}$ means &ldquo;Resistance Exercise&rdquo;, or, in pounds:</p>
<p>$$
\text{&lt;65yo + RE:} \quad \left[ 0.35,\ 0.75 \right] \ \text{g} / \text{lbs} / \text{day}
$$</p>
<p>$$
\text{⩾65yo + RE:} \quad \left[ 0.55,\ 0.65 \right] \ \text{g} / \text{lbs} / \text{day}.
$$</p>
</details>
<h2 id="implementation-the-protein-efficiency-calculator">Implementation: The Protein Efficiency Calculator</h2>
<p>The table below answers a simple question: how much of one single food do you need to eat to hit your daily protein target? Input your weight, toggle metric or imperial, and adjust units per row—grams, ounces, scoops, each. It’ll show you the amount needed to hit benchmarks in the range of $\text{RDA}$ to the $(\text{MAX})$ intake.  Based on resistance training status and the latest research, a sliding scale in this range will allow you to estimate your required protein demands.</p>
<p><strong>Quantity:</strong> the number of units in the <strong>Unit/Size</strong> column you need to eat to acheive the chosen protein target, per day, for your input weight.</p>
<p>Adjusting the <strong>Protein target</strong> slider can be thought of as a direct conversion of protein to training intensity, from sedentary/$\text{RDA}$ all the way to the $\text{MAX}$ intensity threshold.</p>

<link rel="stylesheet" href="/css/calculator.css">

<div id="calculator">

  <div class="calculator">
    <label for="weight-input" class="form-label"><b>Body Weight:</b></label>
    <div class="input-group">
      <input type="number" id="weight-input" value="175" class="form-control weight-input" />
      <div class="unit-toggle">
        <label><input type="radio" name="unit" value="imperial" checked> lbs</label>
        <label><input type="radio" name="unit" value="metric"> kg</label>
      </div>
    </div>

    <label for="target-range" class="form-label mt-4"><b>Protein target:</b>
      <span id="target-label">0.8 g/lb (RDA) [140 g]</span>
    </label>
    <br>
    <input type="range" id="target-range" class="w-full mb-4" />
  </div>

  <table id="resultsTable">
    <thead>
      <tr>
        <th>Source</th>
        <th>Protein/Unit<br>(g/Unit)</th>
        <th id="target-column-label">Quantity <br> (Unit)</th>
        <th>Unit/Size</th>
        <th>Calories/day<br>(kcal)</th>
        <th>Cite</th>
      </tr>
    </thead>
    <tbody id="protein-tbody">
      
      <tr data-protein="84" data-calories="840">
        <td>Eggs, whole</td>
        <td class="protein-per-unit">84</td>
        <td class="col-target"></td>
        <td>
          <select class="unit-select">
            
            
              <option value="7" data-calories="70" >
                each
              </option>
            
              <option value="0.14" data-calories="1.4" >
                grams
              </option>
            
              <option value="84" data-calories="840" selected>
                dozen
              </option>
            
          </select>
        </td>
        <td class="calories-per-unit">840</td>
        <td class="citation">
          <a href="https://fdc.nal.usda.gov/food-details/1663084/nutrients" target="_blank" rel="noopener">[1]</a>
        </td>
      </tr>
      
      <tr data-protein="105" data-calories="735">
        <td>Chicken Breast, Raw</td>
        <td class="protein-per-unit">105</td>
        <td class="col-target"></td>
        <td>
          <select class="unit-select">
            
            
              <option value="105" data-calories="735" selected>
                pounds
              </option>
            
              <option value="6.6" data-calories="46" >
                ounces
              </option>
            
              <option value="0.25" data-calories="1.7" >
                grams
              </option>
            
              <option value="230" data-calories="1700" >
                kilograms
              </option>
            
          </select>
        </td>
        <td class="calories-per-unit">735</td>
        <td class="citation">
          <a href="https://fdc.nal.usda.gov/food-details/577951/nutrients" target="_blank" rel="noopener">[2]</a>
        </td>
      </tr>
      
      <tr data-protein="105" data-calories="975">
        <td>Beef, Ground (85/15)</td>
        <td class="protein-per-unit">105</td>
        <td class="col-target"></td>
        <td>
          <select class="unit-select">
            
            
              <option value="6.6" data-calories="61" >
                ounces
              </option>
            
              <option value="105" data-calories="975" selected>
                pounds
              </option>
            
              <option value="0.23" data-calories="2.2" >
                grams
              </option>
            
              <option value="232" data-calories="2150" >
                kilograms
              </option>
            
          </select>
        </td>
        <td class="calories-per-unit">975</td>
        <td class="citation">
          <a href="https://fdc.nal.usda.gov/food-details/2312134/nutrients" target="_blank" rel="noopener">[3]</a>
        </td>
      </tr>
      
      <tr data-protein="88" data-calories="600">
        <td>Greek Yogurt (2%)</td>
        <td class="protein-per-unit">88</td>
        <td class="col-target"></td>
        <td>
          <select class="unit-select">
            
            
              <option value="22" data-calories="150" >
                cups
              </option>
            
              <option value="2.7" data-calories="18" >
                ounces
              </option>
            
              <option value="0.1" data-calories="0.68" >
                grams
              </option>
            
              <option value="88" data-calories="600" selected>
                32oz tub
              </option>
            
          </select>
        </td>
        <td class="calories-per-unit">600</td>
        <td class="citation">
          <a href="https://fdc.nal.usda.gov/food-details/360742/nutrients" target="_blank" rel="noopener">[4]</a>
        </td>
      </tr>
      
      <tr data-protein="72" data-calories="600">
        <td>Cottage Cheese (2%)</td>
        <td class="protein-per-unit">72</td>
        <td class="col-target"></td>
        <td>
          <select class="unit-select">
            
            
              <option value="3.11" data-calories="23" >
                ounces
              </option>
            
              <option value="0.11" data-calories="0.81" >
                grams
              </option>
            
              <option value="24" data-calories="200" >
                cups
              </option>
            
              <option value="72" data-calories="600" selected>
                24oz tub
              </option>
            
          </select>
        </td>
        <td class="calories-per-unit">600</td>
        <td class="citation">
          <a href="https://fdc.nal.usda.gov/food-details/2658102/nutrients" target="_blank" rel="noopener">[5]</a>
        </td>
      </tr>
      
      <tr data-protein="23" data-calories="850">
        <td>Tree Nuts (Avg)</td>
        <td class="protein-per-unit">23</td>
        <td class="col-target"></td>
        <td>
          <select class="unit-select">
            
            
              <option value="4.6" data-calories="170" >
                ounces
              </option>
            
              <option value="0.165" data-calories="6.1" >
                grams
              </option>
            
              <option value="23" data-calories="850" selected>
                cups
              </option>
            
          </select>
        </td>
        <td class="calories-per-unit">850</td>
        <td class="citation">
          <a href="https://fdc.nal.usda.gov/food-details/2653755/nutrients" target="_blank" rel="noopener">[6]</a>
        </td>
      </tr>
      
      <tr data-protein="6" data-calories="100">
        <td>Peas, Frozen</td>
        <td class="protein-per-unit">6</td>
        <td class="col-target"></td>
        <td>
          <select class="unit-select">
            
            
              <option value="1.3" data-calories="22" >
                ounces
              </option>
            
              <option value="0.045" data-calories="0.78" >
                grams
              </option>
            
              <option value="6" data-calories="100" selected>
                cups
              </option>
            
          </select>
        </td>
        <td class="calories-per-unit">100</td>
        <td class="citation">
          <a href="https://fdc.nal.usda.gov/food-details/2427531/nutrients" target="_blank" rel="noopener">[7]</a>
        </td>
      </tr>
      
      <tr data-protein="30" data-calories="480">
        <td>Chili (Est.)</td>
        <td class="protein-per-unit">30</td>
        <td class="col-target"></td>
        <td>
          <select class="unit-select">
            
            
              <option value="15" data-calories="240" >
                cups
              </option>
            
              <option value="1.9" data-calories="28" >
                ounces
              </option>
            
              <option value="0.07" data-calories="1" >
                grams
              </option>
            
              <option value="70" data-calories="1000" >
                kilograms
              </option>
            
              <option value="30" data-calories="480" selected>
                bowls
              </option>
            
          </select>
        </td>
        <td class="calories-per-unit">480</td>
        <td class="citation">
          <a href="https://fdc.nal.usda.gov/food-details/2671587/nutrients" target="_blank" rel="noopener">[8]</a>
        </td>
      </tr>
      
      <tr data-protein="100" data-calories="1100">
        <td>Salmon, raw</td>
        <td class="protein-per-unit">100</td>
        <td class="col-target"></td>
        <td>
          <select class="unit-select">
            
            
              <option value="6.23" data-calories="65" >
                ounces
              </option>
            
              <option value="0.22" data-calories="2.3" >
                grams
              </option>
            
              <option value="100" data-calories="1100" selected>
                pounds
              </option>
            
              <option value="220" data-calories="2300" >
                kilograms
              </option>
            
          </select>
        </td>
        <td class="calories-per-unit">1100</td>
        <td class="citation">
          <a href="https://fdc.nal.usda.gov/food-details/2684441/nutrients" target="_blank" rel="noopener">[9]</a>
        </td>
      </tr>
      
      <tr data-protein="104" data-calories="760">
        <td>Pork Chop, Boneless</td>
        <td class="protein-per-unit">104</td>
        <td class="col-target"></td>
        <td>
          <select class="unit-select">
            
            
              <option value="6.2" data-calories="48" >
                ounces
              </option>
            
              <option value="0.22" data-calories="1.7" >
                grams
              </option>
            
              <option value="104" data-calories="760" selected>
                pounds
              </option>
            
              <option value="220" data-calories="1680" >
                kilograms
              </option>
            
          </select>
        </td>
        <td class="calories-per-unit">760</td>
        <td class="citation">
          <a href="https://fdc.nal.usda.gov/food-details/2646168/nutrients" target="_blank" rel="noopener">[10]</a>
        </td>
      </tr>
      
      <tr data-protein="25" data-calories="130">
        <td>Whey Protein Powder</td>
        <td class="protein-per-unit">25</td>
        <td class="col-target"></td>
        <td>
          <select class="unit-select">
            
            
              <option value="0.71" data-calories="3.5" >
                grams
              </option>
            
              <option value="25" data-calories="130" selected>
                scoops
              </option>
            
          </select>
        </td>
        <td class="calories-per-unit">130</td>
        <td class="citation">
          <a href="https://www.costco.com/kirkland-signature-whey-protein%2c-creamy-chocolate%2c-5.4lbs.product.4000287218.html" target="_blank" rel="noopener">[11]</a>
        </td>
      </tr>
      
      <tr data-protein="21" data-calories="150">
        <td>Vegan Protein Powder</td>
        <td class="protein-per-unit">21</td>
        <td class="col-target"></td>
        <td>
          <select class="unit-select">
            
            
              <option value="21" data-calories="150" selected>
                scoops
              </option>
            
              <option value="0.8" data-calories="5.7" >
                grams
              </option>
            
          </select>
        </td>
        <td class="calories-per-unit">150</td>
        <td class="citation">
          <a href="https://orgain.com/products/organic-protein-plant-based-protein-powder-vanilla-bean" target="_blank" rel="noopener">[12]</a>
        </td>
      </tr>
      
    </tbody>
  </table>
</div>

<script>
  let currentUnit = 'imperial';
  let userWeightKg = 175 / 2.205;
  let currentTarget = 1.2;

  function updateProteinNeeds(weightKg) {
    userWeightKg = weightKg;
    updateTableValues();
  }

  function updateTableValues() {
    document.querySelectorAll("tbody#protein-tbody tr").forEach(row => updateSingleRow(row));
  }

  function updateSingleRow(row) {
    const proteinPerUnit = parseFloat(row.dataset.protein);
    const caloriesPerUnit = parseFloat(row.dataset.calories);
    if (isNaN(proteinPerUnit) || proteinPerUnit === 0) return;

    const targetGrams = userWeightKg * currentTarget;
    const quantity = targetGrams / proteinPerUnit;
    const calories = quantity * caloriesPerUnit;

    row.querySelector(".col-target").textContent = quantity.toFixed(2);
    row.querySelector(".calories-per-unit").textContent = isNaN(calories) ? '-' : Math.round(calories);
  }

  function updateTargetRangeAttributes() {
    const range = document.querySelector("#target-range");
    if (currentUnit === 'metric') {
      range.min = 0.8;
      range.max = 1.7;
      range.step = 0.05;
      range.value = currentTarget;
    } else {
      range.min = (0.8 / 2.205).toFixed(3);
      range.max = (1.7 / 2.205).toFixed(3);
      range.step = (0.05 / 2.205).toFixed(3);
      range.value = (currentTarget / 2.205).toFixed(3);
    }
  }

  function updateTargetLabel() {
    const unitSuffix = currentUnit === "metric" ? "g/kg" : "g/lb";
    const displayTarget = currentUnit === "metric" ? currentTarget : currentTarget / 2.205;
    const label = document.querySelector("#target-label");

    const targetGrams = userWeightKg * currentTarget;
    const gramsRounded = Math.round(targetGrams);

    let baseText = `${displayTarget.toFixed(2)} ${unitSuffix}`;
    if (currentTarget <= 0.85) {
      baseText += " (RDA)";
    } else if (currentTarget >= 1.6) {
      baseText += " (MAX)";
    }

    label.textContent = `${baseText} [${gramsRounded} g]`;
  }

  document.querySelector("#weight-input").addEventListener("input", function () {
    const val = parseFloat(this.value);
    if (!isNaN(val)) {
      const weightInKg = currentUnit === "metric" ? val : val / 2.205;
      updateProteinNeeds(weightInKg);
    }
  });

  document.querySelectorAll("input[name='unit']").forEach(radio => {
    radio.addEventListener("change", function () {
      currentUnit = this.value;
      const val = parseFloat(document.querySelector("#weight-input").value);
      const weightInKg = currentUnit === "metric" ? val : val / 2.205;
      updateProteinNeeds(weightInKg);
      updateTargetRangeAttributes();
      updateTargetLabel();
    });
  });

  document.querySelector("#target-range").addEventListener("input", function () {
    currentTarget = currentUnit === 'metric' ? parseFloat(this.value) : parseFloat(this.value) * 2.205;
    updateTargetLabel();
    updateTableValues();
  });

  document.querySelectorAll("tbody#protein-tbody tr").forEach(row => {
    const unitSelect = row.querySelector(".unit-select");
    unitSelect.addEventListener("change", function () {
      const protein = parseFloat(this.value);
      const calories = parseFloat(this.selectedOptions[0].dataset.calories);
      row.dataset.protein = protein;
      row.dataset.calories = calories;
      row.querySelector(".protein-per-unit").textContent = protein.toFixed(2);
      updateSingleRow(row);
    });
  });

  updateTargetRangeAttributes();
  updateProteinNeeds(userWeightKg);
  updateTargetLabel();
</script>

<p>I&rsquo;ve also included the <strong>daily Calories</strong> you&rsquo;d likewise take on per food item.  You&rsquo;ll note that high carbohydrate (peas, chili) and especially high fat content (mixed nuts) greatly diminish the remaining calorie budget in the day.  Conversely, protein powders, including the vegan kind, substantially lower the overall caloric footprint.</p>
<h2 id="the-protein-efficiency-matrix">The Protein Efficiency Matrix</h2>
<p>While the above tool scales protein needs to an individual&rsquo;s body composition, it’s also worth viewing high-protein foods in a way that’s independent of bodyweight. The protein-to-calorie efficiency of a food source is a fixed property — it doesn’t change based on who’s eating it.</p>
<p>With that in mind, the <strong>Protein Efficiency Matrix</strong> below expresses the relative caloric density of each food source. It compares the efficiency of each against the others in the table.</p>
<style>
  .nowrap {
    white-space: nowrap;
  }
  .font-bold {
    font-weight: bold;
  }
  #protein-matrix .td-right {
    width: 3rem;
    min-width: 3rem;
    padding-left: 0.4rem;
    padding-right: 0.4rem;
  }
  #protein-matrix .matrix-col-header {
    position: relative;
    height: 10.5rem;
    width: 3rem;
    min-width: 3rem;
    max-width: 3rem;
    padding: 0;
    vertical-align: bottom;
    overflow: visible;
  }
  #protein-matrix .matrix-col-header > span {
    position: absolute;
    left: 0.35rem;
    bottom: 0.35rem;
    display: block;
    white-space: nowrap;
    line-height: 1;
    font-size: 0.92em;
    transform: rotate(-45deg);
    transform-origin: left bottom;
  }
  @media (max-width: 800px) {
    #protein-matrix .td-right {
      width: auto;
      min-width: auto;
    }
    #protein-matrix .matrix-col-header {
      height: auto;
      width: auto;
      min-width: auto;
      max-width: none;
      padding: 0.5rem 0.4rem;
    }
    #protein-matrix .matrix-col-header > span {
      position: static;
      display: inline;
      white-space: normal;
      line-height: inherit;
      transform: none;
      transform-origin: initial;
    }
  }
</style>

<div id="protein-matrix-container" class="mt-6">
  <div id="protein-matrix"></div>
</div>

<script>
  const proteinMatrixEl = document.querySelector("#protein-matrix");
  const tableBodyEl = document.querySelector("#protein-tbody");

  function getProteinDataRows() {
    return Array.from(tableBodyEl.querySelectorAll("tr")).map(row => {
      const source = row.querySelector("td:first-child").textContent.trim();
      const protein = parseFloat(row.dataset.protein);
      const calories = parseFloat(row.dataset.calories);
      return { source, protein, calories, calPer95g: (95 / protein) * calories };
    }).filter(x => !isNaN(x.calPer95g));
  }

  function getCellStyle(val, extraClass = "") {
    let color = "";
    let fontWeight = "";

    if (val <= 0.75) {
      color = "steelblue";
      fontWeight = "font-bold";
    } else if (val < 0.85) {
      color = "steelblue";
    } else if (val <= 1.15) {
      color = "seagreen";
    } else if (val < 2) {
      color = "lightcoral";
    } else {
      color = "lightcoral";
      fontWeight = "font-bold";
    }

    const classes = ["td-right"];
    if (extraClass) classes.push(extraClass);
    if (fontWeight) classes.push(fontWeight);

    return {
      className: classes.join(" "),
      style: `color: ${color};`
    };
  }

  function buildMatrix(data) {
    const n = data.length;
    const matrix = [];

    
    for (let i = 0; i < n; i++) {
      matrix[i] = [];
      for (let j = 0; j < n; j++) {
        matrix[i][j] = data[i].calPer95g / data[j].calPer95g;
      }
    }

    
    const rowAverages = matrix.map(row =>
      row.reduce((sum, val) => sum + val, 0) / n
    );

    const table = document.createElement("table");
    table.className = "table-style text-sm";

    
    const thead = document.createElement("thead");
    const headerRow = document.createElement("tr");

    headerRow.innerHTML = `
      <th class="th-left" aria-hidden="true"></th>
      <th class="th-top matrix-col-header"><span>Average</span></th>
      ${data.map(item => `<th class="th-top matrix-col-header"><span>${item.source}</span></th>`).join('')}
    `;
    thead.appendChild(headerRow);
    table.appendChild(thead);

    
    const tbody = document.createElement("tbody");
    for (let i = 0; i < n; i++) {
      const row = document.createElement("tr");

      const avg = rowAverages[i];
      const avgStyle = getCellStyle(avg, "bg-blue-50");

      row.innerHTML = `
        <th class="th-left nowrap">${data[i].source}</th>
        <td class="${avgStyle.className}" style="${avgStyle.style}">${avg.toFixed(2)}</td>
        ${matrix[i].map(val => {
          const cellStyle = getCellStyle(val);
          return `<td class="${cellStyle.className}" style="${cellStyle.style}">${val.toFixed(2)}</td>`;
        }).join('')}
      `;
      tbody.appendChild(row);
    }

    table.appendChild(tbody);
    return table;
  }

  function updateMatrixView() {
    const proteinData = getProteinDataRows();
    proteinMatrixEl.innerHTML = "";
    if (proteinData.length === 0) return;
    const matrixTable = buildMatrix(proteinData);
    proteinMatrixEl.appendChild(matrixTable);
  }

  const observer = new MutationObserver(updateMatrixView);
  observer.observe(tableBodyEl, { childList: true, subtree: true, attributes: true, attributeFilter: ['data-protein', 'data-calories'] });

  document.querySelector("#target-range").addEventListener("input", updateMatrixView);
  document.querySelector("#weight-input").addEventListener("input", updateMatrixView);
  document.querySelectorAll("input[name='unit']").forEach(el => el.addEventListener("change", updateMatrixView));

  updateMatrixView();
</script>

<p>Your weight and training status don’t matter here — this table is the same for everyone, assuming equal daily protein intake. The first column shows a simple mean of each row’s relative efficiency compared to others — a rough estimate of how each food stacks up overall.
<strong>Smaller numbers are better.</strong>
High-fat items like salmon and nuts, and high-carb ones like peas and chili, form relative &ldquo;islands of inefficiency&rdquo; among high-protein sources.</p>
<p>You can even think of this matrix as a way to zone:
<span style="color: steelblue;">cutting</span> /
<span style="color: seagreen;">maintaining</span> /
<span style="color: lightcoral;">bulking</span>
cycles using
<span style="color: steelblue;">blues</span> /
<span style="color: seagreen;">greens</span> /
<span style="color: lightcoral;">reds</span>
as a visual cue for how your shopping list and pantry pars might shift depending on your current training status.</p>
<p>For example, Greek yogurt has over five times the protein density of tree nuts by volume. If you’re trying to control weight and meal prep, having this kind of visual measure might help you balance portions — say, between yogurt and nuts in your breakfast bowl.</p>
<p>Note also that the
<span style="color: lightcoral;">red, lower-in-protein efficiency</span>
items are still quite efficient. For example, gas station treats like trollies and doritos have relative efficiency values order(s) of magnitude higher.  Compared to greek yogurt, their protein efficiencies are astonomically bad
$\text{eff.}\left(\text{trollies}:\text{yogurt}\right)$
<span style="color: lightcoral;">
$\approx 32$
</span>
and
$\text{eff.}\left(\text{doritos}:\text{yogurt}\right)$
<span style="color: lightcoral;">
$\approx 13$.
</span>
<em>Abysmal.</em></p>
<h2 id="discussion">Discussion</h2>
<p>This lens is useful because it translates the quantities into <strong>grocery store</strong> units—like pack sizes. About a dozen eggs is one day; two pounds of beef and two pounds of chicken is about three days; a tub of cottage cheese (24oz) and a tub of Greek yogurt (1qt) is just under two days. Then 3–4 scoops of protein powder can help balance that week&rsquo;s diet.</p>
<p>There is also an upper limit to the amount of protein available on the planet. 70% of freshwater is already in use; 50% of global land space is already dedicated to agriculture. It’s a 60–40 split between plant- and animal-sourced proteins occupying this space—and both the global population and protein demand will increase by 20–25% in the next 25 years <sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>.</p>
<p>From a purely resource standpoint, there will need to be a lab-grown protein synthesis renaissance. And it&rsquo;s this fact alone that makes the &ldquo;$1 \text{g} \ \text{protein} / \text{lbs} / \text{day}$&rdquo; myth not only overly generous, but also inconsiderate of the planet’s biological limits. One-third of that is the target if you aren’t lifting. Half to two-thirds is more realistic if you’re engaged in moderate to <em>heavy</em> resistance training.</p>
<h2 id="references">References</h2>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Nunes, E. A., Colenso-Semple, L., McKellar, S. R., et al. (2022). <em>Systematic review and meta-analysis of protein intake to support muscle mass and function in healthy adults</em>. <em>Journal of cachexia, sarcopenia and muscle</em>. <a href="https://pubmed.ncbi.nlm.nih.gov/35187864/">https://pubmed.ncbi.nlm.nih.gov/35187864/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Schoenfeld, B. J., &amp; Aragon, A. A. (2021). <em>The effect of protein timing on muscle strength and hypertrophy: A systematic review and meta-analysis</em>. <em>Journal of the International Society of Sports Nutrition</em>. <a href="https://link.springer.com/content/pdf/10.1186/1550-2783-10-53.pdf">https://link.springer.com/content/pdf/10.1186/1550-2783-10-53.pdf</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p>Phillips, S. M. (2014). <em>A brief review of critical processes in exercise-induced muscular hypertrophy</em>. <em>Sports Medicine</em>. <a href="https://link.springer.com/article/10.1007/s40279-014-0152-3">https://link.springer.com/article/10.1007/s40279-014-0152-3</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p>Moore, D. R., Atherton, P. J., Rennie, M. J., &amp; Phillips, S. M. (2011). <em>Resistance exercise enhances mTOR and MAPK signalling in human muscle over that seen at rest after bolus protein ingestion</em>. <em>Acta physiologica</em>. <a href="https://onlinelibrary.wiley.com/doi/abs/10.1111/j.1748-1716.2010.02187.x">https://onlinelibrary.wiley.com/doi/abs/10.1111/j.1748-1716.2010.02187.x</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p>Paddon-Jones, D., &amp; Rasmussen, B. B. (2009). <em>Dietary protein recommendations and the prevention of sarcopenia</em>. <em>Current opinion in clinical nutrition &amp; metabolic care</em>. <a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC2760315/pdf/nihms111079.pdf">https://pmc.ncbi.nlm.nih.gov/articles/PMC2760315/pdf/nihms111079.pdf</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p>Smith, K., Watson, A. W., Lonnie, M., Peeters, W. M., Oonincx, D., Tsoutsoura, N., &hellip; &amp; Corfe, B. M. (2024). <em>Meeting the global protein supply requirements of a growing and ageing population</em>. <em>European journal of nutrition</em>. <a href="https://pmc.ncbi.nlm.nih.gov/articles/PMC11329409/">https://pmc.ncbi.nlm.nih.gov/articles/PMC11329409/</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
    <item>
      <title>Non-linear Weightlifting Progression Calculator</title>
      <link>https://brege.org/post/nonlinear-weightlifting-progression-scheme/</link>
      <pubDate>Tue, 25 Mar 2025 00:00:00 +0000</pubDate>
      <guid>https://brege.org/post/nonlinear-weightlifting-progression-scheme/</guid>
      <description>Using the Epley formula, and others, for estimating an effective pathway through weightlifting plateaus</description>
      <content:encoded><![CDATA[<p>Consider the Epley formula for estimating one-rep max weight:</p>
<p>$$
W_\text{1RM} = W \cdot \left(1 + \frac{R}{30}\right)
$$</p>
<p>To compute your one-rep max weight for a given lift, $W_{1\text{RM}}$, you simply input the weight lifted, $W$, and the number of reps performed before failure, $R$.</p>
<p>This formula has been empirically validated <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and is a useful tool not just for estimating your max weight for a given weight-rep pair $\left(W, R\right)$, but also to compute the number of predictable reps you can do at any other given weight, relative to your one-rep max.</p>
<p>This is quite useful for plateaus. For example, if you get stuck progressing the Overhead Press to 115 lbs for 5 reps after successfully performing the OHP for 110 lbs for 5 reps, you may need to strategize. Using the Epley formula, to go from
$$
\left( 110\text{lb}, 5 \text{reps} \right) \rightarrow \left(115 \text{lb}, 5 \text{reps}\right),
$$
you are effectively increasing your one-rep max: $W_\text{1RM}: 128 \text{lbs} \rightarrow 134 \text{lbs}$. Our goal here is to effectively increment your $W_\text{1RM}$ in smaller steps:
$$
\small
\left( 110\text{lb}, 5 \text{reps} \right)
\rightarrow \left(115 \text{lb}, 4 \text{reps}\right)
\rightarrow \left(110 \text{lb}, 6 \text{reps}\right)
\rightarrow \left(105 \text{lb}, 8 \text{reps}\right)
\rightarrow \left(115 \text{lb}, 5 \text{reps}\right)
$$
because this is the same as
$$
W_{1\text{RM}}: 128.33 \text{lb}
\rightarrow 130.33 \text{lb}
\rightarrow 132.00 \text{lb}
\rightarrow 133.00 \text{lb}
\rightarrow 134.17 \text{lb} \\
W_{5\text{RM}}: 110.00 \text{lb}
\rightarrow 111.71 \text{lb}
\rightarrow 113.14 \text{lb}
\rightarrow 114.00 \text{lb}
\rightarrow 115.00 \text{lb}
$$
I refer to this as the <strong>Epley pathway</strong>.</p>
<p>Because the Epley formula is nonlinear, this requires an iterative approach. I&rsquo;ve made a calculator that will help you find this path over different $\left(W, R\right)$ pairs. It defaults to using a range of reps roughly half that of your initial number, $R_0$, and a few &ldquo;sweet spot&rdquo; rep values.</p>
<link rel="stylesheet" href="/css/calculator.css">

<div id="calculator">
  <label for="weight" class="form-label"><b>Initial Weight</b> (lbs):</label>
  <input type="number" id="weight" class="form-control" value="135" min="10" max="1000">

  <label for="reps" class="form-label"><b>Initial Reps</b>:</label>
  <input type="number" id="reps" class="form-control" value="5" min="1" max="30">

  <label for="repRange" class="form-label"><b>Rep Range Variation:</b></label>
  <select id="repRange" class="post-tags">
    <option value="0">hybrid: ±R₀/2 and (8, 10, 12, 15, 20, 30)</option>
    <option value="1">R₀±5 reps</option>
    <option value="2">±R₀</option>
    <option value="3">R₀±8 reps</option>
    <option value="4">R₀±10 reps</option>
    <option value="5">R₀±12 reps</option>
    <option value="6">R₀±15 reps</option>
    <option value="7">All reps: [0,..,30]</option>
  </select>

  <label for="formulaSelect" class="form-label"><b>1RM Formula:</b></label>
  <select id="formulaSelect" class="post-tags">
    <option value="epley">Epley</option>
    <option value="brzycki">Brzycki</option>
    <option value="lombardi">Lombardi</option>
    <option value="oconner">O’Conner</option>
  </select>

  
  <div class="calculator-explanation">
    <details open=true> 
<summary> Show/Hide extra notes </summary>
<p>You&rsquo;ll note that I&rsquo;ve generalized this calculator to also include different formulas that different apps use:</p>
<p>$$
W_{\text{1RM}} = f(W, R)
$$</p>
<p>Try changing the dropdown to see how different curves generate slightly different effective rep ranges.  To read more on the survey of different calculations, check out the <a href="https://en.wikipedia.org/wiki/One-repetition_maximum">One-repetition maximum article on Wikipedia</a>.</p>
</details>

  </div>
  

  <h3 id="formulaHeading">Epley Pathway </h3>
  <div id="formulaLatex" class="katex-block" style="margin-bottom: 1rem;"></div>

  <table id="resultsTable" class="table table-dark">
    <thead>
      <tr>
        <th>Weight (lbs)</th>
        <th>Reps</th>
        <th>1RM (lbs)</th>
        <th id="effectiveRMHeader">Effective <br> 5RM (lbs)</th>
      </tr>
    </thead>
    <tbody></tbody>
  </table>

  <div id="chart-container"></div>

  <script>
    function calculate1RM(weight, reps) {
      const formula = document.getElementById('formulaSelect').value;
      switch (formula) {
        case 'epley': return weight * (1 + reps / 30);
        case 'brzycki': return weight / (1.0278 - 0.0278 * reps);
        case 'lombardi': return weight * Math.pow(reps, 0.10);
        case 'oconner': return weight * (1 + 0.025 * reps);
        default: return weight * (1 + reps / 30);
      }
    }

    function calculateEffectiveRM(estimated1RM, reps) {
      const formula = document.getElementById('formulaSelect').value;
      switch (formula) {
        case 'epley': return estimated1RM / (1 + reps / 30);
        case 'brzycki': return estimated1RM * (1.0278 - 0.0278 * reps);
        case 'lombardi': return estimated1RM / Math.pow(reps, 0.10);
        case 'oconner': return estimated1RM / (1 + 0.025 * reps);
        default: return estimated1RM / (1 + reps / 30);
      }
    }

    function updateFormulaHeading() {
      const formula = document.getElementById('formulaSelect').value;

      const headingMap = {
        epley: "Epley Pathway",
        brzycki: "Brzycki Pathway",
        lombardi: "Lombardi Pathway",
        oconner: "O’Conner Pathway"
      };

      const latexMap = {
        epley: "W_{\\text{1RM}} = W \\cdot \\left(1 + \\frac{R}{30}\\right)",
        brzycki: "W_{\\text{1RM}} = \\frac{W}{1.0278 - 0.0278R}",
        lombardi: "W_{\\text{1RM}} = W \\cdot R^{0.10}",
        oconner: "W_{\\text{1RM}} = W \\cdot (1 + 0.025R)"
      };

      document.getElementById('formulaHeading').textContent = headingMap[formula] || "Epley Pathway";

      const formulaLatex = latexMap[formula] || latexMap["epley"];
      const formulaLatexDiv = document.getElementById('formulaLatex');

      if (window.katex) {
        katex.render(formulaLatex, formulaLatexDiv, { throwOnError: false });
      } else {
        formulaLatexDiv.textContent = `\\(${formulaLatex}\\)`;
      }
    }

    function getRepRange(repVariation, R_0) {
      const reps = new Set();

      if (repVariation === 0) {
        
        const rStart = Math.floor(R_0 / 2);
        const rEnd = Math.floor(3 * R_0 / 2);
        for (let r = rStart; r <= rEnd; r++) reps.add(r);

        
        [8, 10, 12, 15, 20, 30].forEach(r => reps.add(r));
      } else if (repVariation === 1) {
        for (let r = Math.max(1, R_0 - 5); r <= Math.min(30, R_0 + 5); r++) reps.add(r);
      } else if (repVariation === 2) {
        for (let r = 0; r <= 2 * R_0; r++) reps.add(r);
      } else if (repVariation === 3) {
        for (let r = Math.max(1, R_0 - 8); r <= Math.min(30, R_0 + 8); r++) reps.add(r);
      } else if (repVariation === 4) {
        for (let r = Math.max(1, R_0 - 10); r <= Math.min(30, R_0 + 10); r++) reps.add(r);
      } else if (repVariation === 5) {
        for (let r = Math.max(1, R_0 - 12); r <= Math.min(30, R_0 + 12); r++) reps.add(r);
      } else if (repVariation === 6) {
        for (let r = Math.max(1, R_0 - 15); r <= Math.min(30, R_0 + 15); r++) reps.add(r);
      } else if (repVariation === 7) {
        for (let r = 0; r <= 30; r++) reps.add(r);
      }

      return Array.from(reps).filter(r => r >= 1 && r <= 30).sort((a, b) => a - b);
    }

    function calculateResults() {
      const W_0 = parseInt(document.getElementById('weight').value);
      const R_0 = parseInt(document.getElementById('reps').value);
      const repVariation = parseInt(document.getElementById('repRange').value);

      const repsToTry = getRepRange(repVariation, R_0);
      const lowerLimit = W_0;
      const upperLimit = W_0 + 5;

      document.getElementById('effectiveRMHeader').textContent = `Effective ${R_0}RM (lbs)`;

      const pairs = [];
      for (let W = 10; W <= 1000; W += 5) {
        for (const R of repsToTry) {
          const oneRM = calculate1RM(W, R);
          const effectiveRM = calculateEffectiveRM(oneRM, R_0);
          if (effectiveRM >= lowerLimit && effectiveRM <= upperLimit) {
            pairs.push({ W, R, oneRM, effectiveRM });
          }
        }
      }

      pairs.sort((a, b) => a.effectiveRM - b.effectiveRM);

      const tbody = document.querySelector("#resultsTable tbody");
      tbody.innerHTML = '';

      let finalRowAdded = false;
      for (const row of pairs) {
        const tr = tbody.insertRow();
        tr.insertCell(0).textContent = row.W;
        tr.insertCell(1).textContent = row.R;
        tr.insertCell(2).textContent = row.oneRM.toFixed(2);
        tr.insertCell(3).textContent = row.effectiveRM.toFixed(2);
        if (Math.abs(row.W - W_0) <= 5) tr.classList.add('bold-weight');
        if (row.W === W_0 + 5 && row.R === R_0) finalRowAdded = true;
      }

      if (!finalRowAdded) {
        const finalOneRM = calculate1RM(W_0 + 5, R_0);
        const finalEffective = calculateEffectiveRM(finalOneRM, R_0);
        const finalRow = tbody.insertRow();
        finalRow.insertCell(0).textContent = W_0 + 5;
        finalRow.insertCell(1).textContent = R_0;
        finalRow.insertCell(2).textContent = finalOneRM.toFixed(2);
        finalRow.insertCell(3).textContent = finalEffective.toFixed(2);
        finalRow.classList.add('bold-weight');
      }

      tbody.querySelectorAll("tr").forEach(tr => {
        tr.addEventListener("click", () => {
          tbody.querySelectorAll("tr").forEach(r => r.classList.remove("highlighted-row"));
          tr.classList.add("highlighted-row");
        });
      });

      updateChart(pairs);
    }


    document.getElementById('formulaSelect').addEventListener('change', () => {
      updateFormulaHeading();
      calculateResults();
    });

    document.getElementById('weight').addEventListener('input', calculateResults);
    document.getElementById('reps').addEventListener('input', calculateResults);
    document.getElementById('repRange').addEventListener('change', calculateResults);

    window.addEventListener('DOMContentLoaded', () => {
      updateFormulaHeading();
      calculateResults();
    });
  </script>

  <hr/>
</div>

<style>
  #resultsTable td {
    padding: 0.5rem !important;
    margin: 0 !important;
  }
  #resultsTable th {
    padding: 0.75rem !important;
  }
</style>


<p><em>More details of different, specific formulae for Epley <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> Brzycki <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, Lombardi <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> and O&rsquo;Connor <sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>, and several complimentary research articles <sup id="fnref1:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> and <sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>, are available.</em></p>
<hr />
<h3 id="my-progression-scheme">My Progression Scheme</h3>
<p>This makes it straightforward for me to also compute the warmup sets needed for my leading barbell exercise. Recently, I&rsquo;ve found good success in pyramiding warmups using my calculated $\left(30, 20, 10\right)\text{RM}$&rsquo;s for $\left(12, 8, 4\right) \text{reps}$, respectively.</p>
<p>In addition, another scheme I&rsquo;ve been using for strength-focused exercises has been, after 3–6 reps of heavy weight for three sets, to drop set into $\left(10, 20\right)\text{RM}$ for $\left(8, 12\ldots\text{amrap}\right)$, respectively, to increase my work volume without chewing up my joints, leaving $\sim \text{2}$ $\text{rir}$. Here, $\text{amrap} = $ &ldquo;as many reps as possible&rdquo; and $\text{rir} =$ &ldquo;reps in reserve&rdquo;. My thoughts here reflect the goal to cover warmup (3), strength (3–4), and hypertrophy (2), in a single leading compound exercise.</p>
<div class="progression-chart">
  <svg id="progressionChart" width="100%" height="auto" viewBox="0 0 800 400" preserveAspectRatio="xMidYMid meet"></svg>

  
    <div class="progression-caption"><b>Progression Chart Example: Set Breakdown for Leading Barbell Lift.</b> The width of the bins represents the number of reps, $R$, denoted inside the bins. The weights, $W_{N\text{RM}}$, are in lbs, represented as the height of the bins. From left to right, the weights in my scheme are: 30RM, 20RM, 10RM, 5RM (3x's), 10RM, 20RM. This provides a visual sense of volume in each zone of the exercise. <i>If you change the weights in the calculator, this plot will update. Because this is a hybrid model for strength training and hypertrophy, it will bind the rep ranges between 3–6 reps. The $y$-axis is truncated at ⅔ $W_{\text{30RM}}$.</i></div>
  
</div>

<script src="https://d3js.org/d3.v7.min.js"></script>

<script>
  let inputWeight =  135 ;
  let inputReps =  5 ;
  let chartData = [];

  function progression_calculate1RM(weight, reps) {
    return weight * (1 + reps / 30);
  }

  function progression_roundToNearest5(num) {
    return Math.round(num / 5) * 5;
  }

  function progression_calculateWeights(weight, reps) {
    const W_1RM = progression_roundToNearest5(progression_calculate1RM(weight, reps));
    const W_30RM = progression_roundToNearest5(W_1RM / (1 + 30 / 30));
    const W_20RM = progression_roundToNearest5(W_1RM / (1 + 20 / 30));
    const W_10RM = progression_roundToNearest5(W_1RM / (1 + 10 / 30));
    const W_5RM = progression_roundToNearest5(weight);
    return { W_30RM, W_20RM, W_10RM, W_5RM, W_1RM };
  }

  function progression_updateChart() {
    const constrainedReps = Math.max(3, Math.min(inputReps, 6));
    const { W_30RM, W_20RM, W_10RM, W_5RM, W_1RM } = progression_calculateWeights(inputWeight, constrainedReps);

    chartData = [
      { label: '12 reps (Warmup)', weight: W_30RM, reps: 12, zone: 'warmup' },
      { label: '8 reps (Warmup)', weight: W_20RM, reps: 8, zone: 'warmup' },
      { label: '4 reps (Warmup)', weight: W_10RM, reps: 4, zone: 'warmup' },
      { label: `${constrainedReps} reps (Workset 1)`, weight: inputWeight, reps: constrainedReps, zone: 'worksets' },
      { label: `${constrainedReps} reps (Workset 2)`, weight: inputWeight, reps: constrainedReps, zone: 'worksets' },
      { label: `${constrainedReps} reps (Workset 3)`, weight: inputWeight, reps: constrainedReps, zone: 'worksets' },
      { label: '8 reps (Dropset)', weight: W_10RM, reps: 8, zone: 'dropsets' },
      { label: '12 reps (Dropset)', weight: W_20RM, reps: 12, zone: 'dropsets' }
    ];

    const svg = d3.select("#progressionChart");
    svg.selectAll("*").remove();

    const margin = { top: 20, right: 20, bottom: 60, left: 40 };
    const width = 800 - margin.left - margin.right;
    const height = 400 - margin.top - margin.bottom;
    const svgGroup = svg
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("transform", `translate(${margin.left},${margin.top})`);

    const xScale = d3.scaleBand()
      .domain(chartData.map(d => d.label))
      .range([0, width])
      .padding(0.1);

    const yMin = chartData[0].weight / 1.5;
    const yMax = d3.max(chartData, d => d.weight) * 1.1;

    const yScale = d3.scaleLinear()
      .domain([yMin, yMax])
      .range([height, 0]);

    const barWidth = (reps) => reps * 10;
    const spacing = 5;
    let midPoints = [];
    let currentXPosition = 0;

    svgGroup.selectAll(".bar")
      .data(chartData)
      .enter()
      .append("rect")
      .attr("class", "bar")
      .attr("x", function(d, i) {
        const xPosition = currentXPosition;
        midPoints.push(xPosition + barWidth(d.reps) / 2);
        currentXPosition += barWidth(d.reps) + spacing;
        return xPosition;
      })
      .attr("y", d => yScale(d.weight))
      .attr("width", d => barWidth(d.reps))
      .attr("height", d => height - yScale(d.weight))
      .attr("fill", (d) => {
        if (d.zone === "warmup") return "seagreen";
        if (d.zone === "worksets") return "steelblue";
        return "lightcoral";
      });

    svgGroup.selectAll(".bar-weight")
      .data(chartData)
      .enter()
      .append("text")
      .attr("class", "bar-weight")
      .attr("x", (d, i) => midPoints[i])
      .attr("y", d => yScale(d.weight) - 10)
      .attr("text-anchor", "middle")
      .attr("font-size", "12px")
      .attr("fill", "white")
      .text(d => `${d.weight.toFixed(1)} lbs`)
      .style("fill", "var(--primary)");

    svgGroup.selectAll(".bar-reps")
      .data(chartData)
      .enter()
      .append("g")
      .attr("class", "bar-rep-group")
      .append("text")
      .attr("class", "bar-reps")
      .attr("x", (d, i) => midPoints[i])
      .attr("y", (d) => yScale(d.weight) + (height - yScale(d.weight)) / 2)
      .attr("text-anchor", "middle")
      .attr("font-size", "14px")
      .attr("fill", "var(--theme)")
      .attr("font-weight", "bold")
      .text(d => `${d.reps} reps`)
      .style("z-index", "10");

    const zoneMidpoints = {
      warmup: (midPoints[0] + midPoints[2]) / 2,
      worksets: (midPoints[3] + midPoints[5]) / 2,
      dropsets: (midPoints[6] + midPoints[7]) / 2
    };

    svgGroup.selectAll(".zone-label")
      .data(Object.keys(zoneMidpoints))
      .enter()
      .append("text")
      .attr("class", "zone-label")
      .attr("x", d => zoneMidpoints[d])
      .attr("y", height + 25)
      .attr("text-anchor", "middle")
      .attr("font-size", "14px")
      .text(d => d.charAt(0).toUpperCase() + d.slice(1))
      .style("fill", "var(--primary)");

    const totalWidth = chartData.reduce((acc, d) => acc + barWidth(d.reps) + spacing, 0);
    svgGroup.append("g")
      .attr("class", "x-axis")
      .attr("transform", `translate(0, ${height})`)
      .append("line")
      .attr("x1", 0)
      .attr("x2", totalWidth)
      .attr("y1", 0)
      .attr("y2", 0)
      .style("stroke", "var(--primary)")
      .style("stroke-width", "1px");

    svgGroup.append("g")
      .call(d3.axisLeft(yScale));

    svgGroup.selectAll(".axis text")
      .style("font-size", "14px")
      .style("fill", "var(--primary)");

    d3.select(".progression-chart")
      .style("background", "var(--code-bg)")
      .style("border", "1px solid var(--border)")
      .style("border-radius", "var(--radius)")
      .style("padding", "1rem");
  }

  document.getElementById('weight').addEventListener('input', (event) => {
    inputWeight = parseInt(event.target.value);
    progression_updateChart();
  });

  document.getElementById('reps').addEventListener('input', (event) => {
    inputReps = parseInt(event.target.value);
    progression_updateChart();
  });

  progression_updateChart();
</script>


<h3 id="possible-improvements">Possible Improvements</h3>
<ul>
<li>
<p>Create a similar pathway table for the goal of adding a rep, instead of adding $5 \text{lbs}$ to the bar.  Will take a good amount of effort.</p>
</li>
<li>
<p>Implement a pyramid scheme with broader rep ranges in the progression chart histogram. I haven&rsquo;t thought much about this yet, but when I can hit 7 reps on heavy compound lifts, I feel like I&rsquo;m entering a moderately safe 7–12 rep range. At that point, the effort level shifts to something different.</p>
</li>
<li>
<p><del>The Epley model is just one of many. I haven&rsquo;t explored the others yet because I could solve this with fractional arithmetic in my head while &ldquo;ape-brained&rdquo; in the gym. However, adding a dropdown toggle to choose different models, and referring to recent literature on advancements in curve fitting, would make this tool more robust.</del> Similar to how the Opus audio codec performs at different fidelities with various bitrates, I&rsquo;m sure that the pathway from 1RM to 30RM follows a more complex, multi-fitted approach.</p>
</li>
</ul>
<h3 id="references">References</h3>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p><a href="https://opensiuc.lib.siu.edu/cgi/viewcontent.cgi?article=1744&amp;context=gs_rp">https://opensiuc.lib.siu.edu/cgi/viewcontent.cgi?article=1744&amp;context=gs_rp</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a>&#160;<a href="#fnref1:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p><a href="https://scholar.google.com/scholar?hl=en&amp;as_sdt=0%2C23&amp;q=+Epley%2C+Boyd+%281985%29.+%22Poundage+Chart%22.+Boyd+Epley+Workout.+Lincoln%2C+NE%3A+Body+Enterprises.+p.+86.+&amp;btnG=">https://scholar.google.com/scholar?hl=en&amp;as_sdt=0%2C23&amp;q=+Epley%2C+Boyd+%281985%29.+%22Poundage+Chart%22.+Boyd+Epley+Workout.+Lincoln%2C+NE%3A+Body+Enterprises.+p.+86.+&amp;btnG=</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3">
<p><a href="https://scholar.google.com/scholar?hl=en&amp;as_sdt=0%2C23&amp;q=+Brzycki%2C+Matt+%281998%29.+A+Practical+Approach+To+Strength+Training.+McGraw-Hill.+ISBN+978-1-57028-018-4.+&amp;btnG=">https://scholar.google.com/scholar?hl=en&amp;as_sdt=0%2C23&amp;q=+Brzycki%2C+Matt+%281998%29.+A+Practical+Approach+To+Strength+Training.+McGraw-Hill.+ISBN+978-1-57028-018-4.+&amp;btnG=</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4">
<p><a href="https://www.unm.edu/~rrobergs/478RMStrengthPrediction.pdf">https://www.unm.edu/~rrobergs/478RMStrengthPrediction.pdf</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5">
<p><a href="https://www.medicalalgorithms.com/equation-of-oconnor-et-al-for-predicting-the-one-repetition-maximum-1-rm">https://www.medicalalgorithms.com/equation-of-oconnor-et-al-for-predicting-the-one-repetition-maximum-1-rm</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6">
<p><a href="https://www.researchgate.net/profile/Marcelo-Silva-12/post/Prediction_of_1RM_in_muscular_strength_what_is_the_better_7-10_rep_or_5-7_rep/attachment/59d6235b79197b8077981b20/AS%3A306915098202113%401450185669112/download/478RMStrengthPrediction.pdf">https://www.researchgate.net/profile/Marcelo-Silva-12/post/Prediction_of_1RM_in_muscular_strength_what_is_the_better_7-10_rep_or_5-7_rep/attachment/59d6235b79197b8077981b20/AS%3A306915098202113%401450185669112/download/478RMStrengthPrediction.pdf</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded>
    </item>
  </channel>
</rss>
