omar1232's picture
Add 2 files
ebc11d7 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Audio Visualization Reacting to Beat</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://kit.fontawesome.com/a076d05399.js" crossorigin="anonymous"></script>
<style>
.visualizer-container {
position: relative;
width: 100%;
height: 300px;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
border-radius: 12px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.bar {
position: absolute;
bottom: 0;
width: 8px;
background: linear-gradient(to top, #00b4db, #0083b0);
border-radius: 4px 4px 0 0;
transition: height 0.05s ease-out;
box-shadow: 0 0 10px rgba(0, 180, 219, 0.7);
}
.beat-circle {
position: absolute;
border-radius: 50%;
background: radial-gradient(circle, rgba(255,105,180,0.8) 0%, rgba(255,20,147,0.5) 70%, transparent 100%);
transform: translate(-50%, -50%);
opacity: 0;
transition: opacity 0.3s, transform 0.3s;
}
.pulse {
animation: pulse 0.5s ease-out;
}
@keyframes pulse {
0% { transform: scale(1); opacity: 0.8; }
100% { transform: scale(1.5); opacity: 0; }
}
.audio-wave {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 100px;
background: linear-gradient(to top, rgba(0, 180, 219, 0.1), transparent);
clip-path: polygon(0% 100%, 100% 100%, 100% 0%, 0% 0%);
}
#audioPlayer {
display: none;
}
</style>
</head>
<body class="bg-gray-900 text-white min-h-screen flex flex-col items-center justify-center p-4">
<div class="max-w-4xl w-full">
<h1 class="text-4xl font-bold mb-2 text-center bg-gradient-to-r from-cyan-400 to-pink-500 bg-clip-text text-transparent">
Audio React Visualizer
</h1>
<p class="text-gray-400 text-center mb-8">Visualization reacts to both frequency and beat detection</p>
<div class="visualizer-container mb-8" id="visualizer">
<div class="audio-wave" id="audioWave"></div>
</div>
<div class="flex flex-col md:flex-row gap-4 items-center justify-center">
<div class="flex-1">
<input type="file" id="audioUpload" accept="audio/*" class="hidden" />
<label for="audioUpload" class="cursor-pointer bg-gradient-to-r from-cyan-500 to-blue-500 hover:from-cyan-600 hover:to-blue-600 text-white font-bold py-3 px-6 rounded-lg flex items-center justify-center transition-all duration-300 shadow-lg hover:shadow-xl">
<i class="fas fa-music mr-2"></i> Choose Audio File
</label>
</div>
<div class="flex-1">
<button id="playButton" class="bg-gradient-to-r from-pink-500 to-purple-500 hover:from-pink-600 hover:to-purple-600 text-white font-bold py-3 px-6 rounded-lg w-full flex items-center justify-center transition-all duration-300 shadow-lg hover:shadow-xl">
<i class="fas fa-play mr-2"></i> Play
</button>
</div>
</div>
<audio id="audioPlayer" controls></audio>
<div class="mt-8 grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="bg-gray-800 p-4 rounded-lg">
<h3 class="text-cyan-400 font-semibold mb-2"><i class="fas fa-sliders-h mr-2"></i>Controls</h3>
<div class="space-y-4">
<div>
<label class="block text-gray-400 mb-1">Sensitivity</label>
<input type="range" id="sensitivity" min="0.1" max="1" step="0.05" value="0.5" class="w-full accent-cyan-500">
</div>
<div>
<label class="block text-gray-400 mb-1">Bar Count</label>
<input type="range" id="barCount" min="20" max="200" step="10" value="80" class="w-full accent-pink-500">
</div>
</div>
</div>
<div class="bg-gray-800 p-4 rounded-lg">
<h3 class="text-purple-400 font-semibold mb-2"><i class="fas fa-chart-bar mr-2"></i>Audio Info</h3>
<div class="space-y-2">
<p>Status: <span id="status" class="text-pink-400">Waiting for audio</span></p>
<p>Beat: <span id="beatStatus" class="text-cyan-400">No beat detected</span></p>
<p>Volume: <span id="volumeLevel">0</span>%</p>
</div>
</div>
<div class="bg-gray-800 p-4 rounded-lg">
<h3 class="text-pink-400 font-semibold mb-2"><i class="fas fa-info-circle mr-2"></i>About</h3>
<p class="text-gray-400 text-sm">This visualization analyzes audio frequencies and detects beats in real-time. The bars represent different frequency ranges, while circles appear when beats are detected.</p>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// Audio elements
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
let audioSource = null;
let analyser = null;
let dataArray = null;
let isPlaying = false;
let animationId = null;
// Beat detection variables
let lastBeatTime = 0;
let beatThreshold = 1.5;
let beatHoldTime = 400; // ms
let beatDecayRate = 0.98;
let beatCutOff = 0;
// DOM elements
const visualizer = document.getElementById('visualizer');
const audioWave = document.getElementById('audioWave');
const playButton = document.getElementById('playButton');
const audioUpload = document.getElementById('audioUpload');
const audioPlayer = document.getElementById('audioPlayer');
const statusElement = document.getElementById('status');
const beatStatusElement = document.getElementById('beatStatus');
const volumeLevelElement = document.getElementById('volumeLevel');
const sensitivityInput = document.getElementById('sensitivity');
const barCountInput = document.getElementById('barCount');
let barCount = parseInt(barCountInput.value);
let bars = [];
let sensitivity = parseFloat(sensitivityInput.value);
// Create initial bars
function createBars() {
// Clear existing bars
visualizer.querySelectorAll('.bar').forEach(bar => bar.remove());
bars = [];
const containerWidth = visualizer.clientWidth;
const barWidth = containerWidth / barCount;
for (let i = 0; i < barCount; i++) {
const bar = document.createElement('div');
bar.className = 'bar';
bar.style.left = `${i * barWidth}px`;
bar.style.height = '0px';
visualizer.appendChild(bar);
bars.push(bar);
}
}
createBars();
// Handle file upload
audioUpload.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
const fileURL = URL.createObjectURL(file);
audioPlayer.src = fileURL;
statusElement.textContent = 'Ready to play';
statusElement.className = 'text-green-400';
// Reset visualization
if (isPlaying) {
stopAudio();
}
// Setup audio when ready
audioPlayer.oncanplaythrough = () => {
setupAudio();
};
});
// Play button click
playButton.addEventListener('click', () => {
if (!audioPlayer.src) {
statusElement.textContent = 'No audio selected';
statusElement.className = 'text-red-400';
return;
}
if (isPlaying) {
stopAudio();
playButton.innerHTML = '<i class="fas fa-play mr-2"></i> Play';
} else {
startAudio();
playButton.innerHTML = '<i class="fas fa-pause mr-2"></i> Pause';
}
});
// Setup audio context and analyzer
function setupAudio() {
if (audioSource) {
audioSource.disconnect();
}
analyser = audioContext.createAnalyser();
analyser.fftSize = 2048;
audioSource = audioContext.createMediaElementSource(audioPlayer);
audioSource.connect(analyser);
analyser.connect(audioContext.destination);
const bufferLength = analyser.frequencyBinCount;
dataArray = new Uint8Array(bufferLength);
}
// Start audio playback and visualization
function startAudio() {
if (audioContext.state === 'suspended') {
audioContext.resume();
}
audioPlayer.play();
isPlaying = true;
statusElement.textContent = 'Playing';
statusElement.className = 'text-green-400';
// Start visualization
visualize();
}
// Stop audio playback and visualization
function stopAudio() {
audioPlayer.pause();
isPlaying = false;
statusElement.textContent = 'Paused';
statusElement.className = 'text-yellow-400';
// Stop visualization
cancelAnimationFrame(animationId);
// Reset bars
bars.forEach(bar => {
bar.style.height = '0px';
});
}
// Beat detection function
function detectBeat(level) {
if (level > beatCutOff && level > beatThreshold) {
// Beat detected
beatCutOff = level * 1.1;
lastBeatTime = Date.now();
// Create beat circle
createBeatCircle();
return true;
} else {
// Decay beat cutoff
if (Date.now() - lastBeatTime > beatHoldTime) {
beatCutOff *= beatDecayRate;
beatCutOff = Math.max(beatCutOff, beatThreshold);
}
return false;
}
}
// Create a beat circle at random position
function createBeatCircle() {
const circle = document.createElement('div');
circle.className = 'beat-circle pulse';
// Random position
const x = Math.random() * visualizer.clientWidth;
const y = Math.random() * visualizer.clientHeight * 0.7;
const size = 30 + Math.random() * 70;
circle.style.left = `${x}px`;
circle.style.top = `${y}px`;
circle.style.width = `${size}px`;
circle.style.height = `${size}px`;
visualizer.appendChild(circle);
// Remove after animation
setTimeout(() => {
circle.remove();
}, 500);
}
// Main visualization function
function visualize() {
animationId = requestAnimationFrame(visualize);
analyser.getByteFrequencyData(dataArray);
// Calculate average volume
let sum = 0;
for (let i = 0; i < dataArray.length; i++) {
sum += dataArray[i];
}
const average = sum / dataArray.length;
const volumePercent = Math.min(Math.round((average / 255) * 100), 100);
volumeLevelElement.textContent = volumePercent;
// Check for beat
if (detectBeat(average * sensitivity)) {
beatStatusElement.textContent = 'Beat detected!';
beatStatusElement.className = 'text-pink-400 animate-pulse';
setTimeout(() => {
beatStatusElement.textContent = 'Listening...';
beatStatusElement.className = 'text-cyan-400';
}, 200);
}
// Update bars
const barGroupSize = Math.floor(dataArray.length / barCount);
for (let i = 0; i < barCount; i++) {
const start = i * barGroupSize;
let sum = 0;
for (let j = start; j < start + barGroupSize; j++) {
sum += dataArray[j];
}
const average = sum / barGroupSize;
const height = (average / 255) * visualizer.clientHeight * 1.2;
bars[i].style.height = `${height}px`;
bars[i].style.opacity = `${0.2 + (height / visualizer.clientHeight) * 0.8}`;
}
// Update audio wave
analyser.getByteTimeDomainData(dataArray);
let wavePath = 'path(\'M0 ' + (visualizer.clientHeight / 2) + ' ';
for (let i = 0; i < dataArray.length; i++) {
const x = (i / dataArray.length) * visualizer.clientWidth;
const y = (dataArray[i] / 255) * visualizer.clientHeight;
wavePath += 'L' + x + ' ' + y + ' ';
}
wavePath += 'L' + visualizer.clientWidth + ' ' + (visualizer.clientHeight / 2) + ' Z\')';
audioWave.style.clipPath = wavePath;
}
// Handle sensitivity change
sensitivityInput.addEventListener('input', () => {
sensitivity = parseFloat(sensitivityInput.value);
});
// Handle bar count change
barCountInput.addEventListener('input', () => {
barCount = parseInt(barCountInput.value);
createBars();
});
// Handle window resize
window.addEventListener('resize', () => {
createBars();
});
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=omar1232/audio-react-visualizer" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>