diff --git a/.vscode/settings.json b/.vscode/settings.json index 6b665aaa..3d2cf3d4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "liveServer.settings.port": 5501 + "liveServer.settings.port": 5501, + "livePreview.defaultPreviewPath": "/Domains/Frontend/MiniProjects/MusicVisualizer/index.html" } diff --git a/Domains/Frontend/MiniProjects/MusicVisualizer/README.md b/Domains/Frontend/MiniProjects/MusicVisualizer/README.md new file mode 100644 index 00000000..50b2042a --- /dev/null +++ b/Domains/Frontend/MiniProjects/MusicVisualizer/README.md @@ -0,0 +1,299 @@ +# 🎵 Music Visualizer + +**Contributor:** shr128 +**Domain:** Frontend +**Difficulty:** Intermediate +**Tech Stack:** HTML5, CSS3, JavaScript (ES6+), Web Audio API, Canvas API + +--- + +

+ Music Visualizer Screenshot +

+ +--- + +## 📝 Description + +An **interactive Music Visualizer** that transforms audio into stunning real-time visual animations. Upload your favorite songs or generate test tones to see audio frequencies come to life with multiple visualization modes. + +Features dynamic frequency analysis using the Web Audio API and Canvas rendering for smooth, responsive animations. Perfect for learning audio processing, canvas manipulation, and creating engaging user experiences! + +--- + +## 🎯 Features + +* 🎵 **Audio File Upload** - Support for MP3, WAV, and other audio formats +* 🎹 **Tone Generator** - Built-in 440Hz tone for testing +* 🎨 **4 Visualization Modes:** + * **Bars** - Classic frequency bars with rainbow colors + * **Wave** - Flowing waveform animation + * **Circular** - Radial spectrum analyzer + * **Spectrum** - Gradient frequency display +* ⚡ **Real-time Audio Analysis** - Live frequency data processing +* 🎮 **Playback Controls** - Play, pause, and restart audio +* ✨ **Animated Background** - Floating particle effects +* 🌈 **Dynamic Gradients** - Smooth color transitions +* 📱 **Fully Responsive** - Works on desktop, tablet, and mobile + +--- + +## 🛠️ Tech Stack + +* **HTML5** - Semantic structure with Canvas element +* **CSS3** - Modern UI with gradients, glassmorphism, and animations +* **JavaScript (ES6+)** - Audio processing and visualization logic +* **Web Audio API** - Audio context, analyser nodes, and frequency data +* **Canvas API** - Real-time graphics rendering + +--- + +## 🚀 How to Run + +### Method 1: Direct Browser + +1. Download or clone the repository +2. Open `index.html` in your browser +3. Click "Choose Audio File" to upload a song or "Generate Tone" to test +4. Select a visualization mode and enjoy! + +### Method 2: Live Server (Recommended) + +1. Install VS Code and the Live Server extension +2. Right-click `index.html` → **Open with Live Server** +3. The app will open at `http://localhost:5500` + +### Method 3: Local Server + +```bash +# Using Python 3 +python -m http.server 8000 + +# Using Node.js +npx http-server +``` + +--- + +## 📁 Project Structure + +``` +MusicVisualizer/ +├── index.html # Main HTML structure +├── style.css # Styling and animations +├── script.js # Visualization logic and audio processing +└── README.md # Project documentation +``` + +--- + +## 💻 Code Highlights + +### Audio Context Setup + +```javascript +function setupAudioContext() { + audioContext = new (window.AudioContext || window.webkitAudioContext)(); + source = audioContext.createMediaElementSource(audio); + analyser = audioContext.createAnalyser(); + analyser.fftSize = 256; + + source.connect(analyser); + analyser.connect(audioContext.destination); +} +``` + +### Real-time Frequency Analysis + +```javascript +function visualize() { + animationId = requestAnimationFrame(visualize); + analyser.getByteFrequencyData(dataArray); + + // Clear canvas with fade effect + ctx.fillStyle = 'rgba(26, 26, 46, 0.2)'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + // Draw visualization based on selected mode + if (currentMode === 'bars') drawBars(); +} +``` + +### Circular Visualization + +```javascript +function drawCircular() { + const centerX = canvas.width / 2; + const centerY = canvas.height / 2; + const radius = Math.min(canvas.width, canvas.height) / 3; + + for (let i = 0; i < bufferLength; i++) { + const angle = (i / bufferLength) * Math.PI * 2; + const barHeight = (dataArray[i] / 255) * 100; + + const x1 = centerX + Math.cos(angle) * radius; + const y1 = centerY + Math.sin(angle) * radius; + const x2 = centerX + Math.cos(angle) * (radius + barHeight); + const y2 = centerY + Math.sin(angle) * (radius + barHeight); + + ctx.strokeStyle = `hsl(${(i / bufferLength) * 360}, 100%, 60%)`; + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + } +} +``` + +### Tone Generator + +```javascript +generateToneBtn.addEventListener('click', () => { + const oscillator = audioContext.createOscillator(); + const gainNode = audioContext.createGain(); + + oscillator.type = 'sine'; + oscillator.frequency.setValueAtTime(440, audioContext.currentTime); + gainNode.gain.setValueAtTime(0.3, audioContext.currentTime); + + oscillator.connect(gainNode); + gainNode.connect(analyser); + analyser.connect(audioContext.destination); + + oscillator.start(); + setTimeout(() => oscillator.stop(), 2000); +}); +``` + +--- + +## 📚 Learning Outcomes + +### Skills Practiced + +* ✅ Web Audio API implementation +* ✅ Canvas 2D rendering and animation +* ✅ Real-time data visualization +* ✅ Event-driven programming +* ✅ DOM manipulation and file handling +* ✅ Responsive design principles +* ✅ CSS animations and gradients + +### Concepts Learned + +* Audio frequency analysis and FFT (Fast Fourier Transform) +* RequestAnimationFrame for smooth animations +* Creating audio oscillators and gain nodes +* Canvas drawing techniques (bars, waves, circles) +* Glassmorphism and modern UI design +* Audio context lifecycle management + +--- + +## 🎨 Customization Ideas + +1. **Additional Visualization Modes** + * Waveform with glow effects + * 3D particle system + * Spiral pattern + * Mandala-style visualization + +2. **Audio Controls** + * Volume slider + * Playback speed control + * Loop functionality + * Playlist support + +3. **Visual Enhancements** + * Color theme selector + * Custom gradient builder + * Fullscreen mode + * Screen recording feature + +4. **Advanced Features** + * Beat detection and rhythm analysis + * Equalizer with frequency bands + * Save visualization as GIF/Video + * Microphone input for live audio + +5. **UI Improvements** + * Dark/Light mode toggle + * Keyboard shortcuts + * Drag-and-drop file upload + * Audio file metadata display + +--- + +## 🐛 Known Issues + +* Some browsers may require user interaction before playing audio +* Large audio files may take time to load +* Safari may have limited Web Audio API support + +### Solutions: +* Ensure user clicks a button before initializing audio context +* Add loading indicators for file processing +* Test across multiple browsers for compatibility + +--- + +## 🚀 Future Enhancements + +* [ ] Add microphone input support +* [ ] Implement beat detection +* [ ] Create playlist functionality +* [ ] Add export visualization as video +* [ ] Include preset visual themes +* [ ] Add audio effects (reverb, delay, etc.) +* [ ] Implement WebGL for 3D visualizations +* [ ] Add sharing capabilities +* [ ] Create mobile app version + +--- + +## 🌐 Browser Compatibility + +| Browser | Support | +|---------|---------| +| Chrome | ✅ Full | +| Firefox | ✅ Full | +| Safari | ⚠️ Partial (some audio API limitations) | +| Edge | ✅ Full | +| Opera | ✅ Full | + +--- + +## 📖 Resources + +* [Web Audio API Documentation](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) +* [Canvas API Guide](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) +* [AnalyserNode Reference](https://developer.mozilla.org/en-US/docs/Web/API/AnalyserNode) + +--- + +## 📄 License + +MIT License — Free to use, modify, and share! + +--- + +## 🤝 Contributing + +This project is open for contributions! +Feel free to: + +* Fork and improve the visualizations +* Report bugs or issues +* Add new visualization modes +* Enhance UI/UX design +* Submit pull requests + +--- + +## 👨‍💻 Author + +Created with 🎵 and ❤️ for music and code enthusiasts! + +--- + +**Enjoy the Rhythm! 🎵🔥** \ No newline at end of file diff --git a/Domains/Frontend/MiniProjects/MusicVisualizer/index.html b/Domains/Frontend/MiniProjects/MusicVisualizer/index.html new file mode 100644 index 00000000..d465cb48 --- /dev/null +++ b/Domains/Frontend/MiniProjects/MusicVisualizer/index.html @@ -0,0 +1,44 @@ + + + + + + + Music Visualizer + + + + +
+ +
+

