← back to Model Arena
data/artifacts/1fd68f0d37b7/claude-code.html
330 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Mic Audio Visualizer</title>
<style>
:root {
--bg: #0b0e14;
--panel: #131826;
--text: #e8ecf4;
--muted: #8a93a6;
--accent: #4dd0e1;
--accent2: #b388ff;
--border: rgba(255,255,255,0.08);
}
[data-theme="sunset"] {
--bg: #1a0f14;
--panel: #261520;
--text: #ffeede;
--muted: #b08a7a;
--accent: #ff8a50;
--accent2: #ff4081;
--border: rgba(255,200,150,0.12);
}
[data-theme="matrix"] {
--bg: #050b05;
--panel: #0a140a;
--text: #d8ffd8;
--muted: #5a8a5a;
--accent: #00e676;
--accent2: #76ff03;
--border: rgba(0,255,100,0.12);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { height: 100%; }
body {
background: var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 16px;
transition: background .4s ease, color .4s ease;
}
.app {
width: 100%;
max-width: 900px;
display: flex;
flex-direction: column;
gap: 12px;
}
header {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
flex-wrap: wrap;
}
h1 {
font-size: 18px;
font-weight: 600;
letter-spacing: .04em;
}
h1 .dot {
display: inline-block;
width: 10px; height: 10px;
border-radius: 50%;
background: var(--muted);
margin-right: 8px;
vertical-align: baseline;
transition: background .3s;
}
h1 .dot.live { background: var(--accent); box-shadow: 0 0 10px var(--accent); }
.controls { display: flex; gap: 8px; }
button {
background: var(--panel);
color: var(--text);
border: 1px solid var(--border);
border-radius: 8px;
padding: 8px 14px;
font-size: 13px;
cursor: pointer;
transition: border-color .2s, transform .1s;
}
button:hover { border-color: var(--accent); }
button:active { transform: scale(.97); }
button.primary {
background: linear-gradient(135deg, var(--accent), var(--accent2));
color: #fff;
border: none;
font-weight: 600;
}
.stage {
position: relative;
background: var(--panel);
border: 1px solid var(--border);
border-radius: 14px;
overflow: hidden;
transition: background .4s ease;
}
canvas { display: block; width: 100%; }
#bars { height: 260px; }
#wave { height: 140px; border-top: 1px solid var(--border); }
.overlay {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 14px;
text-align: center;
padding: 24px;
background: color-mix(in srgb, var(--bg) 65%, transparent);
backdrop-filter: blur(3px);
}
.overlay.hidden { display: none; }
.overlay .icon { font-size: 42px; }
.overlay p { color: var(--muted); font-size: 14px; max-width: 420px; line-height: 1.5; }
.overlay .err-title { color: var(--text); font-size: 16px; font-weight: 600; }
footer { color: var(--muted); font-size: 12px; text-align: center; }
</style>
</head>
<body data-theme="ocean">
<div class="app">
<header>
<h1><span class="dot" id="liveDot"></span>Mic Visualizer</h1>
<div class="controls">
<button id="themeBtn" title="Cycle color theme">🎨 Theme: Ocean</button>
<button id="startBtn" class="primary">🎤 Start</button>
</div>
</header>
<div class="stage">
<canvas id="bars"></canvas>
<canvas id="wave"></canvas>
<div class="overlay" id="overlay">
<div class="icon">🎤</div>
<p id="overlayMsg">Click <strong>Start</strong> and allow microphone access to see live frequency bars and a waveform of your audio.</p>
</div>
</div>
<footer>WebAudio AnalyserNode · frequency bars + time-domain waveform · no data leaves your device</footer>
</div>
<script>
(function () {
var themes = ["ocean", "sunset", "matrix"];
var themeNames = { ocean: "Ocean", sunset: "Sunset", matrix: "Matrix" };
var themeIdx = 0;
var barsCanvas = document.getElementById("bars");
var waveCanvas = document.getElementById("wave");
var barsCtx = barsCanvas.getContext("2d");
var waveCtx = waveCanvas.getContext("2d");
var overlay = document.getElementById("overlay");
var overlayMsg = document.getElementById("overlayMsg");
var startBtn = document.getElementById("startBtn");
var themeBtn = document.getElementById("themeBtn");
var liveDot = document.getElementById("liveDot");
var audioCtx = null, analyser = null, freqData = null, timeData = null;
var running = false, rafId = null;
function sizeCanvases() {
var dpr = window.devicePixelRatio || 1;
[barsCanvas, waveCanvas].forEach(function (c) {
var rect = c.getBoundingClientRect();
c.width = Math.max(1, Math.round(rect.width * dpr));
c.height = Math.max(1, Math.round(rect.height * dpr));
});
}
window.addEventListener("resize", sizeCanvases);
sizeCanvases();
function cssVar(name) {
return getComputedStyle(document.body).getPropertyValue(name).trim();
}
themeBtn.addEventListener("click", function () {
themeIdx = (themeIdx + 1) % themes.length;
var t = themes[themeIdx];
document.body.setAttribute("data-theme", t);
themeBtn.textContent = "🎨 Theme: " + themeNames[t];
if (!running) drawIdle();
});
function showError(title, msg) {
overlay.classList.remove("hidden");
overlay.innerHTML =
'<div class="icon">🚫</div>' +
'<p class="err-title">' + title + "</p>" +
"<p>" + msg + "</p>";
}
startBtn.addEventListener("click", function () {
if (running) return;
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
showError("Microphone not available",
"This browser (or this embedded frame) doesn't expose getUserMedia. Try opening the page directly over HTTPS or localhost.");
return;
}
startBtn.disabled = true;
startBtn.textContent = "⏳ Requesting…";
navigator.mediaDevices.getUserMedia({ audio: true }).then(function (stream) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
var source = audioCtx.createMediaStreamSource(stream);
analyser = audioCtx.createAnalyser();
analyser.fftSize = 2048;
analyser.smoothingTimeConstant = 0.8;
source.connect(analyser);
freqData = new Uint8Array(analyser.frequencyBinCount);
timeData = new Uint8Array(analyser.fftSize);
running = true;
overlay.classList.add("hidden");
liveDot.classList.add("live");
startBtn.textContent = "🎧 Listening";
if (audioCtx.state === "suspended") audioCtx.resume();
loop();
}).catch(function (err) {
startBtn.disabled = false;
startBtn.textContent = "🎤 Start";
if (err && (err.name === "NotAllowedError" || err.name === "PermissionDeniedError" || err.name === "SecurityError")) {
showError("Microphone access denied",
"No worries — nothing is recorded either way. To use the visualizer, allow microphone access in your browser's site settings (or the frame's permission prompt) and click Start again.");
} else if (err && err.name === "NotFoundError") {
showError("No microphone found",
"We couldn't find an audio input device. Plug in or enable a microphone and try again.");
} else {
showError("Couldn't start the microphone",
(err && err.message) ? err.message : "An unexpected error occurred while opening the audio input.");
}
});
});
function drawIdle() {
var w = barsCanvas.width, h = barsCanvas.height;
barsCtx.clearRect(0, 0, w, h);
waveCtx.clearRect(0, 0, waveCanvas.width, waveCanvas.height);
}
function loop() {
if (!running) return;
rafId = requestAnimationFrame(loop);
analyser.getByteFrequencyData(freqData);
analyser.getByteTimeDomainData(timeData);
drawBars();
drawWave();
}
function drawBars() {
var w = barsCanvas.width, h = barsCanvas.height;
barsCtx.clearRect(0, 0, w, h);
var accent = cssVar("--accent") || "#4dd0e1";
var accent2 = cssVar("--accent2") || "#b388ff";
var grad = barsCtx.createLinearGradient(0, h, 0, 0);
grad.addColorStop(0, accent);
grad.addColorStop(1, accent2);
var barCount = 64;
// use lower ~2/3 of the spectrum where voice/music energy lives
var usable = Math.floor(freqData.length * 0.66);
var step = usable / barCount;
var gap = Math.max(1, w * 0.004);
var barW = (w - gap * (barCount + 1)) / barCount;
barsCtx.fillStyle = grad;
for (var i = 0; i < barCount; i++) {
var start = Math.floor(i * step), end = Math.floor((i + 1) * step), sum = 0;
for (var j = start; j < end; j++) sum += freqData[j];
var v = sum / Math.max(1, end - start) / 255;
var barH = Math.max(2, v * (h - 12));
var x = gap + i * (barW + gap);
var y = h - barH;
var r = Math.min(barW / 2, 6);
barsCtx.beginPath();
barsCtx.moveTo(x, h);
barsCtx.lineTo(x, y + r);
barsCtx.arcTo(x, y, x + r, y, r);
barsCtx.lineTo(x + barW - r, y);
barsCtx.arcTo(x + barW, y, x + barW, y + r, r);
barsCtx.lineTo(x + barW, h);
barsCtx.closePath();
barsCtx.fill();
}
}
function drawWave() {
var w = waveCanvas.width, h = waveCanvas.height;
waveCtx.clearRect(0, 0, w, h);
var accent = cssVar("--accent") || "#4dd0e1";
// center reference line
waveCtx.strokeStyle = "rgba(255,255,255,0.08)";
waveCtx.lineWidth = 1;
waveCtx.beginPath();
waveCtx.moveTo(0, h / 2);
waveCtx.lineTo(w, h / 2);
waveCtx.stroke();
waveCtx.strokeStyle = accent;
waveCtx.lineWidth = Math.max(1.5, (window.devicePixelRatio || 1) * 1.5);
waveCtx.lineJoin = "round";
waveCtx.beginPath();
var slice = w / timeData.length;
for (var i = 0; i < timeData.length; i++) {
var v = timeData[i] / 128.0; // 0..2, 1 = silence
var y = (v * h) / 2;
var x = i * slice;
if (i === 0) waveCtx.moveTo(x, y);
else waveCtx.lineTo(x, y);
}
waveCtx.stroke();
// soft glow pass
waveCtx.globalAlpha = 0.25;
waveCtx.lineWidth *= 3;
waveCtx.stroke();
waveCtx.globalAlpha = 1;
}
drawIdle();
})();
</script>
</body>
</html>