<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: forestelf</title>
    <description>The latest articles on DEV Community by forestelf (@forestelf).</description>
    <link>https://dev.to/forestelf</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4189993%2F145e030c-bb94-46e7-a5c2-fa4b895b9830.png</url>
      <title>DEV Community: forestelf</title>
      <link>https://dev.to/forestelf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/forestelf"/>
    <language>en</language>
    <item>
      <title>Why Traditional Sourdough Calculators Have a Hidden 3% Water Error (And How We Fixed It With Rheology &amp; Thermodynamics)</title>
      <dc:creator>forestelf</dc:creator>
      <pubDate>Sat, 17 Oct 2026 07:10:04 +0000</pubDate>
      <link>https://dev.to/forestelf/why-traditional-sourdough-calculators-have-a-hidden-3-water-error-and-how-we-fixed-it-with-p94</link>
      <guid>https://dev.to/forestelf/why-traditional-sourdough-calculators-have-a-hidden-3-water-error-and-how-we-fixed-it-with-p94</guid>
      <description>&lt;p&gt;If you ask any software engineer what their weekend hobby is, there is a surprisingly high probability the answer is &lt;strong&gt;sourdough bread baking&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Both disciplines scratch the same neurological itch: deterministic algorithms meeting complex organic state machines. You feed living wild yeast cultures, balance flour-to-water ratios, and wait for bulk fermentation state transitions.&lt;/p&gt;

&lt;p&gt;Yet, after baking hundreds of loaves and analyzing popular bread calculators across the web, I discovered a glaring mathematical flaw that plagues almost every conventional baking calculator: &lt;strong&gt;the hidden 3% hydration trap&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here is why standard calculators fail, the polymer science behind dough stickiness, and how we engineered an open-source, edge-rendered solution using Next.js 14, Cloudflare, and thermodynamic physics.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Hidden 3% Hydration Flaw
&lt;/h2&gt;

&lt;p&gt;In standard baker's percentage accounting, every ingredient is calculated relative to the total flour weight ($100\%$).&lt;/p&gt;

&lt;p&gt;A classic recipe might read:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flour&lt;/strong&gt;: $500\text{g}$ ($100\%$)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Water&lt;/strong&gt;: $375\text{g}$ ($75\%$)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Levain (Starter)&lt;/strong&gt;: $100\text{g}$ ($20\%$)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Salt&lt;/strong&gt;: $10\text{g}$ ($2\%$)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The baker assumes they are handling a &lt;strong&gt;$75\%$ hydration dough&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Mathematical Bug:
&lt;/h3&gt;

&lt;p&gt;A standard sourdough starter is maintained at $100\%$ hydration (equal parts flour and water by weight). That means your $100\text{g}$ of levain is secretly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$50\text{g}$ flour&lt;/li&gt;
&lt;li&gt;$50\text{g}$ water&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you recalculate the &lt;strong&gt;True Total Hydration&lt;/strong&gt; across the entire dough matrix:&lt;br&gt;
$$\text{Total Flour} = 500\text{g} + 50\text{g} = 550\text{g}$$&lt;br&gt;
$$\text{Total Water} = 375\text{g} + 50\text{g} = 425\text{g}$$&lt;br&gt;
$$\text{True Total Hydration} = \frac{425\text{g}}{550\text{g}} \times 100\% = \mathbf{77.27\%}$$&lt;/p&gt;

