interface Props { data: number[]; // envelope values, typically in [0, 1] height?: number; color?: "cyber" | "danger"; } /** Static envelope-style waveform on SVG. Lightweight; no Web Audio decoding. */ export default function WaveformViewer({ data, height = 96, color = "cyber", }: Props) { if (!data.length) { return (
waiting for audio…
); } const stroke = color === "danger" ? "rgb(var(--danger))" : "rgb(var(--cyber))"; const fill = color === "danger" ? "rgb(var(--danger) / 0.13)" : "rgb(var(--cyber) / 0.13)"; const W = 1000; const H = height; const max = Math.max(...data, 0.001); const stepX = W / Math.max(data.length - 1, 1); // Build a mirrored waveform path for the classic oscilloscope look. let topPath = `M 0 ${H / 2}`; let botPath = `M ${W} ${H / 2}`; data.forEach((v, i) => { const norm = v / max; const x = i * stepX; const yT = H / 2 - (norm * H * 0.45); topPath += ` L ${x.toFixed(2)} ${yT.toFixed(2)}`; }); for (let i = data.length - 1; i >= 0; i -= 1) { const norm = data[i] / max; const x = i * stepX; const yB = H / 2 + (norm * H * 0.45); botPath += ` L ${x.toFixed(2)} ${yB.toFixed(2)}`; } const closed = `${topPath} L ${W} ${H / 2} ${botPath} Z`; return (
{/* center line */}
); }