🎵 Music Visualizer

+

Experience your music in a whole new way

+ +
+ + +
+ + + + +
+ +
+
+ + +
+ + +
+ +
Upload an audio file or generate a tone to start
+
+
+ + + + + \ No newline at end of file diff --git a/Domains/Frontend/MiniProjects/MusicVisualizer/script.js b/Domains/Frontend/MiniProjects/MusicVisualizer/script.js new file mode 100644 index 00000000..b6f19a0e --- /dev/null +++ b/Domains/Frontend/MiniProjects/MusicVisualizer/script.js @@ -0,0 +1,246 @@ +const canvas = document.getElementById("canvas"); +const ctx = canvas.getContext("2d"); +const audioFileInput = document.getElementById("audioFile"); +const playPauseBtn = document.getElementById("playPause"); +const generateToneBtn = document.getElementById("generateTone"); +const info = document.getElementById("info"); +const modeButtons = document.querySelectorAll(".mode-btn"); + +let audioContext; +let analyser; +let dataArray; +let bufferLength; +let audio; +let source; +let isPlaying = false; +let animationId; +let currentMode = "bars"; + +// Set canvas size +canvas.width = canvas.offsetWidth; +canvas.height = canvas.offsetHeight; + +window.addEventListener("resize", () => { + canvas.width = canvas.offsetWidth; + canvas.height = canvas.offsetHeight; +}); + +// Create background particles +const particlesContainer = document.getElementById("particles"); +for (let i = 0; i < 50; i++) { + const particle = document.createElement("div"); + particle.className = "particle"; + particle.style.width = Math.random() * 5 + 2 + "px"; + particle.style.height = particle.style.width; + particle.style.left = Math.random() * 100 + "%"; + particle.style.top = Math.random() * 100 + "%"; + particle.style.animationDelay = Math.random() * 15 + "s"; + particle.style.animationDuration = Math.random() * 10 + 10 + "s"; + particlesContainer.appendChild(particle); +} + +// Mode selection +modeButtons.forEach((btn) => { + btn.addEventListener("click", () => { + modeButtons.forEach((b) => b.classList.remove("active")); + btn.classList.add("active"); + currentMode = btn.dataset.mode; + }); +}); + +// Audio file handling +audioFileInput.addEventListener("change", (e) => { + const file = e.target.files[0]; + if (file) { + if (audio) { + audio.pause(); + } + audio = new Audio(); + audio.src = URL.createObjectURL(file); + info.textContent = `Loaded: ${file.name}`; + playPauseBtn.disabled = false; + setupAudioContext(); + } +}); + +// Generate tone +generateToneBtn.addEventListener("click", () => { + if (!audioContext) { + audioContext = new (window.AudioContext || window.webkitAudioContext)(); + } + + if (source) { + source.disconnect(); + } + + const oscillator = audioContext.createOscillator(); + const gainNode = audioContext.createGain(); + + oscillator.type = "sine"; + oscillator.frequency.setValueAtTime(440, audioContext.currentTime); + + gainNode.gain.setValueAtTime(0.3, audioContext.currentTime); + + if (!analyser) { + analyser = audioContext.createAnalyser(); + analyser.fftSize = 256; + bufferLength = analyser.frequencyBinCount; + dataArray = new Uint8Array(bufferLength); + } + + oscillator.connect(gainNode); + gainNode.connect(analyser); + analyser.connect(audioContext.destination); + + oscillator.start(); + + setTimeout(() => { + oscillator.stop(); + }, 2000); + + if (!animationId) { + visualize(); + } + + info.textContent = "Playing 440Hz tone"; +}); + +// Play/Pause +playPauseBtn.addEventListener("click", () => { + if (isPlaying) { + audio.pause(); + playPauseBtn.textContent = "Play"; + isPlaying = false; + } else { + audio.play(); + playPauseBtn.textContent = "Pause"; + isPlaying = true; + if (!animationId) { + visualize(); + } + } +}); + +function setupAudioContext() { + if (!audioContext) { + audioContext = new (window.AudioContext || window.webkitAudioContext)(); + } + + if (source) { + source.disconnect(); + } + + source = audioContext.createMediaElementSource(audio); + analyser = audioContext.createAnalyser(); + analyser.fftSize = 256; + bufferLength = analyser.frequencyBinCount; + dataArray = new Uint8Array(bufferLength); + + source.connect(analyser); + analyser.connect(audioContext.destination); +} + +function visualize() { + animationId = requestAnimationFrame(visualize); + + if (!analyser) return; + + analyser.getByteFrequencyData(dataArray); + + ctx.fillStyle = "rgba(26, 26, 46, 0.2)"; + ctx.fillRect(0, 0, canvas.width, canvas.height); + + if (currentMode === "bars") { + drawBars(); + } else if (currentMode === "wave") { + drawWave(); + } else if (currentMode === "circular") { + drawCircular(); + } else if (currentMode === "spectrum") { + drawSpectrum(); + } +} + +function drawBars() { + const barWidth = (canvas.width / bufferLength) * 2.5; + let x = 0; + + for (let i = 0; i < bufferLength; i++) { + const barHeight = (dataArray[i] / 255) * canvas.height; + + const hue = (i / bufferLength) * 360; + ctx.fillStyle = `hsl(${hue}, 100%, 60%)`; + ctx.fillRect(x, canvas.height - barHeight, barWidth, barHeight); + + x += barWidth + 1; + } +} + +function drawWave() { + ctx.lineWidth = 3; + ctx.strokeStyle = "#f093fb"; + ctx.beginPath(); + + const sliceWidth = canvas.width / bufferLength; + let x = 0; + + for (let i = 0; i < bufferLength; i++) { + const v = dataArray[i] / 255; + const y = v * canvas.height; + + if (i === 0) { + ctx.moveTo(x, y); + } else { + ctx.lineTo(x, y); + } + + x += sliceWidth; + } + + ctx.stroke(); +} + +function drawCircular() { + const centerX = canvas.width / 2; + const centerY = canvas.height / 2; + const radius = Math.min(canvas.width, canvas.height) / 3; + + for (let i = 0; i < bufferLength; i++) { + const angle = (i / bufferLength) * Math.PI * 2; + const barHeight = (dataArray[i] / 255) * 100; + + const x1 = centerX + Math.cos(angle) * radius; + const y1 = centerY + Math.sin(angle) * radius; + const x2 = centerX + Math.cos(angle) * (radius + barHeight); + const y2 = centerY + Math.sin(angle) * (radius + barHeight); + + const hue = (i / bufferLength) * 360; + ctx.strokeStyle = `hsl(${hue}, 100%, 60%)`; + ctx.lineWidth = 2; + ctx.beginPath(); + ctx.moveTo(x1, y1); + ctx.lineTo(x2, y2); + ctx.stroke(); + } +} + +function drawSpectrum() { + const barWidth = canvas.width / bufferLength; + + for (let i = 0; i < bufferLength; i++) { + const barHeight = (dataArray[i] / 255) * canvas.height; + + const gradient = ctx.createLinearGradient( + 0, + canvas.height - barHeight, + 0, + canvas.height + ); + gradient.addColorStop(0, "#f093fb"); + gradient.addColorStop(0.5, "#f5576c"); + gradient.addColorStop(1, "#4facfe"); + + ctx.fillStyle = gradient; + ctx.fillRect(i * barWidth, canvas.height - barHeight, barWidth, barHeight); + } +} diff --git a/Domains/Frontend/MiniProjects/MusicVisualizer/style.css b/Domains/Frontend/MiniProjects/MusicVisualizer/style.css new file mode 100644 index 00000000..dd8c43ba --- /dev/null +++ b/Domains/Frontend/MiniProjects/MusicVisualizer/style.css @@ -0,0 +1,188 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + min-height: 100vh; + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; +} + +.container { + text-align: center; + padding: 20px; + max-width: 1200px; + width: 100%; +} + +h1 { + color: white; + font-size: 3em; + margin-bottom: 10px; + text-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); + animation: glow 2s ease-in-out infinite alternate; +} + +@keyframes glow { + from { + text-shadow: 0 5px 15px rgba(255, 255, 255, 0.3); + } + to { + text-shadow: 0 5px 30px rgba(255, 255, 255, 0.6); + } +} + +.subtitle { + color: rgba(255, 255, 255, 0.8); + font-size: 1.2em; + margin-bottom: 40px; +} + +.visualizer-container { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + border-radius: 30px; + padding: 40px; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); + border: 1px solid rgba(255, 255, 255, 0.2); +} + +#canvas { + width: 100%; + height: 400px; + border-radius: 20px; + background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); + box-shadow: inset 0 5px 20px rgba(0, 0, 0, 0.5); +} + +.controls { + margin-top: 30px; + display: flex; + gap: 15px; + justify-content: center; + flex-wrap: wrap; +} + +.btn { + padding: 15px 35px; + font-size: 1.1em; + font-weight: 600; + border: none; + border-radius: 50px; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); + text-transform: uppercase; + letter-spacing: 1px; +} + +.btn-primary { + background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); + color: white; +} + +.btn-primary:hover { + transform: translateY(-3px); + box-shadow: 0 8px 25px rgba(245, 87, 108, 0.4); +} + +.btn-secondary { + background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%); + color: white; +} + +.btn-secondary:hover { + transform: translateY(-3px); + box-shadow: 0 8px 25px rgba(79, 172, 254, 0.4); +} + +.btn:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; +} + +.file-input-wrapper { + position: relative; + overflow: hidden; + display: inline-block; +} + +.file-input-wrapper input[type=file] { + position: absolute; + left: -9999px; +} + +.visualizer-modes { + margin-top: 20px; + display: flex; + gap: 10px; + justify-content: center; + flex-wrap: wrap; +} + +.mode-btn { + padding: 10px 20px; + background: rgba(255, 255, 255, 0.2); + color: white; + border: 2px solid transparent; + border-radius: 25px; + cursor: pointer; + transition: all 0.3s ease; + font-weight: 600; +} + +.mode-btn:hover { + background: rgba(255, 255, 255, 0.3); +} + +.mode-btn.active { + background: rgba(255, 255, 255, 0.4); + border-color: white; +} + +.info { + margin-top: 20px; + color: rgba(255, 255, 255, 0.9); + font-size: 1.1em; + font-weight: 500; +} + +.particles { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: -1; +} + +.particle { + position: absolute; + background: white; + border-radius: 50%; + opacity: 0.3; + animation: float 15s infinite; +} + +@keyframes float { + 0%, 100% { + transform: translateY(0) translateX(0); + } + 25% { + transform: translateY(-50px) translateX(50px); + } + 50% { + transform: translateY(-100px) translateX(-50px); + } + 75% { + transform: translateY(-50px) translateX(-100px); + } +} \ No newline at end of file