Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Game Sound Synthesizer - 20 Effects</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700;900&display=swap'); | |
| * { | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| } | |
| body { | |
| font-family: 'Orbitron', monospace; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| overflow-x: hidden; | |
| } | |
| .sound-button { | |
| background: linear-gradient(145deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05)); | |
| backdrop-filter: blur(10px); | |
| border: 2px solid rgba(255, 255, 255, 0.2); | |
| transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .sound-button::before { | |
| content: ''; | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| width: 0; | |
| height: 0; | |
| border-radius: 50%; | |
| background: rgba(255, 255, 255, 0.5); | |
| transform: translate(-50%, -50%); | |
| transition: width 0.6s, height 0.6s; | |
| } | |
| .sound-button.playing::before { | |
| width: 300px; | |
| height: 300px; | |
| } | |
| .sound-button:hover { | |
| transform: translateY(-5px) scale(1.05); | |
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); | |
| border-color: rgba(255, 255, 255, 0.4); | |
| } | |
| .sound-button:active { | |
| transform: translateY(-2px) scale(1.02); | |
| } | |
| .visualizer { | |
| position: fixed; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| height: 100px; | |
| background: rgba(0, 0, 0, 0.3); | |
| backdrop-filter: blur(10px); | |
| display: flex; | |
| align-items: flex-end; | |
| justify-content: space-around; | |
| padding: 10px; | |
| z-index: 10; | |
| } | |
| .bar { | |
| width: 3px; | |
| background: linear-gradient(to top, #00ff88, #00ffff); | |
| transition: height 0.1s ease; | |
| border-radius: 2px; | |
| } | |
| @keyframes pulse { | |
| 0% { | |
| transform: scale(1); | |
| } | |
| 50% { | |
| transform: scale(1.05); | |
| } | |
| 100% { | |
| transform: scale(1); | |
| } | |
| } | |
| .pulse { | |
| animation: pulse 0.5s ease-in-out; | |
| } | |
| .glass-morphism { | |
| background: rgba(255, 255, 255, 0.1); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.2); | |
| } | |
| .neon-text { | |
| text-shadow: 0 0 10px rgba(255, 255, 255, 0.8), | |
| 0 0 20px rgba(255, 255, 255, 0.6), | |
| 0 0 30px rgba(255, 255, 255, 0.4); | |
| } | |
| .category-badge { | |
| background: linear-gradient(135deg, #667eea, #764ba2); | |
| padding: 2px 8px; | |
| border-radius: 12px; | |
| font-size: 10px; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| } | |
| @keyframes float { | |
| 0%, | |
| 100% { | |
| transform: translateY(0px); | |
| } | |
| 50% { | |
| transform: translateY(-10px); | |
| } | |
| } | |
| .float-animation { | |
| animation: float 3s ease-in-out infinite; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Header --> | |
| <header class="glass-morphism sticky top-0 z-50 p-4 mb-8"> | |
| <div class="container mx-auto flex justify-between items-center"> | |
| <div class="flex items-center space-x-4"> | |
| <div class="float-animation"> | |
| <i class="fas fa-gamepad text-3xl text-white"></i> | |
| </div> | |
| <div> | |
| <h1 class="text-2xl font-bold text-white neon-text">Game Sound Synthesizer</h1> | |
| <p class="text-xs text-gray-200">20 Procedurally Generated Sound Effects</p> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" class="text-xs text-blue-300 hover:text-blue-200 transition-colors">Built with anycoder</a> | |
| </div> | |
| </div> | |
| <div class="flex items-center space-x-4"> | |
| <div class="flex items-center space-x-2"> | |
| <i class="fas fa-volume-up text-white"></i> | |
| <input type="range" id="volumeControl" min="0" max="100" value="50" | |
| class="w-32 h-2 bg-white/30 rounded-lg appearance-none cursor-pointer"> | |
| <span id="volumeValue" class="text-white text-sm">50%</span> | |
| </div> | |
| <button id="randomPlay" class="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg transition-all"> | |
| <i class="fas fa-random mr-2"></i>Random Play | |
| </button> | |
| </div> | |
| </div> | |
| </header> | |
| <!-- Main Content --> | |
| <main class="container mx-auto px-4 pb-32"> | |
| <div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 gap-4" id="soundGrid"> | |
| <!-- Sound buttons will be generated here --> | |
| </div> | |
| </main> | |
| <!-- Visualizer --> | |
| <div class="visualizer" id="visualizer"> | |
| <!-- Bars will be generated here --> | |
| </div> | |
| <script> | |
| // Audio Context | |
| const audioContext = new (window.AudioContext || window.webkitAudioContext)(); | |
| let masterGainNode = audioContext.createGain(); | |
| masterGainNode.connect(audioContext.destination); | |
| masterGainNode.gain.value = 0.5; | |
| // Sound definitions | |
| const sounds = [ | |
| { id: 1, name: 'Jump', icon: 'fa-arrow-up', category: 'action' }, | |
| { id: 2, name: 'Explosion', icon: 'fa-burst', category: 'action' }, | |
| { id: 3, name: 'Power Up', icon: 'fa-bolt', category: 'power' }, | |
| { id: 4, name: 'Coin', icon: 'fa-coins', category: 'collect' }, | |
| { id: 5, name: 'Laser', icon: 'fa-location-arrow', category: 'weapon' }, | |
| { id: 6, name: 'Hit', icon: 'fa-hammer', category: 'impact' }, | |
| { id: 7, name: 'Footstep', icon: 'fa-shoe-prints', category: 'movement' }, | |
| { id: 8, name: 'Magic', icon: 'fa-hat-wizard', category: 'magic' }, | |
| { id: 9, name: 'Alert', icon: 'fa-bell', category: 'ui' }, | |
| { id: 10, name: 'Success', icon: 'fa-check-circle', category: 'ui' }, | |
| { id: 11, name: 'Door Open', icon: 'fa-door-open', category: 'environment' }, | |
| { id: 12, name: 'Heal', icon: 'fa-heart', category: 'power' }, | |
| { id: 13, name: 'Shield', icon: 'fa-shield', category: 'power' }, | |
| { id: 14, name: 'Teleport', icon: 'fa-portal-enter', category: 'magic' }, | |
| { id: 15, name: 'Sword Swing', icon: 'fa-sword', category: 'weapon' }, | |
| { id: 16, name: 'Water Splash', icon: 'fa-droplet', category: 'environment' }, | |
| { id: 17, name: 'Fire', icon: 'fa-fire', category: 'element' }, | |
| { id: 18, name: 'Thunder', icon: 'fa-cloud-bolt', category: 'element' }, | |
| { id: 19, name: 'Level Up', icon: 'fa-trophy', category: 'ui' }, | |
| { id: 20, name: 'Game Over', icon: 'fa-skull', category: 'ui' } | |
| ]; | |
| // Sound synthesis functions | |
| const soundSynthesizers = { | |
| 1: () => { // Jump | |
| const osc = audioContext.createOscillator(); | |
| const gain = audioContext.createGain(); | |
| osc.connect(gain); | |
| gain.connect(masterGainNode); | |
| osc.frequency.setValueAtTime(400, audioContext.currentTime); | |
| osc.frequency.exponentialRampToValueAtTime(600, audioContext.currentTime + 0.1); | |
| gain.gain.setValueAtTime(0.3, audioContext.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.2); | |
| osc.start(audioContext.currentTime); | |
| osc.stop(audioContext.currentTime + 0.2); | |
| }, | |
| 2: () => { // Explosion | |
| const bufferSize = audioContext.sampleRate * 0.5; | |
| const buffer = audioContext.createBuffer(1, bufferSize, audioContext.sampleRate); | |
| const output = buffer.getChannelData(0); | |
| for (let i = 0; i < bufferSize; i++) { | |
| output[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / bufferSize, 2); | |
| } | |
| const noise = audioContext.createBufferSource(); | |
| const filter = audioContext.createBiquadFilter(); | |
| const gain = audioContext.createGain(); | |
| noise.buffer = buffer; | |
| filter.type = 'lowpass'; | |
| filter.frequency.value = 1000; | |
| noise.connect(filter); | |
| filter.connect(gain); | |
| gain.connect(masterGainNode); | |
| gain.gain.setValueAtTime(0.5, audioContext.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5); | |
| noise.start(); | |
| }, | |
| 3: () => { // Power Up | |
| const osc1 = audioContext.createOscillator(); | |
| const osc2 = audioContext.createOscillator(); | |
| const gain = audioContext.createGain(); | |
| osc1.connect(gain); | |
| osc2.connect(gain); | |
| gain.connect(masterGainNode); | |
| osc1.type = 'sine'; | |
| osc2.type = 'sine'; | |
| osc1.frequency.setValueAtTime(523.25, audioContext.currentTime); | |
| osc2.frequency.setValueAtTime(659.25, audioContext.currentTime); | |
| osc1.frequency.exponentialRampToValueAtTime(1046.5, audioContext.currentTime + 0.3); | |
| osc2.frequency.exponentialRampToValueAtTime(1318.5, audioContext.currentTime + 0.3); | |
| gain.gain.setValueAtTime(0, audioContext.currentTime); | |
| gain.gain.linearRampToValueAtTime(0.3, audioContext.currentTime + 0.05); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5); | |
| osc1.start(audioContext.currentTime); | |
| osc2.start(audioContext.currentTime); | |
| osc1.stop(audioContext.currentTime + 0.5); | |
| osc2.stop(audioContext.currentTime + 0.5); | |
| }, | |
| 4: () => { // Coin | |
| const osc = audioContext.createOscillator(); | |
| const gain = audioContext.createGain(); | |
| osc.connect(gain); | |
| gain.connect(masterGainNode); | |
| osc.type = 'square'; | |
| osc.frequency.setValueAtTime(988, audioContext.currentTime); | |
| osc.frequency.setValueAtTime(1319, audioContext.currentTime + 0.1); | |
| gain.gain.setValueAtTime(0.1, audioContext.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.15); | |
| osc.start(audioContext.currentTime); | |
| osc.stop(audioContext.currentTime + 0.15); | |
| }, | |
| 5: () => { // Laser | |
| const osc = audioContext.createOscillator(); | |
| const gain = audioContext.createGain(); | |
| const filter = audioContext.createBiquadFilter(); | |
| osc.connect(filter); | |
| filter.connect(gain); | |
| gain.connect(masterGainNode); | |
| osc.type = 'sawtooth'; | |
| osc.frequency.setValueAtTime(800, audioContext.currentTime); | |
| filter.type = 'highpass'; | |
| filter.frequency.setValueAtTime(1000, audioContext.currentTime); | |
| filter.Q.value = 10; | |
| gain.gain.setValueAtTime(0.3, audioContext.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.2); | |
| osc.start(audioContext.currentTime); | |
| osc.stop(audioContext.currentTime + 0.2); | |
| }, | |
| 6: () => { // Hit | |
| const osc = audioContext.createOscillator(); | |
| const gain = audioContext.createGain(); | |
| osc.connect(gain); | |
| gain.connect(masterGainNode); | |
| osc.type = 'triangle'; | |
| osc.frequency.setValueAtTime(150, audioContext.currentTime); | |
| osc.frequency.exponentialRampToValueAtTime(50, audioContext.currentTime + 0.1); | |
| gain.gain.setValueAtTime(0.4, audioContext.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1); | |
| osc.start(audioContext.currentTime); | |
| osc.stop(audioContext.currentTime + 0.1); | |
| }, | |
| 7: () => { // Footstep | |
| const noise = audioContext.createBufferSource(); | |
| const filter = audioContext.createBiquadFilter(); | |
| const gain = audioContext.createGain(); | |
| const bufferSize = audioContext.sampleRate * 0.1; | |
| const buffer = audioContext.createBuffer(1, bufferSize, audioContext.sampleRate); | |
| const output = buffer.getChannelData(0); | |
| for (let i = 0; i < bufferSize; i++) { | |
| output[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / bufferSize, 3); | |
| } | |
| noise.buffer = buffer; | |
| filter.type = 'highpass'; | |
| filter.frequency.value = 2000; | |
| noise.connect(filter); | |
| filter.connect(gain); | |
| gain.connect(masterGainNode); | |
| gain.gain.setValueAtTime(0.2, audioContext.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.1); | |
| noise.start(); | |
| }, | |
| 8: () => { // Magic | |
| const osc1 = audioContext.createOscillator(); | |
| const osc2 = audioContext.createOscillator(); | |
| const gain = audioContext.createGain(); | |
| const lfo = audioContext.createOscillator(); | |
| const lfoGain = audioContext.createGain(); | |
| lfo.frequency.value = 5; | |
| lfoGain.gain.value = 100; | |
| lfo.connect(lfoGain); | |
| lfoGain.connect(osc1.frequency); | |
| lfoGain.connect(osc2.frequency); | |
| osc1.connect(gain); | |
| osc2.connect(gain); | |
| gain.connect(masterGainNode); | |
| osc1.type = 'sine'; | |
| osc2.type = 'sine'; | |
| osc1.frequency.value = 440; | |
| osc2.frequency.value = 554.37; | |
| gain.gain.setValueAtTime(0, audioContext.currentTime); | |
| gain.gain.linearRampToValueAtTime(0.2, audioContext.currentTime + 0.1); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 1); | |
| osc1.start(audioContext.currentTime); | |
| osc2.start(audioContext.currentTime); | |
| lfo.start(audioContext.currentTime); | |
| osc1.stop(audioContext.currentTime + 1); | |
| osc2.stop(audioContext.currentTime + 1); | |
| lfo.stop(audioContext.currentTime + 1); | |
| }, | |
| 9: () => { // Alert | |
| const osc = audioContext.createOscillator(); | |
| const gain = audioContext.createGain(); | |
| osc.connect(gain); | |
| gain.connect(masterGainNode); | |
| osc.type = 'square'; | |
| osc.frequency.setValueAtTime(880, audioContext.currentTime); | |
| gain.gain.setValueAtTime(0.2, audioContext.currentTime); | |
| gain.gain.setValueAtTime(0, audioContext.currentTime + 0.1); | |
| gain.gain.setValueAtTime(0.2, audioContext.currentTime + 0.2); | |
| gain.gain.setValueAtTime(0, audioContext.currentTime + 0.3); | |
| gain.gain.setValueAtTime(0.2, audioContext.currentTime + 0.4); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5); | |
| osc.start(audioContext.currentTime); | |
| osc.stop(audioContext.currentTime + 0.5); | |
| }, | |
| 10: () => { // Success | |
| const notes = [523.25, 659.25, 783.99]; | |
| notes.forEach((freq, i) => { | |
| const osc = audioContext.createOscillator(); | |
| const gain = audioContext.createGain(); | |
| osc.connect(gain); | |
| gain.connect(masterGainNode); | |
| osc.frequency.value = freq; | |
| osc.type = 'sine'; | |
| gain.gain.setValueAtTime(0, audioContext.currentTime + i * 0.1); | |
| gain.gain.linearRampToValueAtTime(0.2, audioContext.currentTime + i * 0.1 + 0.05); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + i * 0.1 + 0.3); | |
| osc.start(audioContext.currentTime + i * 0.1); | |
| osc.stop(audioContext.currentTime + i * 0.1 + 0.3); | |
| }); | |
| }, | |
| 11: () => { // Door Open | |
| const osc = audioContext.createOscillator(); | |
| const gain = audioContext.createGain(); | |
| osc.connect(gain); | |
| gain.connect(masterGainNode); | |
| osc.type = 'sine'; | |
| osc.frequency.setValueAtTime(200, audioContext.currentTime); | |
| osc.frequency.linearRampToValueAtTime(400, audioContext.currentTime + 0.5); | |
| gain.gain.setValueAtTime(0.1, audioContext.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5); | |
| osc.start(audioContext.currentTime); | |
| osc.stop(audioContext.currentTime + 0.5); | |
| }, | |
| 12: () => { // Heal | |
| const osc1 = audioContext.createOscillator(); | |
| const osc2 = audioContext.createOscillator(); | |
| const gain = audioContext.createGain(); | |
| osc1.connect(gain); | |
| osc2.connect(gain); | |
| gain.connect(masterGainNode); | |
| osc1.type = 'sine'; | |
| osc2.type = 'sine'; | |
| osc1.frequency.setValueAtTime(440, audioContext.currentTime); | |
| osc2.frequency.setValueAtTime(554.37, audioContext.currentTime); | |
| osc1.frequency.exponentialRampToValueAtTime(880, audioContext.currentTime + 0.5); | |
| osc2.frequency.exponentialRampToValueAtTime(1108.73, audioContext.currentTime + 0.5); | |
| gain.gain.setValueAtTime(0, audioContext.currentTime); | |
| gain.gain.linearRampToValueAtTime(0.2, audioContext.currentTime + 0.1); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.8); | |
| osc1.start(audioContext.currentTime); | |
| osc2.start(audioContext.currentTime); | |
| osc1.stop(audioContext.currentTime + 0.8); | |
| osc2.stop(audioContext.currentTime + 0.8); | |
| }, | |
| 13: () => { // Shield | |
| const osc = audioContext.createOscillator(); | |
| const gain = audioContext.createGain(); | |
| const filter = audioContext.createBiquadFilter(); | |
| osc.connect(filter); | |
| filter.connect(gain); | |
| gain.connect(masterGainNode); | |
| osc.type = 'sawtooth'; | |
| osc.frequency.value = 150; | |
| filter.type = 'bandpass'; | |
| filter.frequency.value = 500; | |
| filter.Q.value = 5; | |
| gain.gain.setValueAtTime(0.2, audioContext.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.3); | |
| osc.start(audioContext.currentTime); | |
| osc.stop(audioContext.currentTime + 0.3); | |
| }, | |
| 14: () => { // Teleport | |
| const bufferSize = audioContext.sampleRate * 0.3; | |
| const buffer = audioContext.createBuffer(1, bufferSize, audioContext.sampleRate); | |
| const output = buffer.getChannelData(0); | |
| for (let i = 0; i < bufferSize; i++) { | |
| output[i] = (Math.random() * 2 - 1) * Math.exp(-i / (bufferSize * 0.1)); | |
| } | |
| const noise = audioContext.createBufferSource(); | |
| const filter = audioContext.createBiquadFilter(); | |
| const gain = audioContext.createGain(); | |
| noise.buffer = buffer; | |
| filter.type = 'highpass'; | |
| filter.frequency.setValueAtTime(100, audioContext.currentTime); | |
| filter.frequency.exponentialRampToValueAtTime(5000, audioContext.currentTime + 0.3); | |
| noise.connect(filter); | |
| filter.connect(gain); | |
| gain.connect(masterGainNode); | |
| gain.gain.setValueAtTime(0.3, audioContext.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.3); | |
| noise.start(); | |
| }, | |
| 15: () => { // Sword Swing | |
| const noise = audioContext.createBufferSource(); | |
| const filter = audioContext.createBiquadFilter(); | |
| const gain = audioContext.createGain(); | |
| const bufferSize = audioContext.sampleRate * 0.2; | |
| const buffer = audioContext.createBuffer(1, bufferSize, audioContext.sampleRate); | |
| const output = buffer.getChannelData(0); | |
| for (let i = 0; i < bufferSize; i++) { | |
| output[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / bufferSize, 2); | |
| } | |
| noise.buffer = buffer; | |
| filter.type = 'highpass'; | |
| filter.frequency.value = 3000; | |
| noise.connect(filter); | |
| filter.connect(gain); | |
| gain.connect(masterGainNode); | |
| gain.gain.setValueAtTime(0.3, audioContext.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.2); | |
| noise.start(); | |
| }, | |
| 16: () => { // Water Splash | |
| const bufferSize = audioContext.sampleRate * 0.4; | |
| const buffer = audioContext.createBuffer(1, bufferSize, audioContext.sampleRate); | |
| const output = buffer.getChannelData(0); | |
| for (let i = 0; i < bufferSize; i++) { | |
| output[i] = (Math.random() * 2 - 1) * Math.pow(1 - i / bufferSize, 1.5); | |
| } | |
| const noise = audioContext.createBufferSource(); | |
| const filter = audioContext.createBiquadFilter(); | |
| const gain = audioContext.createGain(); | |
| noise.buffer = buffer; | |
| filter.type = 'bandpass'; | |
| filter.frequency.value = 1000; | |
| filter.Q.value = 2; | |
| noise.connect(filter); | |
| filter.connect(gain); | |
| gain.connect(masterGainNode); | |
| gain.gain.setValueAtTime(0.3, audioContext.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.4); | |
| noise.start(); | |
| }, | |
| 17: () => { // Fire | |
| const bufferSize = audioContext.sampleRate * 0.5; | |
| const buffer = audioContext.createBuffer(1, bufferSize, audioContext.sampleRate); | |
| const output = buffer.getChannelData(0); | |
| for (let i = 0; i < bufferSize; i++) { | |
| output[i] = (Math.random() * 2 - 1) * (1 - i / bufferSize); | |
| } | |
| const noise = audioContext.createBufferSource(); | |
| const filter = audioContext.createBiquadFilter(); | |
| const gain = audioContext.createGain(); | |
| noise.buffer = buffer; | |
| filter.type = 'lowpass'; | |
| filter.frequency.value = 2000; | |
| noise.connect(filter); | |
| filter.connect(gain); | |
| gain.connect(masterGainNode); | |
| gain.gain.setValueAtTime(0.2, audioContext.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5); | |
| noise.start(); | |
| }, | |
| 18: () => { // Thunder | |
| const bufferSize = audioContext.sampleRate * 1; | |
| const buffer = audioContext.createBuffer(1, bufferSize, audioContext.sampleRate); | |
| const output = buffer.getChannelData(0); | |
| for (let i = 0; i < bufferSize; i++) { | |
| output[i] = (Math.random() * 2 - 1) * Math.exp(-i / (bufferSize * 0.05)); | |
| } | |
| const noise = audioContext.createBufferSource(); | |
| const filter = audioContext.createBiquadFilter(); | |
| const gain = audioContext.createGain(); | |
| noise.buffer = buffer; | |
| filter.type = 'lowpass'; | |
| filter.frequency.value = 200; | |
| noise.connect(filter); | |
| filter.connect(gain); | |
| gain.connect(masterGainNode); | |
| gain.gain.setValueAtTime(0.5, audioContext.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 1); | |
| noise.start(); | |
| }, | |
| 19: () => { // Level Up | |
| const notes = [261.63, 329.63, 392, 523.25, 659.25]; | |
| notes.forEach((freq, i) => { | |
| const osc = audioContext.createOscillator(); | |
| const gain = audioContext.createGain(); | |
| osc.connect(gain); | |
| gain.connect(masterGainNode); | |
| osc.frequency.value = freq; | |
| osc.type = 'square'; | |
| gain.gain.setValueAtTime(0, audioContext.currentTime + i * 0.1); | |
| gain.gain.linearRampToValueAtTime(0.15, audioContext.currentTime + i * 0.1 + 0.05); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + i * 0.1 + 0.2); | |
| osc.start(audioContext.currentTime + i * 0.1); | |
| osc.stop(audioContext.currentTime + i * 0.1 + 0.2); | |
| }); | |
| }, | |
| 20: () => { // Game Over | |
| const osc = audioContext.createOscillator(); | |
| const gain = audioContext.createGain(); | |
| osc.connect(gain); | |
| gain.connect(masterGainNode); | |
| osc.type = 'sawtooth'; | |
| osc.frequency.setValueAtTime(440, audioContext.currentTime); | |
| osc.frequency.exponentialRampToValueAtTime(110, audioContext.currentTime + 1); | |
| gain.gain.setValueAtTime(0.3, audioContext.currentTime); | |
| gain.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 1); | |
| osc.start(audioContext.currentTime); | |
| osc.stop(audioContext.currentTime + 1); | |
| } | |
| }; | |
| // Create visualizer bars | |
| function createVisualizer() { | |
| const visualizer = document.getElementById('visualizer'); | |
| for (let i = 0; i < 50; i++) { | |
| const bar = document.createElement('div'); | |
| bar.className = 'bar'; | |
| bar.style.height = '5px'; | |
| visualizer.appendChild(bar); | |
| } | |
| } | |
| // Animate visualizer | |
| function animateVisualizer() { | |
| const bars = document.querySelectorAll('.bar'); | |
| bars.forEach(bar => { | |
| const height = Math.random() * 80 + 10; | |
| bar.style.height = height + 'px'; | |
| }); | |
| } | |
| // Play sound function | |
| function playSound(soundId) { | |
| if (soundSynthesizers[soundId]) { | |
| soundSynthesizers[soundId](); | |
| animateVisualizer(); | |
| // Add visual feedback | |
| const button = document.querySelector(`[data-sound-id="${soundId}"]`); | |
| if (button) { | |
| button.classList.add('playing', 'pulse'); | |
| setTimeout(() => { | |
| button.classList.remove('playing', 'pulse'); | |
| }, 500); | |
| } | |
| } | |
| } | |
| // Create sound buttons | |
| function createSoundButtons() { | |
| const grid = document.getElementById('soundGrid'); | |
| sounds.forEach(sound => { | |
| const button = document.createElement('button'); | |
| button.className = 'sound-button p-6 rounded-xl text-white flex flex-col items-center justify-center space-y-2 min-h-[120px]'; | |
| button.dataset.soundId = sound.id; | |
| button.innerHTML = ` | |
| <i class="fas ${sound.icon} text-3xl"></i> | |
| <span class="text-sm font-semibold">${sound.name}</span> | |
| <span class="category-badge">${sound.category}</span> | |
| `; | |
| button.addEventListener('click', () => playSound(sound.id)); | |
| grid.appendChild(button); | |
| }); | |
| } | |
| // Volume control | |
| document.getElementById('volumeControl').addEventListener('input', (e) => { | |
| const volume = e.target.value / 100; | |
| masterGainNode.gain.value = volume; | |
| document.getElementById('volumeValue').textContent = e.target.value + '%'; | |
| }); | |
| // Random play | |
| document.getElementById('randomPlay').addEventListener('click', () => { | |
| const randomSound = sounds[Math.floor(Math.random() * sounds.length)]; | |
| playSound(randomSound.id); | |
| }); | |
| // Initialize | |
| createVisualizer(); | |
| createSoundButtons(); | |
| // Animate visualizer periodically | |
| setInterval(() => { | |
| if (Math.random() > 0.7) { | |
| animateVisualizer(); | |
| } | |
| }, 500); | |
| </script> | |
| </body> | |
| </html> |