scottrx11 commited on
Commit
7ddcf6c
·
verified ·
1 Parent(s): 9b372da

Initial DeepSite commit

Browse files
Files changed (4) hide show
  1. README.md +9 -6
  2. index.html +275 -19
  3. script.js +628 -0
  4. style.css +153 -19
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Deepsite Project Lr0up
3
- emoji: 📚
4
- colorFrom: pink
5
- colorTo: green
6
  sdk: static
7
- pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
1
  ---
2
+ title: DeepSite Project
3
+ colorFrom: gray
4
+ colorTo: purple
 
5
  sdk: static
6
+ emoji: 🖥️
7
+ tags:
8
+ - deepsite-v4
9
  ---
10
 
11
+ # DeepSite Project
12
+
13
+ This project has been created with [DeepSite](https://deepsite.hf.co) AI Vibe Coding.
index.html CHANGED
@@ -1,19 +1,275 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Psychedelic Binaural Studio</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://unpkg.com/lucide@latest"></script>
9
+ <link rel="stylesheet" href="style.css">
10
+ </head>
11
+ <body class="bg-gray-900 text-white min-h-screen overflow-x-hidden">
12
+ <!-- Animated Background -->
13
+ <div class="fixed inset-0 z-0">
14
+ <div class="absolute inset-0 bg-gradient-to-br from-purple-900 via-blue-900 to-pink-900 animate-gradient"></div>
15
+ <div class="absolute inset-0 opacity-30 bg-[radial-gradient(circle_at_center,_var(--tw-gradient-stops))] from-yellow-400 via-red-500 to-purple-600 animate-pulse-slow"></div>
16
+ <canvas id="particleCanvas" class="absolute inset-0 w-full h-full"></canvas>
17
+ </div>
18
+
19
+ <!-- Main Container -->
20
+ <div class="relative z-10 container mx-auto px-4 py-8 max-w-6xl">
21
+ <!-- Header -->
22
+ <header class="text-center mb-12">
23
+ <h1 class="text-5xl md:text-7xl font-bold mb-4 text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 via-purple-400 to-pink-400 animate-text-shimmer drop-shadow-lg">
24
+ Binaural Studio
25
+ </h1>
26
+ <p class="text-lg md:text-xl text-purple-200 font-light tracking-wide">
27
+ Customize your consciousness journey 🧘‍♀️✨
28
+ </p>
29
+ </header>
30
+
31
+ <!-- Main Control Panel -->
32
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
33
+
34
+ <!-- Binaural Controls -->
35
+ <div class="glass-panel rounded-3xl p-8 shadow-2xl border border-white/20">
36
+ <div class="flex items-center justify-between mb-6">
37
+ <h2 class="text-2xl font-semibold text-cyan-300 flex items-center gap-2">
38
+ <i data-lucide="activity" class="w-6 h-6"></i>
39
+ Binaural Beats
40
+ </h2>
41
+ <div class="flex gap-2">
42
+ <span class="text-xs px-3 py-1 rounded-full bg-cyan-500/20 text-cyan-300 border border-cyan-400/30">Spatial</span>
43
+ <span class="text-xs px-3 py-1 rounded-full bg-pink-500/20 text-pink-300 border border-pink-400/30">Monaural</span>
44
+ </div>
45
+ </div>
46
+
47
+ <!-- Mode Selection -->
48
+ <div class="mb-6 p-4 bg-black/20 rounded-2xl border border-white/10">
49
+ <label class="text-sm text-purple-200 mb-3 block font-medium">Beat Mode</label>
50
+ <div class="grid grid-cols-2 gap-3">
51
+ <button id="spatialBtn" class="mode-btn active bg-gradient-to-r from-cyan-500 to-blue-500 text-white py-3 px-4 rounded-xl font-medium transition-all hover:scale-105 shadow-lg shadow-cyan-500/30 border-2 border-transparent">
52
+ 🎧 Spatial (Binaural)
53
+ </button>
54
+ <button id="monauralBtn" class="mode-btn bg-white/10 text-purple-200 py-3 px-4 rounded-xl font-medium transition-all hover:scale-105 border-2 border-transparent hover:bg-white/20">
55
+ 🔊 Monaural
56
+ </button>
57
+ </div>
58
+ <p class="text-xs text-purple-300 mt-2 italic" id="modeDescription">
59
+ Different frequencies in each ear create the beat perception
60
+ </p>
61
+ </div>
62
+
63
+ <!-- Frequency Controls -->
64
+ <div class="space-y-6 mb-6">
65
+ <div class="control-group">
66
+ <div class="flex justify-between mb-2">
67
+ <label class="text-cyan-300 font-medium">Carrier Frequency (Base)</label>
68
+ <span id="carrierValue" class="text-cyan-300 font-mono bg-cyan-900/30 px-3 py-1 rounded-lg">200 Hz</span>
69
+ </div>
70
+ <input type="range" id="carrierSlider" min="40" max="500" value="200" class="w-full h-3 bg-black/30 rounded-full appearance-none cursor-pointer accent-cyan-400 hover:accent-cyan-300">
71
+ <div class="flex justify-between text-xs text-purple-300 mt-1">
72
+ <span>40 Hz (Deep)</span>
73
+ <span>500 Hz (Bright)</span>
74
+ </div>
75
+ </div>
76
+
77
+ <div class="control-group">
78
+ <div class="flex justify-between mb-2">
79
+ <label class="text-pink-300 font-medium">Beat Frequency (Difference)</label>
80
+ <span id="beatValue" class="text-pink-300 font-mono bg-pink-900/30 px-3 py-1 rounded-lg">10 Hz</span>
81
+ </div>
82
+ <input type="range" id="beatSlider" min="0.5" max="40" step="0.5" value="10" class="w-full h-3 bg-black/30 rounded-full appearance-none cursor-pointer accent-pink-400 hover:accent-pink-300">
83
+ <div class="flex justify-between text-xs text-purple-300 mt-1">
84
+ <span>0.5 Hz (Delta)</span>
85
+ <span>40 Hz (Gamma)</span>
86
+ </div>
87
+ </div>
88
+ </div>
89
+
90
+ <!-- Brainwave Presets -->
91
+ <div class="grid grid-cols-5 gap-2 mb-6">
92
+ <button class="brainwave-btn bg-indigo-500/20 hover:bg-indigo-500/40 border border-indigo-400/30 rounded-lg py-2 text-xs font-medium transition-all" data-carrier="100" data-beat="0.5">
93
+ Delta<br><span class="text-[10px] opacity-70">Sleep</span>
94
+ </button>
95
+ <button class="brainwave-btn bg-blue-500/20 hover:bg-blue-500/40 border border-blue-400/30 rounded-lg py-2 text-xs font-medium transition-all" data-carrier="150" data-beat="6">
96
+ Theta<br><span class="text-[10px] opacity-70">Relax</span>
97
+ </button>
98
+ <button class="brainwave-btn bg-green-500/20 hover:bg-green-500/40 border border-green-400/30 rounded-lg py-2 text-xs font-medium transition-all" data-carrier="200" data-beat="10">
99
+ Alpha<br><span class="text-[10px] opacity-70">Focus</span>
100
+ </button>
101
+ <button class="brainwave-btn bg-yellow-500/20 hover:bg-yellow-500/40 border border-yellow-400/30 rounded-lg py-2 text-xs font-medium transition-all" data-carrier="250" data-beat="20">
102
+ Beta<br><span class="text-[10px] opacity-70">Active</span>
103
+ </button>
104
+ <button class="brainwave-btn bg-red-500/20 hover:bg-red-500/40 border border-red-400/30 rounded-lg py-2 text-xs font-medium transition-all" data-carrier="300" data-beat="40">
105
+ Gamma<br><span class="text-[10px] opacity-70">Peak</span>
106
+ </button>
107
+ </div>
108
+
109
+ <!-- Ramp Controls -->
110
+ <div class="mb-6 p-4 bg-black/20 rounded-2xl border border-white/10">
111
+ <label class="text-sm text-purple-200 mb-3 block font-medium">Volume Ramping</label>
112
+ <div class="grid grid-cols-3 gap-2">
113
+ <button id="rampNone" class="ramp-btn active bg-gradient-to-r from-purple-500 to-pink-500 text-white py-2 px-3 rounded-lg text-sm font-medium transition-all">
114
+ Instant
115
+ </button>
116
+ <button id="rampUp" class="ramp-btn bg-white/10 text-purple-200 py-2 px-3 rounded-lg text-sm font-medium transition-all hover:bg-white/20">
117
+ Ramp Up ⬆️
118
+ </button>
119
+ <button id="rampDown" class="ramp-btn bg-white/10 text-purple-200 py-2 px-3 rounded-lg text-sm font-medium transition-all hover:bg-white/20">
120
+ Ramp Down ⬇️
121
+ </button>
122
+ </div>
123
+ <div class="mt-3 flex items-center gap-3">
124
+ <label class="text-xs text-purple-300">Duration:</label>
125
+ <input type="range" id="rampDuration" min="5" max="300" value="60" class="flex-1 h-2 bg-black/30 rounded-full accent-purple-400">
126
+ <span id="rampDurationValue" class="text-xs text-purple-300 font-mono w-12">60s</span>
127
+ </div>
128
+ </div>
129
+
130
+ <!-- Main Play Button -->
131
+ <button id="playBinauralBtn" class="w-full bg-gradient-to-r from-cyan-400 via-purple-500 to-pink-500 hover:from-cyan-300 hover:via-purple-400 hover:to-pink-400 text-white font-bold py-4 px-8 rounded-2xl text-xl shadow-lg shadow-purple-500/50 transition-all transform hover:scale-[1.02] active:scale-95 flex items-center justify-center gap-3">
132
+ <i data-lucide="play" class="w-6 h-6"></i>
133
+ <span>Generate Binaural Beat</span>
134
+ </button>
135
+ </div>
136
+
137
+ <!-- Noise Controls -->
138
+ <div class="glass-panel rounded-3xl p-8 shadow-2xl border border-white/20">
139
+ <div class="flex items-center justify-between mb-6">
140
+ <h2 class="text-2xl font-semibold text-amber-300 flex items-center gap-2">
141
+ <i data-lucide="waves" class="w-6 h-6"></i>
142
+ Spatial Noise
143
+ </h2>
144
+ <span class="text-xs px-3 py-1 rounded-full bg-amber-500/20 text-amber-300 border border-amber-400/30">Always Spatial</span>
145
+ </div>
146
+
147
+ <div class="space-y-4 mb-6">
148
+ <p class="text-sm text-purple-200 mb-4">
149
+ Immersive 3D noise environment with stereo spatialization. Each noise type is rendered in true stereo field for maximum immersion.
150
+ </p>
151
+
152
+ <!-- Noise Presets -->
153
+ <div class="grid grid-cols-1 gap-4">
154
+ <button id="pinkNoiseBtn" class="noise-btn group relative overflow-hidden bg-gradient-to-r from-pink-400 to-rose-500 hover:from-pink-300 hover:to-rose-400 text-white p-6 rounded-2xl transition-all transform hover:scale-[1.02] active:scale-95 shadow-lg shadow-pink-500/30 border border-pink-300/50">
155
+ <div class="relative z-10 flex items-center justify-between">
156
+ <div class="flex items-center gap-4">
157
+ <div class="w-12 h-12 rounded-full bg-white/20 flex items-center justify-center backdrop-blur-sm">
158
+ <i data-lucide="cloud" class="w-6 h-6"></i>
159
+ </div>
160
+ <div class="text-left">
161
+ <h3 class="font-bold text-lg">Pink Noise</h3>
162
+ <p class="text-sm text-pink-100">Balanced, natural, soothing</p>
163
+ </div>
164
+ </div>
165
+ <i data-lucide="play-circle" class="w-8 h-8 opacity-80 group-hover:opacity-100 transition-opacity"></i>
166
+ </div>
167
+ <div class="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-1000"></div>
168
+ </button>
169
+
170
+ <button id="brownNoiseBtn" class="noise-btn group relative overflow-hidden bg-gradient-to-r from-amber-600 to-orange-700 hover:from-amber-500 hover:to-orange-600 text-white p-6 rounded-2xl transition-all transform hover:scale-[1.02] active:scale-95 shadow-lg shadow-orange-500/30 border border-orange-300/50">
171
+ <div class="relative z-10 flex items-center justify-between">
172
+ <div class="flex items-center gap-4">
173
+ <div class="w-12 h-12 rounded-full bg-white/20 flex items-center justify-center backdrop-blur-sm">
174
+ <i data-lucide="flame" class="w-6 h-6"></i>
175
+ </div>
176
+ <div class="text-left">
177
+ <h3 class="font-bold text-lg">Brown Noise</h3>
178
+ <p class="text-sm text-orange-100">Deep, rumbling, intense</p>
179
+ </div>
180
+ </div>
181
+ <i data-lucide="play-circle" class="w-8 h-8 opacity-80 group-hover:opacity-100 transition-opacity"></i>
182
+ </div>
183
+ <div class="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-1000"></div>
184
+ </button>
185
+
186
+ <button id="whiteNoiseBtn" class="noise-btn group relative overflow-hidden bg-gradient-to-r from-slate-400 to-gray-500 hover:from-slate-300 hover:to-gray-400 text-white p-6 rounded-2xl transition-all transform hover:scale-[1.02] active:scale-95 shadow-lg shadow-gray-500/30 border border-gray-300/50">
187
+ <div class="relative z-10 flex items-center justify-between">
188
+ <div class="flex items-center gap-4">
189
+ <div class="w-12 h-12 rounded-full bg-white/20 flex items-center justify-center backdrop-blur-sm">
190
+ <i data-lucide="wind" class="w-6 h-6"></i>
191
+ </div>
192
+ <div class="text-left">
193
+ <h3 class="font-bold text-lg">White Noise</h3>
194
+ <p class="text-sm text-gray-100">Sharp, masking, crisp</p>
195
+ </div>
196
+ </div>
197
+ <i data-lucide="play-circle" class="w-8 h-8 opacity-80 group-hover:opacity-100 transition-opacity"></i>
198
+ </div>
199
+ <div class="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent translate-x-[-100%] group-hover:translate-x-[100%] transition-transform duration-1000"></div>
200
+ </button>
201
+ </div>
202
+ </div>
203
+
204
+ <!-- Noise Volume -->
205
+ <div class="mb-6 p-4 bg-black/20 rounded-2xl border border-white/10">
206
+ <div class="flex justify-between mb-2">
207
+ <label class="text-amber-300 font-medium">Noise Volume</label>
208
+ <span id="noiseVolumeValue" class="text-amber-300 font-mono bg-amber-900/30 px-3 py-1 rounded-lg">70%</span>
209
+ </div>
210
+ <input type="range" id="noiseVolumeSlider" min="0" max="100" value="70" class="w-full h-3 bg-black/30 rounded-full appearance-none cursor-pointer accent-amber-400">
211
+ </div>
212
+
213
+ <!-- Spatial Width -->
214
+ <div class="mb-6 p-4 bg-black/20 rounded-2xl border border-white/10">
215
+ <div class="flex justify-between mb-2">
216
+ <label class="text-purple-300 font-medium">Spatial Width (Stereo Separation)</label>
217
+ <span id="spatialWidthValue" class="text-purple-300 font-mono bg-purple-900/30 px-3 py-1 rounded-lg">Wide</span>
218
+ </div>
219
+ <input type="range" id="spatialWidthSlider" min="0" max="100" value="80" class="w-full h-3 bg-black/30 rounded-full appearance-none cursor-pointer accent-purple-400">
220
+ <p class="text-xs text-purple-300 mt-2">Adjust the perceived width of the noise field</p>
221
+ </div>
222
+
223
+ <!-- Stop All Button -->
224
+ <button id="stopAllBtn" class="w-full bg-gradient-to-r from-red-500 to-rose-600 hover:from-red-400 hover:to-rose-500 text-white font-bold py-4 px-8 rounded-2xl text-xl shadow-lg shadow-red-500/50 transition-all transform hover:scale-[1.02] active:scale-95 flex items-center justify-center gap-3 opacity-50 cursor-not-allowed" disabled>
225
+ <i data-lucide="square" class="w-6 h-6"></i>
226
+ <span>Stop All Audio</span>
227
+ </button>
228
+ </div>
229
+ </div>
230
+
231
+ <!-- Visualizer Section -->
232
+ <div class="mt-8 glass-panel rounded-3xl p-6 shadow-2xl border border-white/20">
233
+ <div class="flex items-center justify-between mb-4">
234
+ <h3 class="text-xl font-semibold text-cyan-300 flex items-center gap-2">
235
+ <i data-lucide="bar-chart-2" class="w-5 h-5"></i>
236
+ Real-time Visualization
237
+ </h3>
238
+ <div class="flex gap-2 text-xs">
239
+ <span class="flex items-center gap-1 text-cyan-300">
240
+ <span class="w-2 h-2 rounded-full bg-cyan-400 animate-pulse"></span>
241
+ Left Channel
242
+ </span>
243
+ <span class="flex items-center gap-1 text-pink-300">
244
+ <span class="w-2 h-2 rounded-full bg-pink-400 animate-pulse"></span>
245
+ Right Channel
246
+ </span>
247
+ </div>
248
+ </div>
249
+ <canvas id="audioVisualizer" class="w-full h-48 md:h-64 rounded-2xl bg-black/40 border border-white/10"></canvas>
250
+ </div>
251
+
252
+ <!-- Info Section -->
253
+ <div class="mt-8 grid grid-cols-1 md:grid-cols-3 gap-4 text-center">
254
+ <div class="glass-panel rounded-2xl p-4 border border-white/10">
255
+ <i data-lucide="headphones" class="w-8 h-8 mx-auto mb-2 text-cyan-400"></i>
256
+ <p class="text-sm text-purple-200">Use headphones for best binaural effect</p>
257
+ </div>
258
+ <div class="glass-panel rounded-2xl p-4 border border-white/10">
259
+ <i data-lucide="timer" class="w-8 h-8 mx-auto mb-2 text-pink-400"></i>
260
+ <p class="text-sm text-purple-200">Ramping helps gentle entry/exit</p>
261
+ </div>
262
+ <div class="glass-panel rounded-2xl p-4 border border-white/10">
263
+ <i data-lucide="volume-2" class="w-8 h-8 mx-auto mb-2 text-amber-400"></i>
264
+ <p class="text-sm text-purple-200">Spatial noise creates 3D soundscape</p>
265
+ </div>
266
+ </div>
267
+ </div>
268
+
269
+ <script src="script.js"></script>
270
+ <script>
271
+ lucide.createIcons();
272
+ </script>
273
+ <script src="https://deepsite.hf.co/deepsite-badge.js"></script>
274
+ </body>
275
+ </html>
script.js ADDED
@@ -0,0 +1,628 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Audio Context and State
2
+ let audioContext = null;
3
+ let isPlaying = false;
4
+ let currentMode = 'spatial'; // 'spatial' or 'monaural'
5
+ let rampMode = 'none'; // 'none', 'up', 'down'
6
+ let rampDuration = 60;
7
+ let activeNoiseType = null;
8
+
9
+ // Nodes
10
+ let binauralNodes = {
11
+ leftOsc: null,
12
+ rightOsc: null,
13
+ leftGain: null,
14
+ rightGain: null,
15
+ merger: null,
16
+ masterGain: null,
17
+ modulator: null // For monaural
18
+ };
19
+
20
+ let noiseNodes = {
21
+ leftBuffer: null,
22
+ rightBuffer: null,
23
+ leftSource: null,
24
+ rightSource: null,
25
+ leftGain: null,
26
+ rightGain: null,
27
+ leftPanner: null,
28
+ rightPanner: null,
29
+ masterGain: null,
30
+ filter: null
31
+ };
32
+
33
+ let visualizer = {
34
+ analyser: null,
35
+ canvas: null,
36
+ ctx: null,
37
+ animationId: null
38
+ };
39
+
40
+ // Initialize Audio Context
41
+ function initAudioContext() {
42
+ if (!audioContext) {
43
+ audioContext = new (window.AudioContext || window.webkitAudioContext)();
44
+ setupVisualizer();
45
+ }
46
+ if (audioContext.state === 'suspended') {
47
+ audioContext.resume();
48
+ }
49
+ }
50
+
51
+ // Setup Visualizer
52
+ function setupVisualizer() {
53
+ const canvas = document.getElementById('audioVisualizer');
54
+ const ctx = canvas.getContext('2d');
55
+
56
+ // Handle high DPI displays
57
+ const dpr = window.devicePixelRatio || 1;
58
+ const rect = canvas.getBoundingClientRect();
59
+ canvas.width = rect.width * dpr;
60
+ canvas.height = rect.height * dpr;
61
+ ctx.scale(dpr, dpr);
62
+
63
+ visualizer.canvas = canvas;
64
+ visualizer.ctx = ctx;
65
+
66
+ visualizer.analyser = audioContext.createAnalyser();
67
+ visualizer.analyser.fftSize = 2048;
68
+ visualizer.analyser.smoothingTimeConstant = 0.8;
69
+
70
+ drawVisualizer();
71
+ }
72
+
73
+ function drawVisualizer() {
74
+ if (!visualizer.ctx) return;
75
+
76
+ const { ctx, canvas, analyser } = visualizer;
77
+ const width = canvas.width / (window.devicePixelRatio || 1);
78
+ const height = canvas.height / (window.devicePixelRatio || 1);
79
+
80
+ ctx.fillStyle = 'rgba(0, 0, 0, 0.2)';
81
+ ctx.fillRect(0, 0, width, height);
82
+
83
+ if (isPlaying && analyser) {
84
+ const bufferLength = analyser.frequencyBinCount;
85
+ const dataArray = new Uint8Array(bufferLength);
86
+ analyser.getByteFrequencyData(dataArray);
87
+
88
+ const barWidth = (width / bufferLength) * 2.5;
89
+ let barHeight;
90
+ let x = 0;
91
+
92
+ for (let i = 0; i < bufferLength; i++) {
93
+ barHeight = (dataArray[i] / 255) * height * 0.8;
94
+
95
+ // Create gradient for bars
96
+ const gradient = ctx.createLinearGradient(0, height, 0, height - barHeight);
97
+ gradient.addColorStop(0, 'rgba(6, 182, 212, 0.8)'); // Cyan
98
+ gradient.addColorStop(0.5, 'rgba(168, 85, 247, 0.8)'); // Purple
99
+ gradient.addColorStop(1, 'rgba(236, 72, 153, 0.8)'); // Pink
100
+
101
+ ctx.fillStyle = gradient;
102
+ ctx.fillRect(x, height - barHeight, barWidth, barHeight);
103
+
104
+ // Add glow effect
105
+ ctx.shadowBlur = 10;
106
+ ctx.shadowColor = 'rgba(236, 72, 153, 0.5)';
107
+
108
+ x += barWidth + 1;
109
+ }
110
+ ctx.shadowBlur = 0;
111
+ } else {
112
+ // Idle animation
113
+ const time = Date.now() * 0.001;
114
+ ctx.strokeStyle = 'rgba(6, 182, 212, 0.3)';
115
+ ctx.lineWidth = 2;
116
+ ctx.beginPath();
117
+
118
+ for (let x = 0; x < width; x++) {
119
+ const y = height / 2 + Math.sin(x * 0.01 + time) * 20 * Math.sin(time * 0.5);
120
+ if (x === 0) ctx.moveTo(x, y);
121
+ else ctx.lineTo(x, y);
122
+ }
123
+ ctx.stroke();
124
+ }
125
+
126
+ visualizer.animationId = requestAnimationFrame(drawVisualizer);
127
+ }
128
+
129
+ // Binaural Beat Generation
130
+ function createBinauralBeats() {
131
+ const carrier = parseFloat(document.getElementById('carrierSlider').value);
132
+ const beat = parseFloat(document.getElementById('beatSlider').value);
133
+
134
+ // Cleanup existing
135
+ stopBinaural();
136
+
137
+ // Create nodes
138
+ binauralNodes.masterGain = audioContext.createGain();
139
+ binauralNodes.masterGain.connect(audioContext.destination);
140
+ binauralNodes.masterGain.connect(visualizer.analyser);
141
+
142
+ if (currentMode === 'spatial') {
143
+ // Spatial/Binaural mode: Different frequencies per ear
144
+ binauralNodes.leftOsc = audioContext.createOscillator();
145
+ binauralNodes.rightOsc = audioContext.createOscillator();
146
+ binauralNodes.leftGain = audioContext.createGain();
147
+ binauralNodes.rightGain = audioContext.createGain();
148
+ binauralNodes.merger = audioContext.createChannelMerger(2);
149
+
150
+ // Left ear: carrier
151
+ binauralNodes.leftOsc.frequency.value = carrier;
152
+ binauralNodes.leftOsc.connect(binauralNodes.leftGain);
153
+ binauralNodes.leftGain.connect(binauralNodes.merger, 0, 0);
154
+
155
+ // Right ear: carrier + beat
156
+ binauralNodes.rightOsc.frequency.value = carrier + beat;
157
+ binauralNodes.rightOsc.connect(binauralNodes.rightGain);
158
+ binauralNodes.rightGain.connect(binauralNodes.merger, 0, 1);
159
+
160
+ binauralNodes.merger.connect(binauralNodes.masterGain);
161
+
162
+ binauralNodes.leftOsc.start();
163
+ binauralNodes.rightOsc.start();
164
+ } else {
165
+ // Monaural mode: Amplitude modulation at beat frequency
166
+ binauralNodes.leftOsc = audioContext.createOscillator();
167
+ binauralNodes.modulator = audioContext.createOscillator();
168
+ binauralNodes.modulatorGain = audioContext.createGain();
169
+
170
+ // Carrier tone
171
+ binauralNodes.leftOsc.frequency.value = carrier;
172
+
173
+ // Modulator for amplitude
174
+ binauralNodes.modulator.frequency.value = beat;
175
+ binauralNodes.modulatorGain.gain.value = 0.5; // Modulation depth
176
+
177
+ // Connect modulator to gain
178
+ binauralNodes.modulator.connect(binauralNodes.modulatorGain.gain);
179
+
180
+ // Create constant offset for gain
181
+ const constantOffset = audioContext.createGain();
182
+ constantOffset.gain.value = 0.5;
183
+ binauralNodes.leftOsc.connect(constantOffset);
184
+ constantOffset.connect(binauralNodes.modulatorGain);
185
+
186
+ // Connect to both channels equally (monaural)
187
+ binauralNodes.merger = audioContext.createChannelMerger(2);
188
+ binauralNodes.modulatorGain.connect(binauralNodes.merger, 0, 0);
189
+ binauralNodes.modulatorGain.connect(binauralNodes.merger, 0, 1);
190
+ binauralNodes.merger.connect(binauralNodes.masterGain);
191
+
192
+ binauralNodes.leftOsc.start();
193
+ binauralNodes.modulator.start();
194
+ }
195
+
196
+ // Apply ramping
197
+ applyRamp(binauralNodes.masterGain.gain, 0.3);
198
+ }
199
+
200
+ function stopBinaural() {
201
+ if (binauralNodes.leftOsc) {
202
+ try { binauralNodes.leftOsc.stop(); } catch(e) {}
203
+ binauralNodes.leftOsc = null;
204
+ }
205
+ if (binauralNodes.rightOsc) {
206
+ try { binauralNodes.rightOsc.stop(); } catch(e) {}
207
+ binauralNodes.rightOsc = null;
208
+ }
209
+ if (binauralNodes.modulator) {
210
+ try { binauralNodes.modulator.stop(); } catch(e) {}
211
+ binauralNodes.modulator = null;
212
+ }
213
+ if (binauralNodes.masterGain) {
214
+ binauralNodes.masterGain.disconnect();
215
+ }
216
+ }
217
+
218
+ // Noise Generation (Always Spatial)
219
+ function createNoise(type) {
220
+ stopNoise();
221
+ activeNoiseType = type;
222
+
223
+ const bufferSize = 2 * audioContext.sampleRate; // 2 seconds buffer
224
+ const numChannels = 2; // Stereo
225
+ const buffer = audioContext.createBuffer(numChannels, bufferSize, audioContext.sampleRate);
226
+
227
+ // Generate noise data
228
+ for (let channel = 0; channel < numChannels; channel++) {
229
+ const data = buffer.getChannelData(channel);
230
+
231
+ if (type === 'white') {
232
+ // White noise: random -1 to 1
233
+ for (let i = 0; i < bufferSize; i++) {
234
+ data[i] = Math.random() * 2 - 1;
235
+ }
236
+ } else if (type === 'pink') {
237
+ // Pink noise: 1/f using Paul Kellet's method
238
+ let b0 = 0, b1 = 0, b2 = 0, b3 = 0, b4 = 0, b5 = 0, b6 = 0;
239
+ for (let i = 0; i < bufferSize; i++) {
240
+ const white = Math.random() * 2 - 1;
241
+ b0 = 0.99886 * b0 + white * 0.0555179;
242
+ b1 = 0.99332 * b1 + white * 0.0750759;
243
+ b2 = 0.96900 * b2 + white * 0.1538520;
244
+ b3 = 0.86650 * b3 + white * 0.3104856;
245
+ b4 = 0.55000 * b4 + white * 0.5329522;
246
+ b5 = -0.7616 * b5 - white * 0.0168980;
247
+ data[i] = b0 + b1 + b2 + b3 + b4 + b5 + b6 + white * 0.5362;
248
+ data[i] *= 0.11; // Normalize
249
+ b6 = white * 0.115926;
250
+ }
251
+ } else if (type === 'brown') {
252
+ // Brown noise: 1/f² (integration of white noise)
253
+ let lastOut = 0;
254
+ for (let i = 0; i < bufferSize; i++) {
255
+ const white = Math.random() * 2 - 1;
256
+ data[i] = (lastOut + (0.02 * white)) / 1.02;
257
+ lastOut = data[i];
258
+ data[i] *= 3.5; // Normalize
259
+ }
260
+ }
261
+ }
262
+
263
+ // Create spatial setup
264
+ noiseNodes.masterGain = audioContext.createGain();
265
+ noiseNodes.leftSource = audioContext.createBufferSource();
266
+ noiseNodes.rightSource = audioContext.createBufferSource();
267
+ noiseNodes.leftGain = audioContext.createGain();
268
+ noiseNodes.rightGain = audioContext.createGain();
269
+ noiseNodes.leftPanner = audioContext.createStereoPanner();
270
+ noiseNodes.rightPanner = audioContext.createStereoPanner();
271
+
272
+ // Set up looping
273
+ noiseNodes.leftSource.buffer = buffer;
274
+ noiseNodes.rightSource.buffer = buffer;
275
+ noiseNodes.leftSource.loop = true;
276
+ noiseNodes.rightSource.loop = true;
277
+
278
+ // Get spatial width
279
+ const spatialWidth = parseInt(document.getElementById('spatialWidthSlider').value) / 100;
280
+ const panValue = spatialWidth; // 0 = center, 1 = full side
281
+
282
+ // Left channel fully left
283
+ noiseNodes.leftPanner.pan.value = -panValue;
284
+ noiseNodes.leftSource.connect(noiseNodes.leftGain);
285
+ noiseNodes.leftGain.connect(noiseNodes.leftPanner);
286
+ noiseNodes.leftPanner.connect(noiseNodes.masterGain);
287
+
288
+ // Right channel fully right (with slight offset for true spatial feel)
289
+ noiseNodes.rightPanner.pan.value = panValue;
290
+ noiseNodes.rightSource.connect(noiseNodes.rightGain);
291
+ noiseNodes.rightGain.connect(noiseNodes.rightPanner);
292
+ noiseNodes.rightPanner.connect(noiseNodes.masterGain);
293
+
294
+ // Add slight delay to right channel for enhanced spatialization
295
+ if (spatialWidth > 0.5) {
296
+ const delay = audioContext.createDelay();
297
+ delay.delayTime.value = 0.01; // 10ms delay
298
+ noiseNodes.rightPanner.disconnect();
299
+ noiseNodes.rightPanner.connect(delay);
300
+ delay.connect(noiseNodes.masterGain);
301
+ }
302
+
303
+ noiseNodes.masterGain.connect(audioContext.destination);
304
+ noiseNodes.masterGain.connect(visualizer.analyser);
305
+
306
+ // Set volume
307
+ const volume = parseInt(document.getElementById('noiseVolumeSlider').value) / 100;
308
+ noiseNodes.masterGain.gain.value = volume;
309
+
310
+ noiseNodes.leftSource.start();
311
+ noiseNodes.rightSource.start();
312
+
313
+ // Add slight variation between channels for true spatial effect
314
+ // By starting at slightly different offsets
315
+ noiseNodes.rightSource.playbackRate.value = 0.999; // Slight pitch difference creates beating
316
+
317
+ updateNoiseButtonStates();
318
+ }
319
+
320
+ function stopNoise() {
321
+ if (noiseNodes.leftSource) {
322
+ try { noiseNodes.leftSource.stop(); } catch(e) {}
323
+ noiseNodes.leftSource = null;
324
+ }
325
+ if (noiseNodes.rightSource) {
326
+ try { noiseNodes.rightSource.stop(); } catch(e) {}
327
+ noiseNodes.rightSource = null;
328
+ }
329
+ if (noiseNodes.masterGain) {
330
+ noiseNodes.masterGain.disconnect();
331
+ }
332
+ activeNoiseType = null;
333
+ updateNoiseButtonStates();
334
+ }
335
+
336
+ function updateNoiseButtonStates() {
337
+ const buttons = ['pinkNoiseBtn', 'brownNoiseBtn', 'whiteNoiseBtn'];
338
+ const types = ['pink', 'brown', 'white'];
339
+
340
+ buttons.forEach((id, index) => {
341
+ const btn = document.getElementById(id);
342
+ if (activeNoiseType === types[index]) {
343
+ btn.classList.add('playing');
344
+ btn.querySelector('i[data-lucide="play-circle"]').setAttribute('data-lucide', 'stop-circle');
345
+ } else {
346
+ btn.classList.remove('playing');
347
+ btn.querySelector('i[data-lucide="stop-circle"]')?.setAttribute('data-lucide', 'play-circle');
348
+ }
349
+ });
350
+
351
+ lucide.createIcons();
352
+ }
353
+
354
+ // Ramping Functionality
355
+ function applyRamp(gainNode, targetValue) {
356
+ const now = audioContext.currentTime;
357
+
358
+ if (rampMode === 'none') {
359
+ gainNode.setValueAtTime(targetValue, now);
360
+ } else if (rampMode === 'up') {
361
+ gainNode.setValueAtTime(0.001, now);
362
+ gainNode.exponentialRampToValueAtTime(targetValue, now + rampDuration);
363
+ } else if (rampMode === 'down') {
364
+ gainNode.setValueAtTime(targetValue, now);
365
+ gainNode.exponentialRampToValueAtTime(0.001, now + rampDuration);
366
+ // Stop after ramp down
367
+ setTimeout(() => {
368
+ stopAll();
369
+ }, rampDuration * 1000);
370
+ }
371
+ }
372
+
373
+ function stopAll() {
374
+ stopBinaural();
375
+ stopNoise();
376
+ isPlaying = false;
377
+ updatePlayButton();
378
+ }
379
+
380
+ function updatePlayButton() {
381
+ const btn = document.getElementById('playBinauralBtn');
382
+ const stopBtn = document.getElementById('stopAllBtn');
383
+
384
+ if (isPlaying) {
385
+ btn.innerHTML = '<i data-lucide="pause" class="w-6 h-6"></i><span>Stop Binaural</span>';
386
+ btn.classList.remove('from-cyan-400', 'via-purple-500', 'to-pink-500');
387
+ btn.classList.add('from-red-400', 'via-red-500', 'to-rose-600');
388
+
389
+ stopBtn.disabled = false;
390
+ stopBtn.classList.remove('opacity-50', 'cursor-not-allowed');
391
+ } else {
392
+ btn.innerHTML = '<i data-lucide="play" class="w-6 h-6"></i><span>Generate Binaural Beat</span>';
393
+ btn.classList.add('from-cyan-400', 'via-purple-500', 'to-pink-500');
394
+ btn.classList.remove('from-red-400', 'via-red-500', 'to-rose-600');
395
+
396
+ stopBtn.disabled = true;
397
+ stopBtn.classList.add('opacity-50', 'cursor-not-allowed');
398
+ }
399
+ lucide.createIcons();
400
+ }
401
+
402
+ // Event Listeners
403
+ document.addEventListener('DOMContentLoaded', () => {
404
+ // Mode selection
405
+ document.getElementById('spatialBtn').addEventListener('click', () => {
406
+ currentMode = 'spatial';
407
+ document.getElementById('spatialBtn').classList.add('active', 'from-cyan-500', 'to-blue-500', 'text-white');
408
+ document.getElementById('spatialBtn').classList.remove('bg-white/10', 'text-purple-200');
409
+ document.getElementById('monauralBtn').classList.remove('active', 'from-purple-500', 'to-pink-500', 'text-white');
410
+ document.getElementById('monauralBtn').classList.add('bg-white/10', 'text-purple-200');
411
+ document.getElementById('modeDescription').textContent = 'Different frequencies in each ear create the beat perception';
412
+ });
413
+
414
+ document.getElementById('monauralBtn').addEventListener('click', () => {
415
+ currentMode = 'monaural';
416
+ document.getElementById('monauralBtn').classList.add('active', 'from-purple-500', 'to-pink-500', 'text-white');
417
+ document.getElementById('monauralBtn').classList.remove('bg-white/10', 'text-purple-200');
418
+ document.getElementById('spatialBtn').classList.remove('active', 'from-cyan-500', 'to-blue-500', 'text-white');
419
+ document.getElementById('spatialBtn').classList.add('bg-white/10', 'text-purple-200');
420
+ document.getElementById('modeDescription').textContent = 'Same frequency in both ears with amplitude modulation';
421
+ });
422
+
423
+ // Ramp selection
424
+ const rampButtons = {
425
+ 'rampNone': 'none',
426
+ 'rampUp': 'up',
427
+ 'rampDown': 'down'
428
+ };
429
+
430
+ Object.keys(rampButtons).forEach(id => {
431
+ document.getElementById(id).addEventListener('click', () => {
432
+ rampMode = rampButtons[id];
433
+ Object.keys(rampButtons).forEach(btnId => {
434
+ const btn = document.getElementById(btnId);
435
+ if (btnId === id) {
436
+ btn.classList.add('active', 'from-purple-500', 'to-pink-500', 'text-white');
437
+ btn.classList.remove('bg-white/10', 'text-purple-200');
438
+ } else {
439
+ btn.classList.remove('active', 'from-purple-500', 'to-pink-500', 'text-white');
440
+ btn.classList.add('bg-white/10', 'text-purple-200');
441
+ }
442
+ });
443
+ });
444
+ });
445
+
446
+ // Sliders
447
+ document.getElementById('carrierSlider').addEventListener('input', (e) => {
448
+ document.getElementById('carrierValue').textContent = e.target.value + ' Hz';
449
+ if (isPlaying && currentMode === 'spatial') {
450
+ const beat = parseFloat(document.getElementById('beatSlider').value);
451
+ binauralNodes.leftOsc.frequency.setValueAtTime(parseFloat(e.target.value), audioContext.currentTime);
452
+ binauralNodes.rightOsc.frequency.setValueAtTime(parseFloat(e.target.value) + beat, audioContext.currentTime);
453
+ } else if (isPlaying) {
454
+ binauralNodes.leftOsc.frequency.setValueAtTime(parseFloat(e.target.value), audioContext.currentTime);
455
+ }
456
+ });
457
+
458
+ document.getElementById('beatSlider').addEventListener('input', (e) => {
459
+ document.getElementById('beatValue').textContent = e.target.value + ' Hz';
460
+ if (isPlaying) {
461
+ const carrier = parseFloat(document.getElementById('carrierSlider').value);
462
+ const beat = parseFloat(e.target.value);
463
+ if (currentMode === 'spatial') {
464
+ binauralNodes.rightOsc.frequency.setValueAtTime(carrier + beat, audioContext.currentTime);
465
+ } else {
466
+ binauralNodes.modulator.frequency.setValueAtTime(beat, audioContext.currentTime);
467
+ }
468
+ }
469
+ });
470
+
471
+ document.getElementById('rampDuration').addEventListener('input', (e) => {
472
+ rampDuration = parseInt(e.target.value);
473
+ document.getElementById('rampDurationValue').textContent = rampDuration + 's';
474
+ });
475
+
476
+ document.getElementById('noiseVolumeSlider').addEventListener('input', (e) => {
477
+ const value = e.target.value;
478
+ document.getElementById('noiseVolumeValue').textContent = value + '%';
479
+ if (noiseNodes.masterGain) {
480
+ noiseNodes.masterGain.gain.setValueAtTime(value / 100, audioContext.currentTime);
481
+ }
482
+ });
483
+
484
+ document.getElementById('spatialWidthSlider').addEventListener('input', (e) => {
485
+ const value = parseInt(e.target.value);
486
+ const label = value < 30 ? 'Narrow' : value < 70 ? 'Medium' : 'Wide';
487
+ document.getElementById('spatialWidthValue').textContent = label;
488
+
489
+ if (activeNoiseType) {
490
+ // Recreate noise with new spatial settings
491
+ createNoise(activeNoiseType);
492
+ }
493
+ });
494
+
495
+ // Brainwave presets
496
+ document.querySelectorAll('.brainwave-btn').forEach(btn => {
497
+ btn.addEventListener('click', () => {
498
+ const carrier = btn.dataset.carrier;
499
+ const beat = btn.dataset.beat;
500
+ document.getElementById('carrierSlider').value = carrier;
501
+ document.getElementById('beatSlider').value = beat;
502
+ document.getElementById('carrierValue').textContent = carrier + ' Hz';
503
+ document.getElementById('beatValue').textContent = beat + ' Hz';
504
+
505
+ // Visual feedback
506
+ btn.style.transform = 'scale(0.95)';
507
+ setTimeout(() => btn.style.transform = '', 100);
508
+ });
509
+ });
510
+
511
+ // Play buttons
512
+ document.getElementById('playBinauralBtn').addEventListener('click', () => {
513
+ initAudioContext();
514
+ if (isPlaying) {
515
+ stopBinaural();
516
+ isPlaying = false;
517
+ } else {
518
+ createBinauralBeats();
519
+ isPlaying = true;
520
+ }
521
+ updatePlayButton();
522
+ });
523
+
524
+ // Noise buttons (toggle behavior)
525
+ document.getElementById('pinkNoiseBtn').addEventListener('click', () => {
526
+ initAudioContext();
527
+ if (activeNoiseType === 'pink') {
528
+ stopNoise();
529
+ } else {
530
+ createNoise('pink');
531
+ }
532
+ });
533
+
534
+ document.getElementById('brownNoiseBtn').addEventListener('click', () => {
535
+ initAudioContext();
536
+ if (activeNoiseType === 'brown') {
537
+ stopNoise();
538
+ } else {
539
+ createNoise('brown');
540
+ }
541
+ });
542
+
543
+ document.getElementById('whiteNoiseBtn').addEventListener('click', () => {
544
+ initAudioContext();
545
+ if (activeNoiseType === 'white') {
546
+ stopNoise();
547
+ } else {
548
+ createNoise('white');
549
+ }
550
+ });
551
+
552
+ document.getElementById('stopAllBtn').addEventListener('click', stopAll);
553
+
554
+ // Particle background animation
555
+ initParticles();
556
+ });
557
+
558
+ // Particle System
559
+ function initParticles() {
560
+ const canvas = document.getElementById('particleCanvas');
561
+ const ctx = canvas.getContext('2d');
562
+
563
+ function resize() {
564
+ canvas.width = window.innerWidth;
565
+ canvas.height = window.innerHeight;
566
+ }
567
+ resize();
568
+ window.addEventListener('resize', resize);
569
+
570
+ const particles = [];
571
+ const particleCount = 50;
572
+
573
+ for (let i = 0; i < particleCount; i++) {
574
+ particles.push({
575
+ x: Math.random() * canvas.width,
576
+ y: Math.random() * canvas.height,
577
+ radius: Math.random() * 3 + 1,
578
+ vx: (Math.random() - 0.5) * 0.5,
579
+ vy: (Math.random() - 0.5) * 0.5,
580
+ color: `hsla(${Math.random() * 60 + 240}, 70%, 60%, ${Math.random() * 0.3})`
581
+ });
582
+ }
583
+
584
+ function animate() {
585
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
586
+
587
+ particles.forEach(p => {
588
+ p.x += p.vx;
589
+ p.y += p.vy;
590
+
591
+ if (p.x < 0 || p.x > canvas.width) p.vx *= -1;
592
+ if (p.y < 0 || p.y > canvas.height) p.vy *= -1;
593
+
594
+ ctx.beginPath();
595
+ ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2);
596
+ ctx.fillStyle = p.color;
597
+ ctx.fill();
598
+ });
599
+
600
+ // Connect nearby particles
601
+ particles.forEach((p1, i) => {
602
+ particles.slice(i + 1).forEach(p2 => {
603
+ const dx = p1.x - p2.x;
604
+ const dy = p1.y - p2.y;
605
+ const distance = Math.sqrt(dx * dx + dy * dy);
606
+
607
+ if (distance < 100) {
608
+ ctx.beginPath();
609
+ ctx.moveTo(p1.x, p1.y);
610
+ ctx.lineTo(p2.x, p2.y);
611
+ ctx.strokeStyle = `rgba(147, 51, 234, ${0.1 * (1 - distance / 100)})`;
612
+ ctx.stroke();
613
+ }
614
+ });
615
+ });
616
+
617
+ requestAnimationFrame(animate);
618
+ }
619
+ animate();
620
+ }
621
+
622
+ // Cleanup on page unload
623
+ window.addEventListener('beforeunload', () => {
624
+ stopAll();
625
+ if (audioContext) {
626
+ audioContext.close();
627
+ }
628
+ });
style.css CHANGED
@@ -1,28 +1,162 @@
1
- body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
28
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Custom animations and psychedelic effects */
2
+ @keyframes gradient {
3
+ 0% { background-position: 0% 50%; }
4
+ 50% { background-position: 100% 50%; }
5
+ 100% { background-position: 0% 50%; }
6
  }
