// Interactive PyroWave bitrate estimator for the docs. The formula mirrors the host's // `resolve_bitrate_kbps_for` (crates/punktfunk-host/src/native.rs): the Automatic pin is // ~1.6 bits/pixel for 4:2:0, ~2.6 bpp for 4:4:4, +15% for a 10-bit/HDR session, clamped to // [0.5 Mbps, 8 Gbps]. Self-contained (no design-system imports) so it renders even where the // full workspace UI packages are unavailable; themed via Fumadocs' `--color-fd-*` variables. import { useState } from 'react' type Preset = { label: string; w: number; h: number } const RES_PRESETS: Preset[] = [ { label: '1280 × 800 — Steam Deck', w: 1280, h: 800 }, { label: '1920 × 1080 — 1080p', w: 1920, h: 1080 }, { label: '2560 × 1440 — 1440p', w: 2560, h: 1440 }, { label: '3440 × 1440 — ultrawide', w: 3440, h: 1440 }, { label: '3840 × 2160 — 4K', w: 3840, h: 2160 }, { label: '5120 × 1440 — super-ultrawide', w: 5120, h: 1440 }, ] const FPS_PRESETS = [30, 60, 90, 120, 144, 240] // Practical payload ceilings (a bit under line rate — headers, FEC, framing). const LINKS = [ { label: 'Gigabit', mbps: 940 }, { label: '2.5 GbE', mbps: 2350 }, { label: '5 GbE', mbps: 4700 }, { label: '10 GbE', mbps: 9400 }, ] const MIN_MBPS = 0.5 const MAX_MBPS = 8000 function pyrowaveMbps( w: number, h: number, fps: number, chroma444: boolean, hdr: boolean, ): number { if (!(w > 0) || !(h > 0) || !(fps > 0)) return 0 const bppX10 = chroma444 ? 26 : 16 let kbps = (w * h * fps * bppX10) / 10 / 1000 if (hdr) kbps = (kbps * 115) / 100 kbps = Math.min(Math.max(kbps, MIN_MBPS * 1000), MAX_MBPS * 1000) return kbps / 1000 } const card: React.CSSProperties = { border: '1px solid var(--color-fd-border, #e5e7eb)', borderRadius: '0.75rem', background: 'var(--color-fd-card, transparent)', padding: '1.25rem', margin: '1.5rem 0', } const label: React.CSSProperties = { display: 'block', fontSize: '0.75rem', fontWeight: 600, letterSpacing: '0.02em', textTransform: 'uppercase', color: 'var(--color-fd-muted-foreground, #6b7280)', marginBottom: '0.35rem', } const field: React.CSSProperties = { width: '100%', padding: '0.45rem 0.6rem', borderRadius: '0.5rem', border: '1px solid var(--color-fd-border, #e5e7eb)', background: 'var(--color-fd-background, transparent)', color: 'var(--color-fd-foreground, inherit)', fontSize: '0.9rem', } function Toggle({ active, onClick, children, }: { active: boolean onClick: () => void children: React.ReactNode }) { return ( ) } export default function BitrateCalculator() { const [presetIdx, setPresetIdx] = useState(1) // 1080p const [custom, setCustom] = useState(false) const [cw, setCw] = useState(1920) const [ch, setCh] = useState(1080) const [fps, setFps] = useState(60) const [chroma444, setChroma444] = useState(false) const [hdr, setHdr] = useState(false) const preset = RES_PRESETS[presetIdx] ?? RES_PRESETS[1]! const w = custom ? cw : preset.w const h = custom ? ch : preset.h const mbps = pyrowaveMbps(w, h, fps, chroma444, hdr) const gbps = mbps / 1000 const bpp = w > 0 && h > 0 && fps > 0 ? (mbps * 1e6) / (w * h * fps) : 0 const frameKB = fps > 0 ? (mbps * 1e6) / 8 / fps / 1024 : 0 const needed = LINKS.find((l) => l.mbps >= mbps) const big = mbps >= 1000 ? `${gbps.toFixed(2)} Gbps` : `${Math.round(mbps)} Mbps` return (
Estimate of the Automatic bitrate a PyroWave session pins for this mode. Link bars use a
practical payload ceiling (below line rate). The pin is capped at 8 Gbps; on a constrained
link a host can cap it lower with PUNKTFUNK_PYROWAVE_MAX_MBPS.