&lt;p&gt;That is a &lt;strong&gt;$+2.27\%$ unexpected surge in liquid water&lt;/strong&gt;! If the baker uses a higher inoculation (e.g. $25\%$ starter), the error spikes beyond &lt;strong&gt;$+3.2\%$&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To a baker aiming for a manageable $75\%$ loaf, an unexpected $78\%$ matrix means the gluten network loses its elastic modulus ($G'$), resulting in an uncontrollable, sticky puddle on the bench.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Modeling Multi-Flour Water Absorption Limits
&lt;/h2&gt;

&lt;p&gt;Different flours possess radically different water absorptions due to damaged starch, protein content, and pentosan polymers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unbleached Bread Flour&lt;/strong&gt;: baseline $72\%\text{ - }76\%$&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stoneground Whole Wheat&lt;/strong&gt;: bran fibers absorb water rapidly ($80\%\text{ - }85\%$)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rye Flour&lt;/strong&gt;: rich in mucilaginous arabinoxylans, absorbing up to $85\%\text{ - }90\%$ water but forming zero traditional gluten networks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We modeled this in TypeScript as a weighted absorption rheology engine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;FlourComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;weightGrams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;proteinPercent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;absorptionRatio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// e.g. 0.74 for bread flour, 0.85 for rye&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;calculateEffectiveAbsorptionCapacity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flours&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FlourComponent&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalWeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flours&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weightGrams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalWeight&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;weightedSum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flours&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;weightGrams&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;f&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;absorptionRatio&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;weightedSum&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;totalWeight&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. The Summer DDT Crisis: When Water Must Be Colder Than Ice
&lt;/h2&gt;

&lt;p&gt;Professional bakers rely on &lt;strong&gt;Desired Dough Temperature (DDT)&lt;/strong&gt; to control fermentation kinetics:&lt;br&gt;
$$T_{\text{water}} = (3 \times \text{DDT}) - (T_{\text{room}} + T_{\text{flour}} + T_{\text{starter}} + \text{Friction Factor})$$&lt;/p&gt;

&lt;h3&gt;
  
  
  The Physical Impossibility in Hot Climates:
&lt;/h3&gt;

&lt;p&gt;Suppose your summer kitchen is at $86^\circ\text{F}$ ($30^\circ\text{C}$), flour is $84^\circ\text{F}$, starter is $82^\circ\text{F}$, and mixer friction is $4^\circ\text{F}$. You target a standard DDT of $76^\circ\text{F}$.&lt;/p&gt;

&lt;p&gt;Plug this into the formula:&lt;br&gt;
$$T_{\text{water}} = (3 \times 76) - (86 + 84 + 82 + 4) = 228 - 256 = \mathbf{-28^\circ\text{F}}\;(-33.3^\circ\text{C})$$&lt;/p&gt;

&lt;p&gt;Liquid water cannot exist at $-28^\circ\text{F}$.&lt;/p&gt;

&lt;p&gt;Standard calculators either crash, throw an error, or tell the baker "chill your water," which is physically inadequate because liquid water cannot drop below $32^\circ\text{F}$ ($0^\circ\text{C}$).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Thermodynamic Fix: Latent Heat of Fusion
&lt;/h3&gt;

&lt;p&gt;Ice does not merely cool water by its temperature difference—it absorbs &lt;strong&gt;$80\text{ cal/g}$ ($334\text{ J/g}$)&lt;/strong&gt; of energy purely to break hydrogen bonds during phase change (latent heat of fusion).&lt;/p&gt;

&lt;p&gt;We implemented the thermodynamic energy balance equation:&lt;br&gt;
$$m_{\text{ice}} = m_{\text{water, total}} \times \frac{T_{\text{tap}} - T_{\text{target}}}{T_{\text{tap}} + 144 - T_{\text{ice}} \times 0.5}$$&lt;br&gt;
&lt;em&gt;(where 144 is the BTU/lb latent heat constant)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;With this equation, the engine computes the exact gram-for-gram replacement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Crushed Ice&lt;/strong&gt;: $142\text{g}$&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chilled Tap Water&lt;/strong&gt;: $233\text{g}$&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the ice melts during autolyse, the resulting bulk dough hits $76^\circ\text{F}$ within $\pm 0.5^\circ\text{F}$.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Open-Sourcing the Rheology Engine
&lt;/h2&gt;

&lt;p&gt;We extracted the core mathematics and thermodynamic physics into a zero-dependency open-source library:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/forestelf/sourdough-physics" rel="noopener noreferrer"&gt;&lt;code&gt;forestelf/sourdough-physics&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live Web App&lt;/strong&gt;: &lt;a href="https://sourdoughcalc.app" rel="noopener noreferrer"&gt;SourdoughCalc.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License&lt;/strong&gt;: MIT&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The frontend is built on &lt;strong&gt;Next.js 14 App Router&lt;/strong&gt;, deployed globally to Cloudflare Pages edge locations with $&amp;lt;50\text{ms}$ Time to First Byte (TTFB). It also includes a kitchen companion mode with the &lt;strong&gt;Screen Wake Lock API&lt;/strong&gt;, keeping your phone display alive while kneading flour-covered dough.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. What's Next?
&lt;/h2&gt;

&lt;p&gt;We recently added kinetic microbial timing for starter feeding ratios ($1:1:1$, $1:2:2$, $1:5:5$) and geometric bulk rise calibration for conical mixing bowls.&lt;/p&gt;

&lt;p&gt;Are you baking sourdough or working on computational food science? Check out the &lt;a href="https://sourdoughcalc.app" rel="noopener noreferrer"&gt;live tool&lt;/a&gt; or explore the code on &lt;a href="https://github.com/forestelf/sourdough-physics" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Feedback and edge-case pull requests are warmly welcomed!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>opensource</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