7
 
8
+ @keyframes pulse-slow {
9
+ 0%, 100% { opacity: 0.3; transform: scale(1); }
10
+ 50% { opacity: 0.5; transform: scale(1.1); }
11
  }
12
 
13
+ @keyframes text-shimmer {
14
+ 0% { background-position: -200% center; }
15
+ 100% { background-position: 200% center; }
 
 
16
  }
17
 
18
+ @keyframes float {
19
+ 0%, 100% { transform: translateY(0px); }
20
+ 50% { transform: translateY(-10px); }
 
 
 
21
  }
22
 
23
+ .animate-gradient {
24
+ background-size: 400% 400%;
25
+ animation: gradient 15s ease infinite;
26
  }
27
+
28
+ .animate-pulse-slow {
29
+ animation: pulse-slow 8s ease-in-out infinite;
30
+ }
31
+
32
+ .animate-text-shimmer {
33
+ background-size: 200% auto;
34
+ animation: text-shimmer 5s linear infinite;
35
+ }
36
+
37
+ /* Glassmorphism panels */
38
+ .glass-panel {
39
+ background: rgba(20, 20, 40, 0.6);
40
+ backdrop-filter: blur(12px);
41
+ -webkit-backdrop-filter: blur(12px);
42
+ box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
43
+ }
44
+
45
+ /* Custom range slider styling */
46
+ input[type="range"] {
47
+ -webkit-appearance: none;
48
+ background: transparent;
49
+ }
50
+
51
+ input[type="range"]::-webkit-slider-thumb {
52
+ -webkit-appearance: none;
53
+ height: 20px;
54
+ width: 20px;
55
+ border-radius: 50%;
56
+ background: linear-gradient(135deg, #06b6d4, #3b82f6);
57
+ cursor: pointer;
58
+ box-shadow: 0 0 10px rgba(6, 182, 212, 0.5), 0 0 20px rgba(6, 182, 212, 0.3);
59
+ margin-top: -8px;
60
+ border: 2px solid white;
61
+ transition: transform 0.2s;
62
+ }
63
+
64
+ input[type="range"]::-webkit-slider-thumb:hover {
65
+ transform: scale(1.2);
66
+ box-shadow: 0 0 15px rgba(6, 182, 212, 0.8), 0 0 30px rgba(6, 182, 212, 0.5);
67
+ }
68
+
69
+ input[type="range"]::-webkit-slider-runnable-track {
70
+ width: 100%;
71
+ height: 4px;
72
+ cursor: pointer;
73
+ background: rgba(0, 0, 0, 0.3);
74
+ border-radius: 2px;
75
+ border: 1px solid rgba(255, 255, 255, 0.1);
76
+ }
77
+
78
+ input[type="range"]:focus {
79
+ outline: none;
80
+ }
81
+
82
+ /* Pink accent for beat slider */
83
+ #beatSlider::-webkit-slider-thumb {
84
+ background: linear-gradient(135deg, #ec4899, #f472b6);
85
+ box-shadow: 0 0 10px rgba(236, 72, 153, 0.5), 0 0 20px rgba(236, 72, 153, 0.3);
86
+ }
87
+
88
+ #beatSlider::-webkit-slider-thumb:hover {
89
+ box-shadow: 0 0 15px rgba(236, 72, 153, 0.8), 0 0 30px rgba(236, 72, 153, 0.5);
90
+ }
91
+
92
+ /* Amber accent for noise */
93
+ #noiseVolumeSlider::-webkit-slider-thumb,
94
+ #spatialWidthSlider::-webkit-slider-thumb {
95
+ background: linear-gradient(135deg, #f59e0b, #fbbf24);
96
+ box-shadow: 0 0 10px rgba(245, 158, 11, 0.5), 0 0 20px rgba(245, 158, 11, 0.3);
97
+ }
98
+
99
+ /* Button glow effects */
100
+ .mode-btn.active {
101
+ box-shadow: 0 0 20px rgba(6, 182, 212, 0.5), inset 0 0 10px rgba(255, 255, 255, 0.2);
102
+ transform: scale(1.05);
103
+ }
104
+
105
+ .ramp-btn.active {
106
+ box-shadow: 0 0 20px rgba(168, 85, 247, 0.5), inset 0 0 10px rgba(255, 255, 255, 0.2);
107
+ }
108
+
109
+ .noise-btn.playing {
110
+ box-shadow: 0 0 30px currentColor;
111
+ animation: pulse-border 2s ease-in-out infinite;
112
+ }
113
+
114
+ @keyframes pulse-border {
115
+ 0%, 100% { border-color: rgba(255, 255, 255, 0.5); }
116
+ 50% { border-color: rgba(255, 255, 255, 1); }
117
+ }
118
+
119
+ /* Brainwave button effects */
120
+ .brainwave-btn {
121
+ transition: all 0.3s ease;
122
+ }
123
+
124
+ .brainwave-btn:hover {
125
+ transform: translateY(-2px);
126
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
127
+ }
128
+
129
+ /* Responsive adjustments */
130
+ @media (max-width: 768px) {
131
+ .glass-panel {
132
+ padding: 1.5rem;
133
+ }
134
+
135
+ h1 {
136
+ font-size: 2.5rem;
137
+ }
138
+ }
139
+
140
+ /* Scrollbar styling */
141
+ ::-webkit-scrollbar {
142
+ width: 10px;
143
+ }
144
+
145
+ ::-webkit-scrollbar-track {
146
+ background: rgba(0, 0, 0, 0.3);
147
+ }
148
+
149
+ ::-webkit-scrollbar-thumb {
150
+ background: linear-gradient(180deg, #06b6d4, #ec4899);
151
+ border-radius: 5px;
152
+ }
153
+
154
+ ::-webkit-scrollbar-thumb:hover {
155
+ background: linear-gradient(180deg, #22d3ee, #f472b6);
156
+ }
157
+
158
+ /* Selection styling */
159
+ ::selection {
160
+ background: rgba(236, 72, 153, 0.4);
161
+ color: white;
162
+ }