Audio Card Component
Home
Snippets
Audio Card Component
HTML
CSS
JS
<div class="audio-card" id="card-element"> <div class="wave-container"> <canvas id="wave-canvas"></canvas> </div> <div class="meta-row"> <div class="title-group"> <span class="track-title">Dynamic Synthesizer</span> <span class="track-sub">Channel Matrix // Core_01</span> </div> <div class="status-badge" id="live-indicator">LIVE</div> </div> <div class="controls-row"> <span class="time-code" id="timer-string">00:42.12</span> <button class="btn-toggle"> <svg width="12" height="14" viewBox="0 0 12 14" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M1.5 1.74167V12.2583C1.5 12.8687 2.18958 13.2241 2.6875 12.8727L10.1375 7.61434C10.5646 7.31268 10.5646 6.68732 10.1375 6.38566L2.6875 1.12731C2.18958 0.775871 1.5 1.13132 1.5 1.74167Z" fill="black"/> </svg> </button> </div> </div>
:root { --bg: #050508; --surface: #0e0f19; --border: rgba(255, 255, 255, 0.05); --accent: #ff9500; --text: #f8fafc; } body { margin: 0; background-color: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; height: 100vh; display: flex; justify-content: center; align-items: center; overflow: hidden; user-select: none; -webkit-user-select: none; padding: 20px; box-sizing: border-box; } .audio-card { position: relative; width: 100%; max-width: 340px; background: var(--surface); border: 1px solid var(--border); border-radius: 28px; padding: 28px; box-sizing: border-box; box-shadow: 0 40px 80px rgba(0, 0, 0, 0.7); } .wave-container { position: relative; width: 100%; height: 110px; background: rgba(0, 0, 0, 0.2); border-radius: 16px; margin-bottom: 24px; overflow: hidden; border: 1px solid rgba(255, 255, 255, 0.02); } canvas { display: block; width: 100%; height: 100%; } .meta-row { display: flex; justify-content: space-between; align-items: flex-start; } .title-group { display: flex; flex-direction: column; gap: 4px; } .track-title { font-size: 18px; font-weight: 700; letter-spacing: -0.5px; } .track-sub { font-size: 12px; color: #64748b; font-weight: 500; } .status-badge { font-size: 10px; font-weight: 700; color: var(--accent); background: rgba(255, 149, 0, 0.1); border: 1px solid rgba(255, 149, 0, 0.2); padding: 4px 10px; border-radius: 100px; letter-spacing: 1px; text-transform: uppercase; margin-top: 2px; } .controls-row { margin-top: 28px; display: flex; justify-content: space-between; align-items: center; border-top: 1px solid rgba(255, 255, 255, 0.04); padding-top: 20px; } .time-code { font-family: monospace; font-size: 13px; color: #475569; } .btn-toggle { background: #ffffff; border: none; width: 36px; height: 36px; border-radius: 50%; display: flex; justify-content: center; align-items: center; cursor: pointer; transition: transform 0.2s; } .btn-toggle:active { transform: scale(0.9); }
const card = document.getElementById('card-element'); const canvas = document.getElementById('wave-canvas'); const ctx = canvas.getContext('2d'); const timer = document.getElementById('timer-string'); let bars = []; const barCount = 28; let pointer = { x: null, active: false }; let globalTime = 0; function resizeCanvas() { const rect = canvas.parentNode.getBoundingClientRect(); canvas.width = rect.width; canvas.height = rect.height; initBars(); } function initBars() { bars = []; const spacing = canvas.width / barCount; for (let i = 0; i < barCount; i++) { bars.push({ x: i * spacing + (spacing / 2), height: 20, targetHeight: 20, basePhase: i * 0.2 }); } } function drawWaveform() { ctx.clearRect(0, 0, canvas.width, canvas.height); globalTime += 0.05; const spacing = canvas.width / barCount; const barWidth = spacing * 0.65; bars.forEach((b, idx) => { let ambientMotion = Math.sin(globalTime * 1.5 + b.basePhase) * 22; b.targetHeight = 35 + ambientMotion; if (pointer.active && pointer.x !== null) { let distanceX = Math.abs(pointer.x - b.x); if (distanceX < 60) { let force = (60 - distanceX) / 60; b.targetHeight += force * 45; } } b.height += (b.targetHeight - b.height) * 0.15; if (b.height < 6) b.height = 6; if (b.height > canvas.height - 10) b.height = canvas.height - 10; let topY = (canvas.height / 2) - (b.height / 2); let hueRatio = idx / barCount; ctx.fillStyle = `hsla(${24 + hueRatio * 20}, 100%, 55%, ${0.25 + (b.height / canvas.height) * 0.75})`; ctx.beginPath(); ctx.roundRect(b.x - barWidth / 2, topY, barWidth, b.height, 100); ctx.fill(); }); if (Math.random() > 0.4) { let s = Math.floor(globalTime % 60).toString().padStart(2, '0'); let ms = Math.floor((globalTime * 100) % 99).toString().padStart(2, '0'); timer.textContent = `00:${s}.${ms}`; } requestAnimationFrame(drawWaveform); } function trackPointer(clientX) { const rect = canvas.getBoundingClientRect(); pointer.x = clientX - rect.left; } canvas.parentNode.addEventListener('mouseenter', () => pointer.active = true); canvas.parentNode.addEventListener('mousemove', (e) => trackPointer(e.clientX)); canvas.parentNode.addEventListener('mouseleave', () => { pointer.active = false; pointer.x = null; }); canvas.parentNode.addEventListener('touchstart', (e) => { pointer.active = true; trackPointer(e.touches[0].clientX); }, { passive: true }); canvas.parentNode.addEventListener('touchmove', (e) => { trackPointer(e.touches[0].clientX); }, { passive: true }); canvas.parentNode.addEventListener('touchend', () => { pointer.active = false; pointer.x = null; }); window.addEventListener('resize', resizeCanvas); resizeCanvas(); drawWaveform();
Ad #1
Ad #2
Scroll to Top