← back to Model Arena
data/artifacts/1fd68f0d37b7/kimi.html
314 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Microphone Audio Visualizer</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--bg-color: #0a0a0a;
--text-color: #ffffff;
--accent-color: #00ff88;
--bar-color: #00ccff;
--wave-color: #ffffff;
--error-bg: rgba(255, 50, 50, 0.1);
--error-border: rgba(255, 50, 50, 0.4);
}
body.light {
--bg-color: #f5f5f5;
--text-color: #1a1a1a;
--accent-color: #0066cc;
--bar-color: #6600cc;
--wave-color: #000000;
--error-bg: rgba(200, 0, 0, 0.1);
--error-border: rgba(200, 0, 0, 0.3);
}
body {
background: var(--bg-color);
color: var(--text-color);
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
overflow: hidden;
transition: background 0.3s ease, color 0.3s ease;
}
#container {
position: relative;
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
}
canvas {
flex: 1;
width: 100%;
height: 100%;
display: block;
}
#controls {
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 12px;
z-index: 10;
}
button {
padding: 12px 24px;
border: 2px solid var(--accent-color);
background: var(--bg-color);
color: var(--accent-color);
font-size: 14px;
font-weight: 600;
cursor: pointer;
border-radius: 30px;
transition: all 0.3s ease;
text-transform: uppercase;
letter-spacing: 1px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
button:hover {
background: var(--accent-color);
color: var(--bg-color);
transform: translateY(-2px);
box-shadow: 0 4px 16px rgba(0,0,0,0.2);
}
button:active {
transform: translateY(0);
}
#error {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--error-bg);
border: 1px solid var(--error-border);
color: var(--text-color);
padding: 24px;
border-radius: 12px;
text-align: center;
max-width: 400px;
width: 90%;
display: none;
font-size: 16px;
line-height: 1.5;
backdrop-filter: blur(10px);
}
#status {
position: absolute;
bottom: 30px;
left: 50%;
transform: translateX(-50%);
font-size: 13px;
opacity: 0.6;
pointer-events: none;
letter-spacing: 0.5px;
}
</style>
</head>
<body>
<div id="container">
<div id="controls">
<button id="startBtn">Start Microphone</button>
<button id="themeBtn">Toggle Theme</button>
</div>
<div id="error"></div>
<div id="status">Click 'Start Microphone' to begin visualization</div>
<canvas id="canvas"></canvas>
</div>
<script>
(function() {
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const startBtn = document.getElementById('startBtn');
const themeBtn = document.getElementById('themeBtn');
const errorDiv = document.getElementById('error');
const statusDiv = document.getElementById('status');
let audioContext;
let analyser;
let microphone;
let freqArray;
let timeArray;
let animationId;
let isRunning = false;
// Theme toggle
themeBtn.addEventListener('click', () => {
document.body.classList.toggle('light');
});
// Handle canvas resolution
function resize() {
const dpr = window.devicePixelRatio || 1;
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * dpr;
canvas.height = rect.height * dpr;
ctx.scale(dpr, dpr);
}
window.addEventListener('resize', resize);
resize();
async function initAudio() {
try {
audioContext = new (window.AudioContext || window.webkitAudioContext)();
const stream = await navigator.mediaDevices.getUserMedia({
audio: {
echoCancellation: false,
noiseSuppression: false,
autoGainControl: false
}
});
microphone = audioContext.createMediaStreamSource(stream);
analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
const bufferLength = analyser.frequencyBinCount;
// Frequency data uses half the FFT size
freqArray = new Uint8Array(bufferLength);
// Time domain data uses full FFT size
timeArray = new Uint8Array(analyser.fftSize);
microphone.connect(analyser);
isRunning = true;
startBtn.textContent = 'Stop Microphone';
statusDiv.textContent = 'Visualizing live audio...';
errorDiv.style.display = 'none';
draw();
} catch (err) {
console.error('Microphone access error:', err);
let message = 'Unable to access microphone. ';
if (err.name === 'NotAllowedError' || err.name === 'PermissionDeniedError') {
message += 'Permission was denied. Please allow microphone access in your browser settings and try again.';
} else if (err.name === 'NotFoundError') {
message += 'No microphone found. Please ensure your device has a microphone connected.';
} else {
message += 'Please check your browser permissions and ensure a microphone is available.';
}
errorDiv.textContent = message;
errorDiv.style.display = 'block';
statusDiv.textContent = 'Error accessing microphone';
}
}
function stopAudio() {
if (animationId) {
cancelAnimationFrame(animationId);
}
if (microphone && microphone.mediaStream) {
microphone.mediaStream.getTracks().forEach(track => track.stop());
}
if (audioContext && audioContext.state !== 'closed') {
audioContext.close();
}
isRunning = false;
startBtn.textContent = 'Start Microphone';
statusDiv.textContent = "Click 'Start Microphone' to begin visualization";
// Clear canvas
const width = canvas.clientWidth;
const height = canvas.clientHeight;
ctx.clearRect(0, 0, width, height);
}
startBtn.addEventListener('click', () => {
if (isRunning) {
stopAudio();
} else {
initAudio();
}
});
function draw() {
if (!isRunning) return;
animationId = requestAnimationFrame(draw);
const width = canvas.clientWidth;
const height = canvas.clientHeight;
analyser.getByteFrequencyData(freqArray);
analyser.getByteTimeDomainData(timeArray);
ctx.clearRect(0, 0, width, height);
const isLight = document.body.classList.contains('light');
const accent = isLight ? '#0066cc' : '#00ff88';
const barBase = isLight ? '102, 0, 204' : '0, 204, 255';
// Draw Frequency Bars (bottom)
const barWidth = (width / freqArray.length) * 2.5;
let x = 0;
for (let i = 0; i < freqArray.length; i++) {
const percent = freqArray[i] / 255;
const barHeight = percent * (height * 0.45);
const gradient = ctx.createLinearGradient(0, height, 0, height - barHeight);
gradient.addColorStop(0, `rgba(${barBase}, 0.4)`);
gradient.addColorStop(0.5, `rgba(${barBase}, 0.7)`);
gradient.addColorStop(1, accent);
ctx.fillStyle = gradient;
ctx.fillRect(x, height - barHeight, barWidth, barHeight);
x += barWidth + 1;
if (x > width) break;
}
// Draw Waveform (center)
ctx.lineWidth = 3;
ctx.strokeStyle = isLight ? '#000000' : '#ffffff';
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.beginPath();
const sliceWidth = width / timeArray.length;
x = 0;
for (let i = 0; i < timeArray.length; i++) {
const v = timeArray[i] / 128.0;
const y = (v * height * 0.4) + (height * 0.3);
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
x += sliceWidth;
}
ctx.stroke();
// Subtle center line
ctx.lineWidth = 1;
ctx.strokeStyle = isLight ? 'rgba(0,0,0,0.08)' : 'rgba(255,255,255,0.08)';
ctx.beginPath();
ctx.moveTo(0, height / 2);
ctx.lineTo(width, height / 2);
ctx.stroke();
}
})();
</script>
</body>
</html>