diff --git a/public/assets/atelier-ambiance.mp3 b/public/assets/atelier-ambiance.mp3 new file mode 100644 index 0000000..be21f6a Binary files /dev/null and b/public/assets/atelier-ambiance.mp3 differ diff --git a/public/main.js b/public/main.js index 5c5f9ac..f4e7f08 100644 --- a/public/main.js +++ b/public/main.js @@ -601,13 +601,16 @@ document.addEventListener('DOMContentLoaded', () => { }); // ========================================================== - // 6. AMBIENT WORKSHOP SOUND + // 6. AMBIENT WORKSHOP SOUND (MP3) // ========================================================== - let audioCtx = null; let soundOn = false; - let soundNodes = []; - let clickTimer = null; + + // Create audio element (preloaded, looped, low volume) + const ambientAudio = new Audio('/assets/atelier-ambiance.mp3'); + ambientAudio.loop = true; + ambientAudio.volume = 0; + ambientAudio.preload = 'auto'; const headerNav = document.querySelector('.header-nav'); if (headerNav) { @@ -626,93 +629,18 @@ document.addEventListener('DOMContentLoaded', () => { attachCursorHover([soundBtn]); } - function createAudioContext() { - audioCtx = new (window.AudioContext || window.webkitAudioContext)(); - } - - function generateBrownNoise(ctx, duration) { - const bufferSize = ctx.sampleRate * duration; - const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate); - const data = buffer.getChannelData(0); - let lastOut = 0; - for (let i = 0; i < bufferSize; i++) { - const white = Math.random() * 2 - 1; - data[i] = (lastOut + (0.02 * white)) / 1.02; - lastOut = data[i]; - data[i] *= 3.5; - } - return buffer; - } - + // Smooth volume fade using GSAP function startAmbientSound() { - if (!audioCtx) createAudioContext(); - if (audioCtx.state === 'suspended') audioCtx.resume(); - - const noiseBuffer = generateBrownNoise(audioCtx, 4); - const noiseSource = audioCtx.createBufferSource(); - noiseSource.buffer = noiseBuffer; - noiseSource.loop = true; - - const lowpass = audioCtx.createBiquadFilter(); - lowpass.type = 'lowpass'; - lowpass.frequency.value = 180; - - const gain = audioCtx.createGain(); - gain.gain.value = 0; - gain.gain.linearRampToValueAtTime(0.045, audioCtx.currentTime + 2); - - noiseSource.connect(lowpass); - lowpass.connect(gain); - gain.connect(audioCtx.destination); - noiseSource.start(); - - soundNodes.push({ source: noiseSource, gain: gain }); - scheduleMetalClick(); - } - - function playMetalClick() { - if (!audioCtx || audioCtx.state !== 'running') return; - - const osc = audioCtx.createOscillator(); - osc.type = 'sine'; - osc.frequency.value = 600 + Math.random() * 2400; - - const gain = audioCtx.createGain(); - gain.gain.value = 0.006 + Math.random() * 0.014; - gain.gain.exponentialRampToValueAtTime( - 0.0001, - audioCtx.currentTime + 0.08 + Math.random() * 0.25 - ); - - const filter = audioCtx.createBiquadFilter(); - filter.type = 'bandpass'; - filter.frequency.value = 1000 + Math.random() * 2000; - filter.Q.value = 12 + Math.random() * 25; - - osc.connect(filter); - filter.connect(gain); - gain.connect(audioCtx.destination); - osc.start(); - osc.stop(audioCtx.currentTime + 0.5); - } - - function scheduleMetalClick() { - if (!soundOn) return; - const delay = 2000 + Math.random() * 7000; - clickTimer = setTimeout(() => { - if (!soundOn) return; - playMetalClick(); - scheduleMetalClick(); - }, delay); + ambientAudio.play().then(() => { + gsap.to(ambientAudio, { volume: 0.04, duration: 2, ease: 'power2.out' }); + }).catch(() => {}); } function stopAmbientSound() { - if (clickTimer) { clearTimeout(clickTimer); clickTimer = null; } - soundNodes.forEach(({ source, gain }) => { - gain.gain.linearRampToValueAtTime(0, audioCtx.currentTime + 1); - setTimeout(() => { try { source.stop(); } catch (e) {} }, 1200); + gsap.to(ambientAudio, { + volume: 0, duration: 1.2, ease: 'power2.in', + onComplete: () => ambientAudio.pause(), }); - soundNodes = []; } function toggleSound() { diff --git a/public/style.css b/public/style.css index 9bcbfad..b1345ef 100644 --- a/public/style.css +++ b/public/style.css @@ -14,7 +14,7 @@ --pad: 2rem; } -* { margin: 0; padding: 0; box-sizing: border-box; } +* { margin: 0; padding: 0; box-sizing: border-box; cursor: none !important; } html { font-size: 13px; } @@ -35,32 +35,33 @@ body { pointer-events: none; z-index: 999998; opacity: 0; - transition: width 0.15s, height 0.15s; + background: var(--clr-black); + transition: width 0.15s, height 0.15s, background 0.15s; } .cad-h { - width: 40px; height: 1px; - background: var(--clr-black); - transform: translateY(-0.5px); + width: 40px; height: 2px; + transform: translateY(-1px); } -.cad-h.cad-hover { width: 56px; margin-left: -8px; } +.cad-h.cad-hover { width: 56px; margin-left: -8px; background: var(--clr-red); } .cad-v { - width: 1px; height: 40px; - background: var(--clr-black); - transform: translateX(-0.5px); + width: 2px; height: 40px; + transform: translateX(-1px); } -.cad-v.cad-hover { height: 56px; margin-top: -8px; } +.cad-v.cad-hover { height: 56px; margin-top: -8px; background: var(--clr-red); } .cad-center { position: fixed; - width: 3px; height: 3px; + width: 6px; height: 6px; background: var(--clr-red); pointer-events: none; z-index: 999999; opacity: 0; - transform: translate(-1.5px, -1.5px); + transform: translate(-3px, -3px); + transition: width 0.15s, height 0.15s; } .cad-center.cad-hover { - width: 5px; height: 5px; - transform: translate(-2.5px, -2.5px); + width: 9px; height: 9px; + transform: translate(-4.5px, -4.5px); + background: var(--clr-black); } .cad-coords { position: fixed;