breath-observatory/breath-calculator.html
2026-07-18 13:44:53 +00:00

353 lines
14 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>