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