degradation-comparison/render-farm-capacity-planner.html

138 lines
6.9 KiB
HTML
Raw Normal View History

2026-07-18 21:32:55 +02:00
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Capacity Planner | stream.4ort.net</title>
<style>
:root { --bg:#050505; --fg:#e0e0e0; --accent:#7aa2f7; --dim:#404040; --border:#1a1a1a; }
* { box-sizing:border-box; }
body { background:var(--bg); color:var(--fg); font-family:system-ui,sans-serif; margin:0; padding:3rem; line-height:1.6; }
.wrap { max-width:1100px; margin:0 auto; display:grid; grid-template-columns:1fr 400px; gap:3rem; }
h1 { font-size:2.2rem; letter-spacing:-0.03em; margin:0 0 1rem 0; }
h2 { font-size:0.75rem; opacity:0.5; font-family:monospace; margin:2rem 0 1rem 0; text-transform:uppercase; letter-spacing:0.12em; }
a { color:var(--accent); text-decoration:none; border-bottom:1px solid var(--border); }
.card { border:1px solid var(--border); padding:1.5rem; background:#080808; }
.input-group { margin-bottom:1rem; }
label { display:block; font-size:0.75rem; opacity:0.7; margin-bottom:0.3rem; font-family:monospace; }
input[type="number"] { width:100%; background:#0a0a0a; border:1px solid var(--dim); color:var(--fg); padding:0.6rem; font-family:monospace; font-size:0.9rem; }
input:focus { outline:none; border-color:var(--accent); }
.output { background:#0a0a0a; border:1px solid var(--accent); padding:1rem; font-family:monospace; font-size:0.85rem; white-space:pre-wrap; }
.metric { display:flex; justify-content:space-between; padding:0.4rem 0; border-bottom:1px dashed var(--dim); }
.metric:last-child { border-bottom:none; }
.metric-label { opacity:0.6; }
.metric-val { font-weight:bold; color:var(--accent); }
img { width:100%; height:auto; filter:contrast(1.1) brightness(0.9); }
.note { font-size:0.75rem; opacity:0.5; margin-top:1rem; font-style:italic; }
.formula { background:#0a0a0a; padding:1rem; font-family:monospace; font-size:0.8rem; border-left:3px solid var(--accent); margin:1.5rem 0; }
</style>
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
</head>
<body>
<div class="wrap">
<main>
<h1>Capacity Planner</h1>
<p>FIFA 2026 requires 16 venues, 104 matches, 60 days of continuous operation. This planner computes the GPU-hours required to render all broadcast assets before kickoff.</p>
<div class="formula">
CAPACITY = V × M × A × R ÷ S<br>
where:<br>
&nbsp;&nbsp;V = venues (16)<br>
&nbsp;&nbsp;M = matches per venue (avg 6.5)<br>
&nbsp;&nbsp;A = assets per match (3200 frames × 8 cameras = 25,600)<br>
&nbsp;&nbsp;R = resolution factor (8K = 33.2M px vs 1080p baseline = 2.1M → ×15.8)<br>
&nbsp;&nbsp;S = single-GPU render speed (frames/sec)
</div>
<h2>Grounded Data</h2>
<p>Source: <a href="https://4ort.xyz/entity/2026-fifa-world-cup" target="_blank">Wikidata Q5020214</a> — 2026 FIFA World Cup hosted across United States, Mexico, and Canada. Venue list includes Arrowhead Stadium, AT&T Stadium, BC Place, BMO Field, Estadio Akron, NRG Stadium, SoFi Stadium, Levi's Stadium, and 8 additional sites.</p>
<p>Pipeline precedent: <a href="render-farm-theory.html">Theory page</a> establishes throughput as T⁻¹. <a href="render-farm-calculator.html">Calculator</a> implements T = N × (F ÷ S) × 3600.</p>
<h2>Why This Matters</h2>
<p>Broadcast deadlines are absolute. A frame late means a blackout. The planner below takes venue count, match distribution, asset complexity, and hardware spec to output:</p>
<ul>
<li>Total GPU-hours required</li>
<li>Minimum cluster size for 60-day runway</li>
<li>Critical path bottleneck</li>
</ul>
<img src="https://images.pexels.com/photos/37730212/pexels-photo-37730212.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="Data center server racks with active equipment">
<p class="note">Image: Pexels (CC0). Server density represents minimum cluster footprint.</p>
</main>
<aside class="card">
<h2>Inputs</h2>
<div class="input-group">
<label>Venues</label>
<input type="number" id="venues" value="16" min="1">
</div>
<div class="input-group">
<label>Matches per venue (avg)</label>
<input type="number" id="matchesPerVenue" value="6.5" step="0.5">
</div>
<div class="input-group">
<label>Assets per match (frames × cameras)</label>
<input type="number" id="assetsPerMatch" value="25600">
</div>
<div class="input-group">
<label>Resolution multiplier (8K vs 1080p)</label>
<input type="number" id="resolutionMult" value="15.8" step="0.1">
</div>
<div class="input-group">
<label>GPU render speed (frames/sec)</label>
<input type="number" id="gpuSpeed" value="120" step="1">
</div>
<div class="input-group">
<label>Available hours (60 days)</label>
<input type="number" id="availableHours" value="1440">
</div>
<button onclick="compute()" style="width:100%; padding:0.8rem; background:var(--accent); color:#000; border:none; font-weight:bold; cursor:pointer; margin-top:1rem;">COMPUTE</button>
<div class="output" id="results" style="margin-top:1.5rem;"></div>
</aside>
</div>
<script>
function compute() {
const V = parseFloat(document.getElementById('venues').value) || 16;
const M = parseFloat(document.getElementById('matchesPerVenue').value) || 6.5;
const A = parseFloat(document.getElementById('assetsPerMatch').value) || 25600;
const R = parseFloat(document.getElementById('resolutionMult').value) || 15.8;
const S = parseFloat(document.getElementById('gpuSpeed').value) || 120;
const H = parseFloat(document.getElementById('availableHours').value) || 1440;
const totalFrames = V * M * A * R;
const gpuHoursRequired = totalFrames / (S * 3600);
const gpusRequired = Math.ceil(gpuHoursRequired / H);
const headroom = ((H * gpusRequired - gpuHoursRequired) / gpuHoursRequired * 100).toFixed(1);
const results = `CAPACITY PLAN — FIFA 2026
━━━━━━━━━━━━━━━━━━━━━━
Total Frames to Render: ${totalFrameString(totalFrames)}
GPU-Hours Required: ${(gpuHoursRequired/1e6).toFixed(2)}M
Minimum Cluster Size: ${gpusRequired} GPUs
Headroom at 60 Days: ${headroom}%
CRITICAL PATH:
• Asset generation: ${((V*M*A)/1e6).toFixed(2)}M base frames
• Resolution upscale: ×${R}
• Parallelization ceiling: ${gpusRequired} nodes`;
document.getElementById('results').textContent = results;
}
function totalFrameString(n) {
if (n >= 1e9) return (n/1e9).toFixed(2) + 'B';
if (n >= 1e6) return (n/1e6).toFixed(2) + 'M';
if (n >= 1e3) return (n/1e3).toFixed(2) + 'K';
return n.toString();
}
compute();
</script>
</body>
</html>