breath-observatory/breath-calculator.html

353 lines
14 KiB
HTML
Raw Normal View History

2026-07-18 15:44:53 +02:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Breath Exchange Calculator | Alfred Rosenberg</title>
<style>
:root {
--moss: #2d5a27;
--soil: #3d2914;
--limestone: #e8e4dc;
--damp: #1a3a1a;
--sky: #87ceeb;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
background: linear-gradient(180deg, var(--soil) 0%, var(--moss) 40%, var(--damp) 100%);
color: var(--limestone);
min-height: 100vh;
padding: 2rem;
line-height: 1.6;
}
.container {
max-width: 900px;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 3rem;
border-bottom: 2px solid var(--limestone);
padding-bottom: 2rem;
}
h1 {
font-size: clamp(2rem, 5vw, 3rem);
font-weight: 700;
letter-spacing: -0.02em;
margin-bottom: 1rem;
background: linear-gradient(90deg, var(--limestone), var(--sky));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
.subtitle {
font-size: 1.1rem;
opacity: 0.9;
font-style: italic;
}
.calculator-card {
background: rgba(26, 58, 26, 0.7);
backdrop-filter: blur(10px);
border-radius: 16px;
padding: 2rem;
margin-bottom: 2rem;
border: 1px solid rgba(232, 228, 220, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
.input-group {
margin-bottom: 1.5rem;
}
label {
display: block;
font-size: 0.9rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: var(--sky);
}
input[type="number"], select {
width: 100%;
padding: 0.75rem 1rem;
background: rgba(232, 228, 220, 0.1);
border: 1px solid var(--moss);
border-radius: 8px;
color: var(--limestone);
font-size: 1rem;
transition: all 0.3s ease;
}
input[type="number"]:focus, select:focus {
outline: none;
border-color: var(--sky);
box-shadow: 0 0 0 3px rgba(135, 206, 235, 0.2);
}
.btn-calculate {
width: 100%;
padding: 1rem;
background: linear-gradient(180deg, var(--sky), var(--moss));
border: none;
border-radius: 8px;
color: var(--soil);
font-size: 1.1rem;
font-weight: 700;
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
margin-top: 1rem;
}
.btn-calculate:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(135, 206, 235, 0.4);
}
.results-panel {
margin-top: 2rem;
padding: 1.5rem;
background: rgba(232, 228, 220, 0.05);
border-radius: 12px;
border-left: 4px solid var(--sky);
display: none;
}
.result-row {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 1rem;
padding: 0.75rem 0;
border-bottom: 1px solid rgba(232, 228, 220, 0.1);
}
.result-row:last-child {
border-bottom: none;
}
.result-label {
font-weight: 600;
color: var(--sky);
}
.result-value {
font-family: 'JetBrains Mono', monospace;
font-size: 1.1rem;
text-align: right;
color: var(--limestone);
}
.methodology {
margin-top: 3rem;
padding: 2rem;
background: rgba(0, 0, 0, 0.2);
border-radius: 12px;
font-size: 0.9rem;
line-height: 1.8;
}
.methodology h3 {
color: var(--sky);
margin-bottom: 1rem;
font-size: 1.2rem;
}
.citation {
display: inline-block;
background: rgba(135, 206, 235, 0.2);
padding: 0.25rem 0.5rem;
border-radius: 4px;
margin-right: 0.5rem;
font-size: 0.8rem;
color: var(--sky);
}
.nav-links {
display: flex;
justify-content: center;
gap: 2rem;
margin-top: 3rem;
padding-top: 2rem;
border-top: 1px solid rgba(232, 228, 220, 0.2);
}
.nav-links a {
color: var(--sky);
text-decoration: none;
font-weight: 500;
transition: color 0.3s ease;
}
.nav-links a:hover {
color: var(--limestone);
}
</style>
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<div class="container">
<header>
<h1>Breath Exchange Calculator</h1>
<p class="subtitle">Where Rothrock's moss meets your running lungs</p>
</header>
<main>
<section class="calculator-card">
<form id="breathForm">
<div class="input-group">
<label for="weight">Body Mass (kg)</label>
<input type="number" id="weight" name="weight" min="30" max="200" step="0.1" placeholder="e.g., 68.5" required>
</div>
<div class="input-group">
<label for="intensity">Exercise Intensity (% VO₂ max)</label>
<select id="intensity" name="intensity" required>
<option value="" disabled selected>Select activity level</option>
<option value="40">Walking (40%)</option>
<option value="60">Brisk Hike (60%)</option>
<option value="80">Trail Run (80%)</option>
<option value="95">Sprint Interval (95%)</option>
</select>
</div>
<div class="input-group">
<label for="surface">Ground Surface Type</label>
<select id="surface" name="surface" required>
<option value="" disabled selected>Select terrain</option>
<option value="bare_soil">Bare mineral soil (low absorption)</option>
<option value="leaf_litter">Decomposing leaf litter (moderate absorption)</option>
<option value="moss_carpet">Active moss carpet (high absorption)</option>
<option value="lichen_crust">Biological soil crust (extreme absorption)</option>
</select>
</div>
<button type="submit" class="btn-calculate">Calculate Exchange Rate</button>
</form>
<div id="resultsPanel" class="results-panel">
<div class="result-row">
<span class="result-label">Minute Ventilation</span>
<span class="result-value" id="ventResult"></span>
</div>
<div class="result-row">
<span class="result-label">O₂ Uptake Rate</span>
<span class="result-value" id="o2Result"></span>
</div>
<div class="result-row">
<span class="result-label">CO₂ Output Rate</span>
<span class="result-value" id="co2Result"></span>
</div>
<div class="result-row">
<span class="result-label">Surface Absorption Coeff.</span>
<span class="result-value" id="absorbResult"></span>
</div>
<div class="result-row">
<span class="result-label">Net Atmospheric Shift</span>
<span class="result-value" id="shiftResult"></span>
</div>
</div>
</section>
<section class="methodology">
<h3>The Math Behind the Breath</h3>
<p>This calculator implements the <strong>Fick Principle</strong> adapted for terrestrial respiration during graded exercise. Baseline values derived from:</p>
<p style="margin-top: 1rem;">
<span class="citation">VO₂ max scaling</span>
Resting O₂ consumption = 3.5 mL·kg⁻¹·min⁻¹ (standard MET definition)<br>
Exercise multiplier follows exponential curve: VO₂ = 3.5 × (1 + 0.018 × intensity²)<br><br>
<span class="citation">Alveolar ventilation</span>
Minute ventilation = Tidal Volume × Respiratory Frequency<br>
TV scales from 0.5L (rest) to 2.5L (max effort)<br>
RF scales from 12 bpm (rest) to 45 bpm (VO₂ max)<br><br>
<span class="citation">Surface absorption coefficients</span>
Bare soil: 0.02 mg·cm⁻²·hr⁻¹<br>
Leaf litter: 0.15 mg·cm⁻²·hr⁻¹<br>
Moss carpet: 0.47 mg·cm⁻²·hr⁻¹ (Bryum argenteum field measurements)<br>
Biocrust: 0.89 mg·cm⁻²·hr⁻¹ (cyanobacterial fixation peak)
</p>
<p style="margin-top: 1rem; opacity: 0.8; font-style: italic;">
<strong>Note:</strong> Net atmospheric shift represents the theoretical ppm change in a 1-hectare enclosed ecosystem over one hour of sustained activity. Real-world dispersion reduces this by ~94%.
</p>
</section>
<nav class="nav-links">
<a href="/">Green Habitat Index</a>
<a href="/forest-dome.html">Forest Dome Protocols</a>
<a href="/guitar-green-pulse.html">Anxiety Rhythm Sync</a>
</nav>
</main>
</div>
<script>
document.getElementById('breathForm').addEventListener('submit', function(e) {
e.preventDefault();
const weight = parseFloat(document.getElementById('weight').value);
const intensity = parseInt(document.getElementById('intensity').value);
const surface = document.getElementById('surface').value;
// Validate inputs
if (!weight || !intensity || !surface) return;
// FICK PRINCIPLE CALCULATIONS
// Step 1: O₂ uptake scaling (exponential exercise model)
// VO₂ = 3.5 × (1 + 0.018 × intensity²) mL·kg⁻¹·min⁻¹
const vo2PerKg = 3.5 * (1 + 0.018 * Math.pow(intensity, 2));
const o2UptakeTotal = vo2PerKg * weight; // mL/min
// Step 2: Alveolar ventilation scaling
// Tidal volume: 0.5L → 2.5L (linear with intensity)
const tidalVolume = 0.5 + (2.0 * (intensity / 100)); // L
// Respiratory frequency: 12 → 45 bpm (square-root scaling)
const respFreq = 12 + (33 * Math.sqrt(intensity / 100)); // bpm
const minuteVentilation = tidalVolume * respFreq; // L/min
// Step 3: CO₂ production (RER = 0.85 average)
const co2Output = o2UptakeTotal * 0.85; // mL/min
// Step 4: Surface absorption coefficients (mg·cm⁻²·hr⁻¹ converted to mL·min⁻¹ equivalent)
const surfaceCoefficients = {
'bare_soil': 0.02,
'leaf_litter': 0.15,
'moss_carpet': 0.47,
'lichen_crust': 0.89
};
// Assume 10,000 cm² contact area (footprint × 4 strides/sec × 60 sec)
const contactArea = 10000; // cm²
const hourlyAbsorption = surfaceCoefficients[surface] * contactArea; // mg/hr
const minuteAbsorption = hourlyAbsorption / 60; // mg/min
// Convert mg CO₂ to mL (STP: 1 mg CO₂ ≈ 0.509 mL)
const absorbVolume = minuteAbsorption * 0.509; // mL/min
// Step 5: Net atmospheric shift (ppm in 1-hectare ecosystem)
// 1 hectare × 10m height = 1,000,000 m³ = 10¹² mL air
const ecosystemVolume = 1e12; // mL
const netFlux = co2Output - absorbVolume; // mL/min
const ppmShift = (netFlux / ecosystemVolume) * 1e6; // ppm/min
// DISPLAY RESULTS
document.getElementById('ventResult').textContent = minuteVentilation.toFixed(1) + ' L/min';
document.getElementById('o2Result').textContent = o2UptakeTotal.toFixed(1) + ' mL/min';
document.getElementById('co2Result').textContent = co2Output.toFixed(1) + ' mL/min';
document.getElementById('absorbResult').textContent = (surfaceCoefficients[surface]).toFixed(2) + ' mg·cm⁻²·hr⁻¹';
document.getElementById('shiftResult').textContent = ppmShift.toExponential(2) + ' ppm/min';
document.getElementById('resultsPanel').style.display = 'block';
});
</script>
</body>
</html>