| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Sound Visualizer</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: 400px; |
| overflow: hidden; |
| background: linear-gradient(to bottom, #0f172a, #1e293b); |
| border-radius: 12px; |
| justify-content: center; |
| } |
| |
| .bar { |
| width: 2%; |
| height: 100%; |
| display: inline-block; |
| margin: 0 1%; |
| background: linear-gradient(to top, #3b82f6, #9333ea); |
| transform-origin: bottom; |
| border-radius: 5px; |
| transition: transform 0.1s ease-out; |
| } |
| |
| .circle { |
| position: absolute; |
| border-radius: 50%; |
| background: radial-gradient(circle, rgba(59,130,246,0.8) 0%, rgba(147,51,234,0.4) 70%); |
| transform-origin: center; |
| transition: all 0.2s ease-out; |
| } |
| |
| .wave { |
| position: absolute; |
| top: 40%; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| fill: none; |
| stroke: url(#waveGradient); |
| stroke-width: 15px; |
| } |
| |
| .particle { |
| position: absolute; |
| border-radius: 50%; |
| background: linear-gradient(45deg, #3b82f6, #9333ea); |
| transform-origin: center; |
| transition: all 0.3s ease-out; |
| } |
| |
| .spectrum-line { |
| position: absolute; |
| bottom: 0; |
| width: 2px; |
| background: linear-gradient(to top, #3b82f6, #9333ea); |
| transform-origin: bottom; |
| transition: height 0.1s ease-out; |
| } |
| |
| .radial-bar { |
| position: absolute; |
| width: 15px; |
| background: linear-gradient(to top, #3b82f6, #9333ea); |
| transform-origin: bottom; |
| border-radius: 2px; |
| transition: height 0.1s ease-out; |
| } |
| |
| .rounded-wave-container { |
| position: relative; |
| width: 100%; |
| max-width: 400px; |
| height: 400px; |
| margin: auto; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| overflow: hidden; |
| } |
| |
| .rounded-wave { |
| position: absolute; |
| border-radius: 50%; |
| border: 2px solid rgba(59, 130, 246, 0.4); |
| opacity: 0.7; |
| animation: pulse 3s infinite; |
| } |
| |
| .wave-1 { |
| width: 180px; |
| height: 180px; |
| z-index: 1; |
| } |
| |
| .wave-2 { |
| width: 240px; |
| height: 240px; |
| z-index: 0; |
| animation-delay: 0.5s; |
| } |
| |
| .wave-3 { |
| width: 300px; |
| height: 300px; |
| z-index: -1; |
| animation-delay: 1s; |
| } |
| |
| .center-image { |
| position: relative; |
| width: 120px; |
| height: 120px; |
| border-radius: 50%; |
| background-size: cover; |
| background-position: center; |
| z-index: 10; |
| box-shadow: 0 0 30px rgba(59, 130, 246, 0.7); |
| border: 2px solid rgba(59, 130, 246, 0.5); |
| } |
| |
| @keyframes pulse { |
| 0% { |
| transform: scale(1); |
| opacity: 0.7; |
| } |
| 50% { |
| transform: scale(1.1); |
| opacity: 0.3; |
| } |
| 100% { |
| transform: scale(1); |
| opacity: 0.7; |
| } |
| } |
| |
| .fade-in { |
| animation: fadeIn 0.5s ease-in; |
| } |
| |
| @keyframes fadeIn { |
| from { opacity: 0; } |
| to { opacity: 1; } |
| } |
| |
| .gradient-bg { |
| background: linear-gradient(135deg, #1e293b, #0f172a); |
| } |
| |
| .glow { |
| box-shadow: 0 0 15px rgba(59, 130, 246, 0.5); |
| } |
| |
| .visualizer-style-btn.active { |
| transform: scale(1.05); |
| box-shadow: 0 0 10px rgba(59, 130, 246, 0.5); |
| } |
| |
| .bidirectional-wave { |
| position: absolute; |
| width: 100%; |
| height: 100%; |
| top: 0; |
| left: 0; |
| } |
| |
| .bidirectional-wave path { |
| fill: url(#bidirectionalWaveGradient); |
| opacity: 0.8; |
| transition: all 0.1s ease-out; |
| } |
| |
| .rounded-wave path { |
| fill: url(#roundedWaveGradient); |
| opacity: 0.8; |
| transition: all 0.1s ease-out; |
| } |
| |
| .image-upload-container { |
| display: none; |
| position: absolute; |
| top: 50%; |
| left: 50%; |
| transform: translate(-50%, -50%); |
| background: rgba(30, 41, 59, 0.95); |
| padding: 20px; |
| border-radius: 10px; |
| z-index: 100; |
| box-shadow: 0 0 30px rgba(0, 0, 0, 0.7); |
| border: 1px solid rgba(59, 130, 246, 0.3); |
| width: 90%; |
| max-width: 400px; |
| } |
| |
| .image-upload-container.active { |
| display: block; |
| animation: fadeIn 0.3s ease-out; |
| } |
| |
| .slider-container { |
| width: 100%; |
| max-width: 300px; |
| } |
| |
| .slider-label { |
| display: flex; |
| justify-content: space-between; |
| margin-bottom: 0.5rem; |
| font-size: 0.875rem; |
| color: #94a3b8; |
| } |
| |
| .slider-value { |
| color: #e2e8f0; |
| font-weight: 500; |
| } |
| |
| input[type="range"] { |
| -webkit-appearance: none; |
| width: 100%; |
| height: 8px; |
| border-radius: 4px; |
| background: #334155; |
| outline: none; |
| } |
| |
| input[type="range"]::-webkit-slider-thumb { |
| -webkit-appearance: none; |
| appearance: none; |
| width: 18px; |
| height: 18px; |
| border-radius: 50%; |
| background: #3b82f6; |
| cursor: pointer; |
| transition: all 0.2s; |
| } |
| |
| input[type="range"]::-webkit-slider-thumb:hover { |
| background: #2563eb; |
| transform: scale(1.1); |
| } |
| |
| .upload-btn { |
| position: absolute; |
| bottom: 50px; |
| right: 50px; |
| background: rgba(59, 130, 246, 0.9); |
| color: white; |
| border: none; |
| border-radius: 50%; |
| width: 10px; |
| height: 10px; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| cursor: pointer; |
| box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2); |
| transition: all 0.3s ease; |
| z-index: 20; |
| } |
| |
| .upload-btn:hover { |
| background: rgba(37, 99, 235, 0.9); |
| transform: scale(1.1); |
| } |
| |
| .upload-btn i { |
| font-size: 1.2rem; |
| } |
| </style> |
| </head> |
| <body class="gradient-bg text-white min-h-screen"> |
| <div class="container mx-auto px-4 py-8"> |
| <header class="text-center mb-8 fade-in"> |
| <h1 class="text-5xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-purple-600 mb-2"> |
| Sound Visualizer |
| </h1> |
| <p class="text-gray-400 text-lg">Transform your voice into stunning visual art</p> |
| </header> |
| |
| <div class="bg-gray-800 rounded-xl p-6 shadow-xl mb-8 glow"> |
| <div class="flex flex-wrap justify-between items-center mb-6"> |
| <div class="mb-4 md:mb-0"> |
| <button id="startBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-full font-medium transition-all transform hover:scale-105 active:scale-95"> |
| <i class="fas fa-microphone mr-2"></i> Start Microphone |
| </button> |
| <button id="stopBtn" disabled class="bg-gray-600 hover:bg-gray-700 text-white px-6 py-3 rounded-full font-medium transition-all transform hover:scale-105 active:scale-95 ml-2"> |
| <i class="fas fa-stop mr-2"></i> Stop |
| </button> |
| </div> |
| |
| <div class="flex items-center"> |
| <div id="status" class="text-gray-400 flex items-center mr-4"> |
| <i class="fas fa-info-circle mr-2"></i> Mic inactive ❗ |
| </div> |
| </div> |
| </div> |
| |
| <div class="visualizer-container" id="visualizer"> |
| |
| <div id="barsContainer" class="h-full flex items-end justify-center"></div> |
| |
| |
| <div id="circlesContainer" class="h-full w-full relative" style="display: none;"></div> |
| |
| |
| <svg id="waveContainer" class="wave" viewBox="0 0 1000 200" preserveAspectRatio="none" style="display: none;"> |
| <defs> |
| <linearGradient id="waveGradient" x1="0%" y1="0%" x2="100%" y2="0%"> |
| <stop offset="0%" stop-color="#3b82f6" /> |
| <stop offset="100%" stop-color="#9333ea" /> |
| </linearGradient> |
| </defs> |
| <path id="wavePath" d=""></path> |
| </svg> |
| |
| |
| <svg id="bidirectionalWaveContainer" class="bidirectional-wave" viewBox="0 0 1000 200" preserveAspectRatio="none" style="display: none;"> |
| <defs> |
| <linearGradient id="bidirectionalWaveGradient" x1="0%" y1="0%" x2="100%" y2="0%"> |
| <stop offset="0%" stop-color="#3b82f6" /> |
| <stop offset="100%" stop-color="#9333ea" /> |
| </linearGradient> |
| </defs> |
| <path id="bidirectionalWavePathTop" d=""></path> |
| <path id="bidirectionalWavePathBottom" d=""></path> |
| </svg> |
| |
| |
| <div id="roundedWaveContainer" class="rounded-wave-container" style="display: none;"> |
| <div id="centerImage" class="center-image pulse"></div> |
| <svg id="roundedWave" class="rounded-wave" viewBox="0 0 1000 1000" preserveAspectRatio="none"> |
| <defs> |
| <radialGradient id="roundedWaveGradient" cx="50%" cy="50%" r="50%" fx="50%" fy="50%"> |
| <stop offset="0%" stop-color="#3b82f6" /> |
| <stop offset="100%" stop-color="#9333ea" /> |
| </radialGradient> |
| </defs> |
| <path id="roundedWavePath" d=""></path> |
| </svg> |
| <button id="changeImageBtn" class="upload-btn"> |
| <i class="fas fa-camera"></i> |
| </button> |
| </div> |
| |
| |
| <div id="imageUploadContainer" class="image-upload-container"> |
| <h3 class="text-xl font-semibold mb-4 text-center">Change Center Image</h3> |
| <div class="mb-4"> |
| <input type="file" id="imageUpload" accept="image/*" class="hidden"> |
| <label for="imageUpload" class="block w-full bg-blue-600 hover:bg-blue-700 text-white px-4 py-3 rounded-lg cursor-pointer text-center transition-all mb-3"> |
| <i class="fas fa-upload mr-2"></i> Upload Image |
| </label> |
| <p class="text-gray-400 text-sm mb-2 text-center">Or use a URL:</p> |
| <input type="text" id="imageUrl" placeholder="Enter image URL" class="w-full px-4 py-2 bg-gray-700 rounded-lg text-white border border-gray-600 focus:border-blue-500 focus:outline-none"> |
| </div> |
| <div class="flex justify-between"> |
| <button id="cancelImageBtn" class="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-lg transition-all flex-1 mr-2"> |
| Cancel |
| </button> |
| <button id="applyImageBtn" class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition-all flex-1 ml-2"> |
| Apply |
| </button> |
| </div> |
| </div> |
| |
| |
| <div id="particlesContainer" class="h-full w-full relative" style="display: none;"></div> |
| |
| |
| <div id="spectrumContainer" class="h-full w-full relative" style="display: none;"></div> |
| |
| |
| <div id="radialContainer" class="h-full w-full relative" style="display: none;"></div> |
| </div> |
| <br/> |
| <div class="mt-6 flex space-x-4 w-full"> |
| |
| <div class="w-[30%]"> |
| <div class="flex justify-between text-sm mb-1"> |
| <span>Sensitivity:</span> |
| <span id="sensitivityValue">100%</span> |
| </div> |
| <input type="range" id="sensitivity" min="10" max="300" value="100" class="w-full"> |
| </div> |
|
|
| |
| <div class="w-[20%]"> |
| <div class="flex justify-between text-sm mb-1"> |
| <span>Smoothness:</span> |
| <span id="smoothnessValue">5</span> |
| </div> |
| <input type="range" id="smoothness" min="1" max="30" value="5" class="w-full"> |
| </div> |
| </div> |
| <div class="mt-6"> |
| <span class="text-gray-400 mr-3">Visual Style:</span> |
| <div class="flex flex-wrap gap-2 mt-2"> |
| <button data-style="bars" class="visualizer-style-btn bg-blue-600 text-white px-4 py-2 rounded-full text-sm transition-all active">Bars</button> |
| <button data-style="circles" class="visualizer-style-btn bg-gray-700 hover:bg-gray-600 text-white px-4 py-2 rounded-full text-sm transition-all">Circles</button> |
| <button data-style="wave" class="visualizer-style-btn bg-gray-700 hover:bg-gray-600 text-white px-4 py-2 rounded-full text-sm transition-all">Wave</button> |
| <button data-style="bidirectional" class="visualizer-style-btn bg-gray-700 hover:bg-gray-600 text-white px-4 py-2 rounded-full text-sm transition-all">Bidirectional</button> |
| <button data-style="rounded" class="visualizer-style-btn bg-gray-700 hover:bg-gray-600 text-white px-4 py-2 rounded-full text-sm transition-all">Rounded</button> |
| <button data-style="particles" class="visualizer-style-btn bg-gray-700 hover:bg-gray-600 text-white px-4 py-2 rounded-full text-sm transition-all">Particles</button> |
| <button data-style="spectrum" class="visualizer-style-btn bg-gray-700 hover:bg-gray-600 text-white px-4 py-2 rounded-full text-sm transition-all">Spectrum</button> |
| <button data-style="radial" class="visualizer-style-btn bg-gray-700 hover:bg-gray-600 text-white px-4 py-2 rounded-full text-sm transition-all">Radial</button> |
| </div> |
| </div> |
| </div> |
| |
| <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8"> |
| <div class="bg-gray-800 rounded-xl p-6 shadow-lg hover:transform hover:scale-105 transition-all duration-300"> |
| <div class="flex items-center mb-4"> |
| <div class="bg-blue-600 p-3 rounded-full mr-3"> |
| <i class="fas fa-chart-bar text-white"></i> |
| </div> |
| <h3 class="text-lg font-semibold">Bar Visualizer</h3> |
| </div> |
| <p class="text-gray-400">Classic frequency bars that rise and fall with your voice. Perfect for seeing the different frequencies in your sound.</p> |
| </div> |
| |
| <div class="bg-gray-800 rounded-xl p-6 shadow-lg hover:transform hover:scale-105 transition-all duration-300"> |
| <div class="flex items-center mb-4"> |
| <div class="bg-purple-600 p-3 rounded-full mr-3"> |
| <i class="fas fa-circle-notch text-white"></i> |
| </div> |
| <h3 class="text-lg font-semibold">Circle Visualizer</h3> |
| </div> |
| <p class="text-gray-400">Pulsing circles that respond to volume. The more intense your voice, the more circles appear and grow.</p> |
| </div> |
| |
| <div class="bg-gray-800 rounded-xl p-6 shadow-lg hover:transform hover:scale-105 transition-all duration-300"> |
| <div class="flex items-center mb-4"> |
| <div class="bg-indigo-600 p-3 rounded-full mr-3"> |
| <i class="fas fa-wave-square text-white"></i> |
| </div> |
| <h3 class="text-lg font-semibold">Wave Visualizer</h3> |
| </div> |
| <p class="text-gray-400">Smooth waveform that undulates with your voice. Creates beautiful flowing patterns from your sound.</p> |
| </div> |
| |
| <div class="bg-gray-800 rounded-xl p-6 shadow-lg hover:transform hover:scale-105 transition-all duration-300"> |
| <div class="flex items-center mb-4"> |
| <div class="bg-pink-600 p-3 rounded-full mr-3"> |
| <i class="fas fa-exchange-alt text-white"></i> |
| </div> |
| <h3 class="text-lg font-semibold">Bidirectional Wave</h3> |
| </div> |
| <p class="text-gray-400">Waveform that expands both upwards and downwards with a beautiful gradient, creating a symmetrical visualization of your sound.</p> |
| </div> |
| |
| <div class="bg-gray-800 rounded-xl p-6 shadow-lg hover:transform hover:scale-105 transition-all duration-300"> |
| <div class="flex items-center mb-4"> |
| <div class="bg-teal-600 p-3 rounded-full mr-3"> |
| <i class="fas fa-circle text-white"></i> |
| </div> |
| <h3 class="text-lg font-semibold">Rounded Wave</h3> |
| </div> |
| <p class="text-gray-400">Circular waveform with a central image that pulses with your voice, creating a hypnotic radial effect with a vibrant gradient.</p> |
| </div> |
| |
| <div class="bg-gray-800 rounded-xl p-6 shadow-lg hover:transform hover:scale-105 transition-all duration-300"> |
| <div class="flex items-center mb-4"> |
| <div class="bg-orange-600 p-3 rounded-full mr-3"> |
| <i class="fas fa-atom text-white"></i> |
| </div> |
| <h3 class="text-lg font-semibold">Particle Visualizer</h3> |
| </div> |
| <p class="text-gray-400">Dynamic particles that dance with your voice. Each particle responds to different frequency ranges.</p> |
| </div> |
| |
| <div class="bg-gray-800 rounded-xl p-6 shadow-lg hover:transform hover:scale-105 transition-all duration-300"> |
| <div class="flex items-center mb-4"> |
| <div class="bg-yellow-600 p-3 rounded-full mr-3"> |
| <i class="fas fa-sliders-h text-white"></i> |
| </div> |
| <h3 class="text-lg font-semibold">Spectrum Visualizer</h3> |
| </div> |
| <p class="text-gray-400">Vertical lines representing the full frequency spectrum. See the complete range of your sound.</p> |
| </div> |
| |
| <div class="bg-gray-800 rounded-xl p-6 shadow-lg hover:transform hover:scale-105 transition-all duration-300"> |
| <div class="flex items-center mb-4"> |
| <div class="bg-red-600 p-3 rounded-full mr-3"> |
| <i class="fas fa-bullseye text-white"></i> |
| </div> |
| <h3 class="text-lg font-semibold">Radial Visualizer</h3> |
| </div> |
| <p class="text-gray-400">Circular bars radiating from the center. Creates a hypnotic pattern that responds to your voice.</p> |
| </div> |
| </div> |
| |
| <footer class="text-center text-gray-500 text-sm mt-8"> |
| <p>Sound Visualizer App | Created by <a href="https://github.com/almahmudbd/sound-visualizer" target"_blank">almahmud</a></p> |
| </footer> |
| </div> |
|
|
| <script> |
| document.addEventListener('DOMContentLoaded', function() { |
| |
| const startBtn = document.getElementById('startBtn'); |
| const stopBtn = document.getElementById('stopBtn'); |
| const statusEl = document.getElementById('status'); |
| const visualizer = document.getElementById('visualizer'); |
| const barsContainer = document.getElementById('barsContainer'); |
| const circlesContainer = document.getElementById('circlesContainer'); |
| const waveContainer = document.getElementById('waveContainer'); |
| const wavePath = document.getElementById('wavePath'); |
| const bidirectionalWaveContainer = document.getElementById('bidirectionalWaveContainer'); |
| const bidirectionalWavePathTop = document.getElementById('bidirectionalWavePathTop'); |
| const bidirectionalWavePathBottom = document.getElementById('bidirectionalWavePathBottom'); |
| const roundedWaveContainer = document.getElementById('roundedWaveContainer'); |
| const centerImage = document.getElementById('centerImage'); |
| const roundedWave = document.getElementById('roundedWave'); |
| const roundedWavePath = document.getElementById('roundedWavePath'); |
| const particlesContainer = document.getElementById('particlesContainer'); |
| const spectrumContainer = document.getElementById('spectrumContainer'); |
| const radialContainer = document.getElementById('radialContainer'); |
| const styleBtns = document.querySelectorAll('.visualizer-style-btn'); |
| const sensitivitySlider = document.getElementById('sensitivity'); |
| const smoothnessSlider = document.getElementById('smoothness'); |
| const sensitivityValue = document.getElementById('sensitivityValue'); |
| const smoothnessValue = document.getElementById('smoothnessValue'); |
| const changeImageBtn = document.getElementById('changeImageBtn'); |
| const imageUploadContainer = document.getElementById('imageUploadContainer'); |
| const imageUpload = document.getElementById('imageUpload'); |
| const imageUrl = document.getElementById('imageUrl'); |
| const cancelImageBtn = document.getElementById('cancelImageBtn'); |
| const applyImageBtn = document.getElementById('applyImageBtn'); |
| |
| |
| centerImage.style.backgroundImage = 'url("https://i.ibb.co.com/kVvn0mqM/Sukkar-Shop-mini-logo.png")'; |
| |
| |
| let audioContext; |
| let analyser; |
| let microphone; |
| let dataArray; |
| let animationId; |
| let isActive = false; |
| let currentStyle = 'bars'; |
| let sensitivity = 100; |
| let smoothness = 5; |
| let lastValues = []; |
| |
| |
| initializeBars(); |
| initializeSpectrum(); |
| initializeRadial(); |
| |
| |
| sensitivitySlider.addEventListener('input', function() { |
| sensitivity = parseInt(this.value); |
| sensitivityValue.textContent = `${sensitivity}%`; |
| }); |
| |
| smoothnessSlider.addEventListener('input', function() { |
| smoothness = parseInt(this.value); |
| smoothnessValue.textContent = smoothness; |
| }); |
| |
| function initializeBars() { |
| |
| for (let i = 0; i < 30; i++) { |
| const bar = document.createElement('div'); |
| bar.className = 'bar'; |
| barsContainer.appendChild(bar); |
| } |
| } |
| |
| function initializeSpectrum() { |
| |
| for (let i = 0; i < 128; i++) { |
| const line = document.createElement('div'); |
| line.className = 'spectrum-line'; |
| line.style.left = `${(i / 128) * 100}%`; |
| spectrumContainer.appendChild(line); |
| |
| |
| lastValues[i] = 0; |
| } |
| } |
| |
| function initializeRadial() { |
| |
| const centerX = 50; |
| const centerY = 50; |
| const radius = 40; |
| |
| for (let group = 0; group < 6; group++) { |
| for (let i = 0; i < 10; i++) { |
| const angle = (group * 60 + i * 6) * (Math.PI / 180); |
| const x = centerX + radius * Math.cos(angle); |
| const y = centerY + radius * Math.sin(angle); |
| |
| const bar = document.createElement('div'); |
| bar.className = 'radial-bar'; |
| bar.style.left = `${x}%`; |
| bar.style.top = `${y}%`; |
| bar.style.transform = `rotate(${group * 60 + i * 6}deg)`; |
| radialContainer.appendChild(bar); |
| } |
| } |
| } |
| |
| |
| styleBtns.forEach(btn => { |
| btn.addEventListener('click', function() { |
| |
| styleBtns.forEach(b => { |
| b.classList.remove('bg-blue-600', 'hover:bg-gray-600', 'active'); |
| b.classList.add('bg-gray-700', 'hover:bg-gray-600'); |
| }); |
| this.classList.remove('bg-gray-700', 'hover:bg-gray-600'); |
| this.classList.add('bg-blue-600', 'active'); |
| |
| |
| currentStyle = this.dataset.style; |
| |
| |
| barsContainer.style.display = 'none'; |
| circlesContainer.style.display = 'none'; |
| waveContainer.style.display = 'none'; |
| bidirectionalWaveContainer.style.display = 'none'; |
| roundedWaveContainer.style.display = 'none'; |
| particlesContainer.style.display = 'none'; |
| spectrumContainer.style.display = 'none'; |
| radialContainer.style.display = 'none'; |
| |
| |
| if (currentStyle === 'bars') { |
| barsContainer.style.display = 'flex'; |
| } else if (currentStyle === 'circles') { |
| circlesContainer.style.display = 'block'; |
| } else if (currentStyle === 'wave') { |
| waveContainer.style.display = 'block'; |
| } else if (currentStyle === 'bidirectional') { |
| bidirectionalWaveContainer.style.display = 'block'; |
| } else if (currentStyle === 'rounded') { |
| roundedWaveContainer.style.display = 'flex'; |
| } else if (currentStyle === 'particles') { |
| particlesContainer.style.display = 'block'; |
| } else if (currentStyle === 'spectrum') { |
| spectrumContainer.style.display = 'block'; |
| } else if (currentStyle === 'radial') { |
| radialContainer.style.display = 'block'; |
| } |
| }); |
| }); |
| |
| |
| document.querySelector('[data-style="bars"]').click(); |
| |
| |
| changeImageBtn.addEventListener('click', function() { |
| imageUploadContainer.classList.add('active'); |
| }); |
| |
| cancelImageBtn.addEventListener('click', function() { |
| imageUploadContainer.classList.remove('active'); |
| imageUrl.value = ''; |
| imageUpload.value = ''; |
| }); |
| |
| applyImageBtn.addEventListener('click', function() { |
| if (imageUrl.value) { |
| |
| centerImage.style.backgroundImage = `url("${imageUrl.value}")`; |
| imageUploadContainer.classList.remove('active'); |
| imageUrl.value = ''; |
| } else if (imageUpload.files && imageUpload.files[0]) { |
| |
| const reader = new FileReader(); |
| reader.onload = function(e) { |
| centerImage.style.backgroundImage = `url("${e.target.result}")`; |
| imageUploadContainer.classList.remove('active'); |
| imageUpload.value = ''; |
| }; |
| reader.readAsDataURL(imageUpload.files[0]); |
| } |
| }); |
| |
| |
| startBtn.addEventListener('click', async function() { |
| try { |
| const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); |
| |
| |
| audioContext = new (window.AudioContext || window.webkitAudioContext)(); |
| analyser = audioContext.createAnalyser(); |
| analyser.fftSize = 256; |
| |
| const source = audioContext.createMediaStreamSource(stream); |
| source.connect(analyser); |
| |
| dataArray = new Uint8Array(analyser.frequencyBinCount); |
| |
| isActive = true; |
| startBtn.disabled = true; |
| stopBtn.disabled = false; |
| statusEl.innerHTML = '<i class="fas fa-check-circle mr-2 text-green-400"></i> Microphone active'; |
| |
| |
| visualize(); |
| } catch (err) { |
| console.error('Error accessing microphone:', err); |
| statusEl.innerHTML = '<i class="fas fa-exclamation-circle mr-2 text-red-400"></i> Microphone access denied'; |
| } |
| }); |
| |
| |
| stopBtn.addEventListener('click', function() { |
| if (audioContext) { |
| if (audioContext.state !== 'closed') { |
| audioContext.close(); |
| } |
| } |
| |
| if (microphone) { |
| microphone.getTracks().forEach(track => track.stop()); |
| } |
| |
| isActive = false; |
| cancelAnimationFrame(animationId); |
| |
| startBtn.disabled = false; |
| stopBtn.disabled = true; |
| statusEl.innerHTML = '<i class="fas fa-info-circle mr-2"></i> Mic inactive ❗'; |
| |
| |
| resetVisualizers(); |
| }); |
| |
| function resetVisualizers() { |
| |
| const bars = document.querySelectorAll('.bar'); |
| bars.forEach(bar => { |
| bar.style.transform = 'scaleY(0)'; |
| }); |
| |
| |
| circlesContainer.innerHTML = ''; |
| |
| |
| wavePath.setAttribute('d', ''); |
| |
| |
| bidirectionalWavePathTop.setAttribute('d', ''); |
| bidirectionalWavePathBottom.setAttribute('d', ''); |
| |
| |
| roundedWavePath.setAttribute('d', ''); |
| |
| |
| particlesContainer.innerHTML = ''; |
| |
| |
| const spectrumLines = document.querySelectorAll('.spectrum-line'); |
| spectrumLines.forEach(line => { |
| line.style.height = '0'; |
| }); |
| |
| |
| const radialBars = document.querySelectorAll('.radial-bar'); |
| radialBars.forEach(bar => { |
| bar.style.height = '0'; |
| }); |
| } |
| |
| |
| function visualize() { |
| if (!isActive) return; |
| |
| analyser.getByteFrequencyData(dataArray); |
| |
| if (currentStyle === 'bars') { |
| |
| const bars = document.querySelectorAll('.bar'); |
| for (let i = 0; i < bars.length; i++) { |
| const index = Math.floor(i * (dataArray.length / bars.length)); |
| const rawValue = dataArray[index] / 255; |
| const value = applySmoothing(rawValue * (sensitivity / 100), i); |
| bars[i].style.transform = `scaleY(${value})`; |
| } |
| } |
| else if (currentStyle === 'circles') { |
| |
| circlesContainer.innerHTML = ''; |
| |
| |
| for (let i = 0; i < dataArray.length; i += 5) { |
| const value = dataArray[i] / 255; |
| if (value > 0.2) { |
| const circle = document.createElement('div'); |
| circle.className = 'circle pulse'; |
| |
| |
| const x = Math.random() * 100; |
| const y = Math.random() * 100; |
| |
| |
| const size = 5 + (value * 100 * (sensitivity / 100)); |
| |
| circle.style.width = `${size}px`; |
| circle.style.height = `${size}px`; |
| circle.style.left = `${x}%`; |
| circle.style.top = `${y}%`; |
| circle.style.opacity = value; |
| circle.style.animationDelay = `${Math.random() * 0.5}s`; |
| |
| circlesContainer.appendChild(circle); |
| } |
| } |
| } |
| else if (currentStyle === 'wave') { |
| |
| let pathData = 'M 0 100'; |
| const segmentWidth = 1000 / (dataArray.length - 1); |
| |
| for (let i = 0; i < dataArray.length; i++) { |
| const rawValue = dataArray[i] / 255; |
| const value = applySmoothing(rawValue, i); |
| const y = 100 - (value * 100 * (sensitivity / 100)); |
| pathData += ` L ${i * segmentWidth} ${y}`; |
| } |
| |
| pathData += ' L 1000 100 Z'; |
| wavePath.setAttribute('d', pathData); |
| } |
| else if (currentStyle === 'bidirectional') { |
| |
| let pathDataTop = 'M 0 100'; |
| let pathDataBottom = 'M 0 100'; |
| const segmentWidth = 1000 / (dataArray.length - 1); |
| |
| for (let i = 0; i < dataArray.length; i++) { |
| const rawValue = dataArray[i] / 255; |
| const value = applySmoothing(rawValue, i); |
| const yTop = 100 - (value * 100 * (sensitivity / 100)); |
| const yBottom = 100 + (value * 100 * (sensitivity / 100)); |
| |
| pathDataTop += ` L ${i * segmentWidth} ${yTop}`; |
| pathDataBottom += ` L ${i * segmentWidth} ${yBottom}`; |
| } |
| |
| pathDataTop += ' L 1000 100 Z'; |
| pathDataBottom += ' L 1000 100 Z'; |
| |
| bidirectionalWavePathTop.setAttribute('d', pathDataTop); |
| bidirectionalWavePathBottom.setAttribute('d', pathDataBottom); |
| } |
| else if (currentStyle === 'rounded') { |
| |
| const centerX = 500; |
| const centerY = 500; |
| const radius = 400; |
| const numPoints = dataArray.length; |
| let pathData = ''; |
| |
| for (let i = 0; i <= numPoints; i++) { |
| const index = i % numPoints; |
| const rawValue = dataArray[index] / 255; |
| const value = applySmoothing(rawValue, index); |
| const angle = (i / numPoints) * Math.PI * 2; |
| const pointRadius = radius + (value * 200 * (sensitivity / 100)); |
| |
| const x = centerX + pointRadius * Math.cos(angle); |
| const y = centerY + pointRadius * Math.sin(angle); |
| |
| if (i === 0) { |
| pathData = `M ${x} ${y}`; |
| } else { |
| pathData += ` L ${x} ${y}`; |
| } |
| } |
| |
| pathData += ' Z'; |
| roundedWavePath.setAttribute('d', pathData); |
| |
| |
| const avgValue = dataArray.reduce((sum, val) => sum + val, 0) / dataArray.length / 255; |
| centerImage.style.transform = `scale(${1 + avgValue * 0.2})`; |
| } |
| else if (currentStyle === 'particles') { |
| |
| particlesContainer.innerHTML = ''; |
| |
| |
| for (let i = 0; i < dataArray.length; i += 3) { |
| const value = dataArray[i] / 255; |
| if (value > 0.1) { |
| const particle = document.createElement('div'); |
| particle.className = 'particle'; |
| |
| |
| const x = (i / dataArray.length) * 100; |
| const y = 50 + (Math.random() * 40 - 20); |
| |
| |
| const size = 5 + (value * 20 * (sensitivity / 100)); |
| const opacity = 0.3 + (value * 0.7); |
| |
| |
| const hue = 240 + (i / dataArray.length * 60); |
| |
| particle.style.width = `${size}px`; |
| particle.style.height = `${size}px`; |
| particle.style.left = `${x}%`; |
| particle.style.top = `${y}%`; |
| particle.style.opacity = opacity; |
| particle.style.background = `linear-gradient(45deg, hsl(${hue}, 80%, 60%), hsl(${hue + 30}, 80%, 60%))`; |
| particle.style.transform = `translate(-50%, -50%) scale(${1 + value})`; |
| |
| particlesContainer.appendChild(particle); |
| } |
| } |
| } |
| else if (currentStyle === 'spectrum') { |
| |
| const spectrumLines = document.querySelectorAll('.spectrum-line'); |
| |
| for (let i = 0; i < spectrumLines.length; i++) { |
| const index = Math.floor(i * (dataArray.length / spectrumLines.length)); |
| const rawValue = dataArray[index] / 255; |
| const value = applySmoothing(rawValue * (sensitivity / 100), i); |
| |
| spectrumLines[i].style.height = `${value * 100}%`; |
| spectrumLines[i].style.opacity = 0.5 + (value * 0.5); |
| |
| |
| const hue = 200 + (i / spectrumLines.length * 160); |
| spectrumLines[i].style.background = `linear-gradient(to top, hsl(${hue}, 80%, 60%), hsl(${hue + 20}, 80%, 60%))`; |
| } |
| } |
| else if (currentStyle === 'radial') { |
| |
| const radialBars = document.querySelectorAll('.radial-bar'); |
| |
| for (let i = 0; i < radialBars.length; i++) { |
| const index = Math.floor(i * (dataArray.length / radialBars.length)); |
| const rawValue = dataArray[index] / 255; |
| const value = applySmoothing(rawValue * (sensitivity / 100), i); |
| |
| radialBars[i].style.height = `${value * 50}px`; |
| radialBars[i].style.opacity = 0.5 + (value * 0.5); |
| |
| |
| const hue = 240 + (i / radialBars.length * 120); |
| radialBars[i].style.background = `linear-gradient(to top, hsl(${hue}, 80%, 60%), hsl(${hue + 30}, 80%, 60%))`; |
| } |
| } |
| |
| animationId = requestAnimationFrame(visualize); |
| } |
| |
| |
| function applySmoothing(newValue, index) { |
| if (!lastValues[index]) { |
| lastValues[index] = newValue; |
| return newValue; |
| } |
| |
| |
| const smoothedValue = lastValues[index] + (newValue - lastValues[index]) / smoothness; |
| lastValues[index] = smoothedValue; |
| return smoothedValue; |
| } |
| |
| |
| console.log('Click "Start Microphone" to begin visualizing your voice. Choose between different visualization styles.'); |
| }); |
| </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=almahmud/simple-sound-visualizer" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |