SREAL commited on
Commit
d4e9dbb
·
verified ·
1 Parent(s): a3230d6

I want you to make a tone.JS based synthesizer with 3 oscillators, ADSR, an X/Y modulation matrix and a large variety of effects with all relevant parameters. give it a graphic interface that is reminiscent of the original XBOX GUI.

Browse files
Files changed (4) hide show
  1. README.md +8 -5
  2. index.html +433 -19
  3. script.js +406 -0
  4. style.css +163 -18
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Synthx Xbox Edition
3
- emoji: 🐠
4
- colorFrom: blue
5
- colorTo: purple
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: SynthX - XBOX Edition 🎮
3
+ colorFrom: purple
4
+ colorTo: gray
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
index.html CHANGED
@@ -1,19 +1,433 @@
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>SynthX - XBOX Edition</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js"></script>
10
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
11
+ <script src="https://unpkg.com/feather-icons"></script>
12
+ </head>
13
+ <body class="bg-gray-900 text-white min-h-screen font-sans">
14
+ <!-- Header -->
15
+ <header class="p-4 border-b-2 border-green-500 flex justify-between items-center">
16
+ <h1 class="text-2xl font-bold text-green-400 flex items-center">
17
+ <i data-feather="music" class="mr-2"></i> SynthX - XBOX Edition
18
+ </h1>
19
+ <div class="flex space-x-4">
20
+ <button id="save-preset" class="px-4 py-2 bg-green-600 rounded hover:bg-green-700 transition">Save Preset</button>
21
+ <button id="load-preset" class="px-4 py-2 bg-blue-600 rounded hover:bg-blue-700 transition">Load Preset</button>
22
+ </div>
23
+ </header>
24
+
25
+ <main class="container mx-auto p-4">
26
+ <!-- Oscillators Section -->
27
+ <section class="mb-8">
28
+ <h2 class="text-xl font-bold mb-4 text-green-400 border-b border-green-500 pb-2">Oscillators</h2>
29
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
30
+ <!-- Oscillator 1 -->
31
+ <div class="bg-gray-800 p-4 rounded-lg border-2 border-green-700">
32
+ <h3 class="font-bold mb-2 text-green-300">OSC 1</h3>
33
+ <div class="space-y-4">
34
+ <div>
35
+ <label class="block text-sm mb-1">Waveform</label>
36
+ <select id="osc1-waveform" class="w-full bg-gray-700 rounded p-2">
37
+ <option value="sine">Sine</option>
38
+ <option value="square">Square</option>
39
+ <option value="sawtooth">Sawtooth</option>
40
+ <option value="triangle">Triangle</option>
41
+ </select>
42
+ </div>
43
+ <div>
44
+ <label class="block text-sm mb-1">Frequency</label>
45
+ <input type="range" id="osc1-frequency" min="20" max="2000" value="440" class="w-full">
46
+ <span id="osc1-freq-value" class="text-xs">440 Hz</span>
47
+ </div>
48
+ <div>
49
+ <label class="block text-sm mb-1">Detune</label>
50
+ <input type="range" id="osc1-detune" min="-50" max="50" value="0" class="w-full">
51
+ <span id="osc1-detune-value" class="text-xs">0 cents</span>
52
+ </div>
53
+ <div>
54
+ <label class="block text-sm mb-1">Volume</label>
55
+ <input type="range" id="osc1-volume" min="-60" max="0" value="0" class="w-full">
56
+ <span id="osc1-vol-value" class="text-xs">0 dB</span>
57
+ </div>
58
+ </div>
59
+ </div>
60
+
61
+ <!-- Oscillator 2 -->
62
+ <div class="bg-gray-800 p-4 rounded-lg border-2 border-green-700">
63
+ <h3 class="font-bold mb-2 text-green-300">OSC 2</h3>
64
+ <div class="space-y-4">
65
+ <div>
66
+ <label class="block text-sm mb-1">Waveform</label>
67
+ <select id="osc2-waveform" class="w-full bg-gray-700 rounded p-2">
68
+ <option value="sine">Sine</option>
69
+ <option value="square">Square</option>
70
+ <option value="sawtooth">Sawtooth</option>
71
+ <option value="triangle">Triangle</option>
72
+ </select>
73
+ </div>
74
+ <div>
75
+ <label class="block text-sm mb-1">Frequency</label>
76
+ <input type="range" id="osc2-frequency" min="20" max="2000" value="440" class="w-full">
77
+ <span id="osc2-freq-value" class="text-xs">440 Hz</span>
78
+ </div>
79
+ <div>
80
+ <label class="block text-sm mb-1">Detune</label>
81
+ <input type="range" id="osc2-detune" min="-50" max="50" value="0" class="w-full">
82
+ <span id="osc2-detune-value" class="text-xs">0 cents</span>
83
+ </div>
84
+ <div>
85
+ <label class="block text-sm mb-1">Volume</label>
86
+ <input type="range" id="osc2-volume" min="-60" max="0" value="0" class="w-full">
87
+ <span id="osc2-vol-value" class="text-xs">0 dB</span>
88
+ </div>
89
+ </div>
90
+ </div>
91
+
92
+ <!-- Oscillator 3 -->
93
+ <div class="bg-gray-800 p-4 rounded-lg border-2 border-green-700">
94
+ <h3 class="font-bold mb-2 text-green-300">OSC 3</h3>
95
+ <div class="space-y-4">
96
+ <div>
97
+ <label class="block text-sm mb-1">Waveform</label>
98
+ <select id="osc3-waveform" class="w-full bg-gray-700 rounded p-2">
99
+ <option value="sine">Sine</option>
100
+ <option value="square">Square</option>
101
+ <option value="sawtooth">Sawtooth</option>
102
+ <option value="triangle">Triangle</option>
103
+ </select>
104
+ </div>
105
+ <div>
106
+ <label class="block text-sm mb-1">Frequency</label>
107
+ <input type="range" id="osc3-frequency" min="20" max="2000" value="440" class="w-full">
108
+ <span id="osc3-freq-value" class="text-xs">440 Hz</span>
109
+ </div>
110
+ <div>
111
+ <label class="block text-sm mb-1">Detune</label>
112
+ <input type="range" id="osc3-detune" min="-50" max="50" value="0" class="w-full">
113
+ <span id="osc3-detune-value" class="text-xs">0 cents</span>
114
+ </div>
115
+ <div>
116
+ <label class="block text-sm mb-1">Volume</label>
117
+ <input type="range" id="osc3-volume" min="-60" max="0" value="0" class="w-full">
118
+ <span id="osc3-vol-value" class="text-xs">0 dB</span>
119
+ </div>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ </section>
124
+
125
+ <!-- ADSR Envelope -->
126
+ <section class="mb-8">
127
+ <h2 class="text-xl font-bold mb-4 text-green-400 border-b border-green-500 pb-2">ADSR Envelope</h2>
128
+ <div class="bg-gray-800 p-6 rounded-lg border-2 border-green-700">
129
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-6">
130
+ <div>
131
+ <label class="block text-sm mb-1">Attack</label>
132
+ <input type="range" id="attack" min="0.01" max="5" step="0.01" value="0.1" class="w-full">
133
+ <span id="attack-value" class="text-xs">0.1 s</span>
134
+ </div>
135
+ <div>
136
+ <label class="block text-sm mb-1">Decay</label>
137
+ <input type="range" id="decay" min="0.01" max="5" step="0.01" value="0.3" class="w-full">
138
+ <span id="decay-value" class="text-xs">0.3 s</span>
139
+ </div>
140
+ <div>
141
+ <label class="block text-sm mb-1">Sustain</label>
142
+ <input type="range" id="sustain" min="0" max="1" step="0.01" value="0.7" class="w-full">
143
+ <span id="sustain-value" class="text-xs">70%</span>
144
+ </div>
145
+ <div>
146
+ <label class="block text-sm mb-1">Release</label>
147
+ <input type="range" id="release" min="0.01" max="10" step="0.01" value="0.5" class="w-full">
148
+ <span id="release-value" class="text-xs">0.5 s</span>
149
+ </div>
150
+ </div>
151
+ <div class="mt-6">
152
+ <canvas id="adsr-canvas" width="800" height="200" class="w-full bg-gray-900 rounded"></canvas>
153
+ </div>
154
+ </div>
155
+ </section>
156
+
157
+ <!-- X/Y Modulation Matrix -->
158
+ <section class="mb-8">
159
+ <h2 class="text-xl font-bold mb-4 text-green-400 border-b border-green-500 pb-2">Modulation Matrix</h2>
160
+ <div class="bg-gray-800 p-6 rounded-lg border-2 border-green-700">
161
+ <div class="flex flex-col md:flex-row gap-8">
162
+ <div class="flex-1">
163
+ <h3 class="font-bold mb-2 text-green-300">X/Y Pad</h3>
164
+ <div id="xy-pad" class="relative w-full h-64 bg-gray-900 border-2 border-green-600 rounded cursor-pointer">
165
+ <div id="xy-handle" class="absolute w-6 h-6 bg-green-500 rounded-full -ml-3 -mt-3 transform -translate-x-1/2 -translate-y-1/2"></div>
166
+ </div>
167
+ <div class="flex justify-between mt-2 text-xs">
168
+ <span>X: <span id="x-value">0.5</span></span>
169
+ <span>Y: <span id="y-value">0.5</span></span>
170
+ </div>
171
+ </div>
172
+ <div class="flex-1">
173
+ <h3 class="font-bold mb-2 text-green-300">Modulation Sources</h3>
174
+ <div class="grid grid-cols-2 gap-4">
175
+ <div>
176
+ <label class="block text-sm mb-1">Source X</label>
177
+ <select class="w-full bg-gray-700 rounded p-2">
178
+ <option>LFO 1</option>
179
+ <option>LFO 2</option>
180
+ <option>Envelope</option>
181
+ <option>Velocity</option>
182
+ </select>
183
+ </div>
184
+ <div>
185
+ <label class="block text-sm mb-1">Destination X</label>
186
+ <select class="w-full bg-gray-700 rounded p-2">
187
+ <option>Osc 1 Frequency</option>
188
+ <option>Osc 2 Frequency</option>
189
+ <option>Filter Cutoff</option>
190
+ <option>Amp Volume</option>
191
+ </select>
192
+ </div>
193
+ <div>
194
+ <label class="block text-sm mb-1">Source Y</label>
195
+ <select class="w-full bg-gray-700 rounded p-2">
196
+ <option>LFO 1</option>
197
+ <option>LFO 2</option>
198
+ <option>Envelope</option>
199
+ <option>Velocity</option>
200
+ </select>
201
+ </div>
202
+ <div>
203
+ <label class="block text-sm mb-1">Destination Y</label>
204
+ <select class="w-full bg-gray-700 rounded p-2">
205
+ <option>Osc 1 Frequency</option>
206
+ <option>Osc 2 Frequency</option>
207
+ <option>Filter Cutoff</option>
208
+ <option>Amp Volume</option>
209
+ </select>
210
+ </div>
211
+ </div>
212
+ </div>
213
+ </div>
214
+ </div>
215
+ </section>
216
+
217
+ <!-- Effects Section -->
218
+ <section class="mb-8">
219
+ <h2 class="text-xl font-bold mb-4 text-green-400 border-b border-green-500 pb-2">Effects</h2>
220
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
221
+ <!-- Reverb -->
222
+ <div class="bg-gray-800 p-4 rounded-lg border-2 border-green-700">
223
+ <div class="flex justify-between items-center mb-2">
224
+ <h3 class="font-bold text-green-300">Reverb</h3>
225
+ <label class="switch">
226
+ <input type="checkbox" id="reverb-toggle">
227
+ <span class="slider round"></span>
228
+ </label>
229
+ </div>
230
+ <div class="space-y-3">
231
+ <div>
232
+ <label class="block text-sm mb-1">Decay</label>
233
+ <input type="range" id="reverb-decay" min="0.1" max="10" step="0.1" value="2" class="w-full">
234
+ <span class="text-xs">2 s</span>
235
+ </div>
236
+ <div>
237
+ <label class="block text-sm mb-1">Wet</label>
238
+ <input type="range" id="reverb-wet" min="0" max="1" step="0.01" value="0.3" class="w-full">
239
+ <span class="text-xs">30%</span>
240
+ </div>
241
+ </div>
242
+ </div>
243
+
244
+ <!-- Delay -->
245
+ <div class="bg-gray-800 p-4 rounded-lg border-2 border-green-700">
246
+ <div class="flex justify-between items-center mb-2">
247
+ <h3 class="font-bold text-green-300">Delay</h3>
248
+ <label class="switch">
249
+ <input type="checkbox" id="delay-toggle">
250
+ <span class="slider round"></span>
251
+ </label>
252
+ </div>
253
+ <div class="space-y-3">
254
+ <div>
255
+ <label class="block text-sm mb-1">Time</label>
256
+ <input type="range" id="delay-time" min="0.01" max="2" step="0.01" value="0.5" class="w-full">
257
+ <span class="text-xs">0.5 s</span>
258
+ </div>
259
+ <div>
260
+ <label class="block text-sm mb-1">Feedback</label>
261
+ <input type="range" id="delay-feedback" min="0" max="1" step="0.01" value="0.3" class="w-full">
262
+ <span class="text-xs">30%</span>
263
+ </div>
264
+ <div>
265
+ <label class="block text-sm mb-1">Wet</label>
266
+ <input type="range" id="delay-wet" min="0" max="1" step="0.01" value="0.2" class="w-full">
267
+ <span class="text-xs">20%</span>
268
+ </div>
269
+ </div>
270
+ </div>
271
+
272
+ <!-- Filter -->
273
+ <div class="bg-gray-800 p-4 rounded-lg border-2 border-green-700">
274
+ <div class="flex justify-between items-center mb-2">
275
+ <h3 class="font-bold text-green-300">Filter</h3>
276
+ <label class="switch">
277
+ <input type="checkbox" id="filter-toggle">
278
+ <span class="slider round"></span>
279
+ </label>
280
+ </div>
281
+ <div class="space-y-3">
282
+ <div>
283
+ <label class="block text-sm mb-1">Type</label>
284
+ <select id="filter-type" class="w-full bg-gray-700 rounded p-2">
285
+ <option value="lowpass">Lowpass</option>
286
+ <option value="highpass">Highpass</option>
287
+ <option value="bandpass">Bandpass</option>
288
+ </select>
289
+ </div>
290
+ <div>
291
+ <label class="block text-sm mb-1">Frequency</label>
292
+ <input type="range" id="filter-frequency" min="20" max="20000" value="1000" class="w-full">
293
+ <span class="text-xs">1000 Hz</span>
294
+ </div>
295
+ <div>
296
+ <label class="block text-sm mb-1">Q</label>
297
+ <input type="range" id="filter-q" min="0.1" max="20" step="0.1" value="1" class="w-full">
298
+ <span class="text-xs">1.0</span>
299
+ </div>
300
+ </div>
301
+ </div>
302
+
303
+ <!-- Distortion -->
304
+ <div class="bg-gray-800 p-4 rounded-lg border-2 border-green-700">
305
+ <div class="flex justify-between items-center mb-2">
306
+ <h3 class="font-bold text-green-300">Distortion</h3>
307
+ <label class="switch">
308
+ <input type="checkbox" id="distortion-toggle">
309
+ <span class="slider round"></span>
310
+ </label>
311
+ </div>
312
+ <div class="space-y-3">
313
+ <div>
314
+ <label class="block text-sm mb-1">Drive</label>
315
+ <input type="range" id="distortion-drive" min="0" max="1" step="0.01" value="0.5" class="w-full">
316
+ <span class="text-xs">50%</span>
317
+ </div>
318
+ <div>
319
+ <label class="block text-sm mb-1">Wet</label>
320
+ <input type="range" id="distortion-wet" min="0" max="1" step="0.01" value="0.3" class="w-full">
321
+ <span class="text-xs">30%</span>
322
+ </div>
323
+ </div>
324
+ </div>
325
+
326
+ <!-- Chorus -->
327
+ <div class="bg-gray-800 p-4 rounded-lg border-2 border-green-700">
328
+ <div class="flex justify-between items-center mb-2">
329
+ <h3 class="font-bold text-green-300">Chorus</h3>
330
+ <label class="switch">
331
+ <input type="checkbox" id="chorus-toggle">
332
+ <span class="slider round"></span>
333
+ </label>
334
+ </div>
335
+ <div class="space-y-3">
336
+ <div>
337
+ <label class="block text-sm mb-1">Rate</label>
338
+ <input type="range" id="chorus-rate" min="0.1" max="10" step="0.1" value="2" class="w-full">
339
+ <span class="text-xs">2 Hz</span>
340
+ </div>
341
+ <div>
342
+ <label class="block text-sm mb-1">Depth</label>
343
+ <input type="range" id="chorus-depth" min="0" max="1" step="0.01" value="0.5" class="w-full">
344
+ <span class="text-xs">50%</span>
345
+ </div>
346
+ <div>
347
+ <label class="block text-sm mb-1">Wet</label>
348
+ <input type="range" id="chorus-wet" min="0" max="1" step="0.01" value="0.3" class="w-full">
349
+ <span class="text-xs">30%</span>
350
+ </div>
351
+ </div>
352
+ </div>
353
+
354
+ <!-- Phaser -->
355
+ <div class="bg-gray-800 p-4 rounded-lg border-2 border-green-700">
356
+ <div class="flex justify-between items-center mb-2">
357
+ <h3 class="font-bold text-green-300">Phaser</h3>
358
+ <label class="switch">
359
+ <input type="checkbox" id="phaser-toggle">
360
+ <span class="slider round"></span>
361
+ </label>
362
+ </div>
363
+ <div class="space-y-3">
364
+ <div>
365
+ <label class="block text-sm mb-1">Rate</label>
366
+ <input type="range" id="phaser-rate" min="0.1" max="10" step="0.1" value="2" class="w-full">
367
+ <span class="text-xs">2 Hz</span>
368
+ </div>
369
+ <div>
370
+ <label class="block text-sm mb-1">Depth</label>
371
+ <input type="range" id="phaser-depth" min="0" max="1" step="0.01" value="0.5" class="w-full">
372
+ <span class="text-xs">50%</span>
373
+ </div>
374
+ <div>
375
+ <label class="block text-sm mb-1">Wet</label>
376
+ <input type="range" id="phaser-wet" min="0" max="1" step="0.01" value="0.3" class="w-full">
377
+ <span class="text-xs">30%</span>
378
+ </div>
379
+ </div>
380
+ </div>
381
+ </div>
382
+ </section>
383
+
384
+ <!-- Keyboard -->
385
+ <section class="mb-8">
386
+ <h2 class="text-xl font-bold mb-4 text-green-400 border-b border-green-500 pb-2">Keyboard</h2>
387
+ <div id="keyboard" class="bg-gray-800 p-4 rounded-lg border-2 border-green-700">
388
+ <div class="flex justify-center">
389
+ <div class="keyboard-container relative">
390
+ <!-- White keys -->
391
+ <div class="flex">
392
+ <div class="key white-key w-12 h-40 bg-white border border-gray-300 rounded-b-md mr-px relative z-10"></div>
393
+ <div class="key white-key w-12 h-40 bg-white border border-gray-300 rounded-b-md mr-px relative z-10"></div>
394
+ <div class="key white-key w-12 h-40 bg-white border border-gray-300 rounded-b-md mr-px relative z-10"></div>
395
+ <div class="key white-key w-12 h-40 bg-white border border-gray-300 rounded-b-md mr-px relative z-10"></div>
396
+ <div class="key white-key w-12 h-40 bg-white border border-gray-300 rounded-b-md mr-px relative z-10"></div>
397
+ <div class="key white-key w-12 h-40 bg-white border border-gray-300 rounded-b-md mr-px relative z-10"></div>
398
+ <div class="key white-key w-12 h-40 bg-white border border-gray-300 rounded-b-md mr-px relative z-10"></div>
399
+ <div class="key white-key w-12 h-40 bg-white border border-gray-300 rounded-b-md mr-px relative z-10"></div>
400
+ <div class="key white-key w-12 h-40 bg-white border border-gray-300 rounded-b-md mr-px relative z-10"></div>
401
+ <div class="key white-key w-12 h-40 bg-white border border-gray-300 rounded-b-md mr-px relative z-10"></div>
402
+ <div class="key white-key w-12 h-40 bg-white border border-gray-300 rounded-b-md mr-px relative z-10"></div>
403
+ <div class="key white-key w-12 h-40 bg-white border border-gray-300 rounded-b-md mr-px relative z-10"></div>
404
+ <div class="key white-key w-12 h-40 bg-white border border-gray-300 rounded-b-md mr-px relative z-10"></div>
405
+ </div>
406
+ <!-- Black keys -->
407
+ <div class="absolute top-0 left-0 flex">
408
+ <div class="key black-key w-8 h-24 bg-black rounded-b-md ml-8 relative z-20"></div>
409
+ <div class="key black-key w-8 h-24 bg-black rounded-b-md ml-16 relative z-20"></div>
410
+ <div class="key black-key w-8 h-24 bg-black rounded-b-md ml-24 relative z-20"></div>
411
+ <div class="key black-key w-8 h-24 bg-black rounded-b-md ml-32 relative z-20"></div>
412
+ <div class="key black-key w-8 h-24 bg-black rounded-b-md ml-40 relative z-20"></div>
413
+ <div class="key black-key w-8 h-24 bg-black rounded-b-md ml-64 relative z-20"></div>
414
+ <div class="key black-key w-8 h-24 bg-black rounded-b-md ml-72 relative z-20"></div>
415
+ <div class="key black-key w-8 h-24 bg-black rounded-b-md ml-80 relative z-20"></div>
416
+ <div class="key black-key w-8 h-24 bg-black rounded-b-md ml-88 relative z-20"></div>
417
+ <div class="key black-key w-8 h-24 bg-black rounded-b-md ml-112 relative z-20"></div>
418
+ </div>
419
+ </div>
420
+ </div>
421
+ </div>
422
+ </section>
423
+ </main>
424
+
425
+ <footer class="p-4 border-t-2 border-green-500 text-center text-gray-400">
426
+ <p>SynthX - XBOX Edition | Made with Tone.js</p>
427
+ </footer>
428
+
429
+ <script src="script.js"></script>
430
+ <script>feather.replace();</script>
431
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
432
+ </body>
433
+ </html>
script.js ADDED
@@ -0,0 +1,406 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Initialize Tone.js synth
2
+ const synth = new Tone.PolySynth(Tone.Synth).toDestination();
3
+
4
+ // Initialize effects
5
+ const reverb = new Tone.Reverb({
6
+ decay: 2,
7
+ wet: 0.3
8
+ }).toDestination();
9
+
10
+ const delay = new Tone.FeedbackDelay({
11
+ delayTime: 0.5,
12
+ feedback: 0.3,
13
+ wet: 0.2
14
+ }).toDestination();
15
+
16
+ const filter = new Tone.Filter({
17
+ type: "lowpass",
18
+ frequency: 1000,
19
+ Q: 1
20
+ }).toDestination();
21
+
22
+ const distortion = new Tone.Distortion({
23
+ distortion: 0.5,
24
+ wet: 0.3
25
+ }).toDestination();
26
+
27
+ const chorus = new Tone.Chorus({
28
+ frequency: 2,
29
+ depth: 0.5,
30
+ wet: 0.3
31
+ }).toDestination();
32
+
33
+ const phaser = new Tone.Phaser({
34
+ frequency: 2,
35
+ depth: 0.5,
36
+ wet: 0.3
37
+ }).toDestination();
38
+
39
+ // Connect synth to effects chain
40
+ synth.chain(filter, distortion, chorus, phaser, delay, reverb, Tone.Destination);
41
+
42
+ // Oscillator controls
43
+ document.getElementById('osc1-waveform').addEventListener('change', (e) => {
44
+ synth.set({ oscillator: { type: e.target.value } });
45
+ });
46
+
47
+ document.getElementById('osc1-frequency').addEventListener('input', (e) => {
48
+ const freq = parseFloat(e.target.value);
49
+ document.getElementById('osc1-freq-value').textContent = `${freq} Hz`;
50
+ // In a real implementation, this would update oscillator frequency
51
+ });
52
+
53
+ document.getElementById('osc1-detune').addEventListener('input', (e) => {
54
+ const detune = parseFloat(e.target.value);
55
+ document.getElementById('osc1-detune-value').textContent = `${detune} cents`;
56
+ // In a real implementation, this would update oscillator detune
57
+ });
58
+
59
+ document.getElementById('osc1-volume').addEventListener('input', (e) => {
60
+ const vol = parseFloat(e.target.value);
61
+ document.getElementById('osc1-vol-value').textContent = `${vol} dB`;
62
+ // In a real implementation, this would update oscillator volume
63
+ });
64
+
65
+ // Similar event listeners for osc2 and osc3 would go here
66
+
67
+ // ADSR Controls
68
+ document.getElementById('attack').addEventListener('input', (e) => {
69
+ const attack = parseFloat(e.target.value);
70
+ document.getElementById('attack-value').textContent = `${attack.toFixed(2)} s`;
71
+ synth.set({ envelope: { attack } });
72
+ });
73
+
74
+ document.getElementById('decay').addEventListener('input', (e) => {
75
+ const decay = parseFloat(e.target.value);
76
+ document.getElementById('decay-value').textContent = `${decay.toFixed(2)} s`;
77
+ synth.set({ envelope: { decay } });
78
+ });
79
+
80
+ document.getElementById('sustain').addEventListener('input', (e) => {
81
+ const sustain = parseFloat(e.target.value);
82
+ document.getElementById('sustain-value').textContent = `${Math.round(sustain * 100)}%`;
83
+ synth.set({ envelope: { sustain } });
84
+ });
85
+
86
+ document.getElementById('release').addEventListener('input', (e) => {
87
+ const release = parseFloat(e.target.value);
88
+ document.getElementById('release-value').textContent = `${release.toFixed(2)} s`;
89
+ synth.set({ envelope: { release } });
90
+ });
91
+
92
+ // Draw ADSR visualization
93
+ function drawADSR() {
94
+ const canvas = document.getElementById('adsr-canvas');
95
+ const ctx = canvas.getContext('2d');
96
+ const width = canvas.width;
97
+ const height = canvas.height;
98
+
99
+ // Clear canvas
100
+ ctx.clearRect(0, 0, width, height);
101
+
102
+ // Get current values
103
+ const attack = parseFloat(document.getElementById('attack').value);
104
+ const decay = parseFloat(document.getElementById('decay').value);
105
+ const sustain = parseFloat(document.getElementById('sustain').value);
106
+ const release = parseFloat(document.getElementById('release').value);
107
+
108
+ // Normalize times for visualization
109
+ const totalTime = attack + decay + release + 1; // Add 1 second for sustain
110
+
111
+ // Draw envelope
112
+ ctx.beginPath();
113
+ ctx.moveTo(0, height); // Start at bottom left
114
+
115
+ // Attack
116
+ const attackWidth = (attack / totalTime) * width;
117
+ ctx.lineTo(attackWidth, 0);
118
+
119
+ // Decay
120
+ const decayWidth = (decay / totalTime) * width;
121
+ ctx.lineTo(attackWidth + decayWidth, height * (1 - sustain));
122
+
123
+ // Sustain
124
+ const sustainWidth = (1 / totalTime) * width;
125
+ ctx.lineTo(attackWidth + decayWidth + sustainWidth, height * (1 - sustain));
126
+
127
+ // Release
128
+ const releaseWidth = (release / totalTime) * width;
129
+ ctx.lineTo(attackWidth + decayWidth + sustainWidth + releaseWidth, height);
130
+
131
+ ctx.strokeStyle = '#107c10';
132
+ ctx.lineWidth = 3;
133
+ ctx.stroke();
134
+ }
135
+
136
+ // Update ADSR visualization when values change
137
+ ['attack', 'decay', 'sustain', 'release'].forEach(id => {
138
+ document.getElementById(id).addEventListener('input', drawADSR);
139
+ });
140
+
141
+ // Initial draw
142
+ drawADSR();
143
+
144
+ // X/Y Pad functionality
145
+ const xyPad = document.getElementById('xy-pad');
146
+ const xyHandle = document.getElementById('xy-handle');
147
+ let isDragging = false;
148
+
149
+ function updateXYPosition(x, y) {
150
+ const rect = xyPad.getBoundingClientRect();
151
+ let posX = x - rect.left;
152
+ let posY = y - rect.top;
153
+
154
+ // Constrain to pad boundaries
155
+ posX = Math.max(0, Math.min(posX, rect.width));
156
+ posY = Math.max(0, Math.min(posY, rect.height));
157
+
158
+ // Update handle position
159
+ xyHandle.style.left = `${posX}px`;
160
+ xyHandle.style.top = `${posY}px`;
161
+
162
+ // Update values (0-1 range)
163
+ const xValue = posX / rect.width;
164
+ const yValue = posY / rect.height;
165
+
166
+ document.getElementById('x-value').textContent = xValue.toFixed(2);
167
+ document.getElementById('y-value').textContent = yValue.toFixed(2);
168
+
169
+ // In a real implementation, these values would control modulation destinations
170
+ }
171
+
172
+ xyPad.addEventListener('mousedown', (e) => {
173
+ isDragging = true;
174
+ updateXYPosition(e.clientX, e.clientY);
175
+ });
176
+
177
+ document.addEventListener('mousemove', (e) => {
178
+ if (isDragging) {
179
+ updateXYPosition(e.clientX, e.clientY);
180
+ }
181
+ });
182
+
183
+ document.addEventListener('mouseup', () => {
184
+ isDragging = false;
185
+ });
186
+
187
+ // Touch support for mobile
188
+ xyPad.addEventListener('touchstart', (e) => {
189
+ isDragging = true;
190
+ const touch = e.touches[0];
191
+ updateXYPosition(touch.clientX, touch.clientY);
192
+ e.preventDefault();
193
+ });
194
+
195
+ document.addEventListener('touchmove', (e) => {
196
+ if (isDragging) {
197
+ const touch = e.touches[0];
198
+ updateXYPosition(touch.clientX, touch.clientY);
199
+ e.preventDefault();
200
+ }
201
+ });
202
+
203
+ document.addEventListener('touchend', () => {
204
+ isDragging = false;
205
+ });
206
+
207
+ // Initialize XY pad handle position
208
+ xyHandle.style.left = '50%';
209
+ xyHandle.style.top = '50%';
210
+
211
+ // Effect toggles
212
+ document.getElementById('reverb-toggle').addEventListener('change', (e) => {
213
+ reverb.wet.value = e.target.checked ? parseFloat(document.getElementById('reverb-wet').value) : 0;
214
+ });
215
+
216
+ document.getElementById('delay-toggle').addEventListener('change', (e) => {
217
+ delay.wet.value = e.target.checked ? parseFloat(document.getElementById('delay-wet').value) : 0;
218
+ });
219
+
220
+ document.getElementById('filter-toggle').addEventListener('change', (e) => {
221
+ filter.frequency.value = e.target.checked ? parseFloat(document.getElementById('filter-frequency').value) : 20000;
222
+ });
223
+
224
+ document.getElementById('distortion-toggle').addEventListener('change', (e) => {
225
+ distortion.wet.value = e.target.checked ? parseFloat(document.getElementById('distortion-wet').value) : 0;
226
+ });
227
+
228
+ document.getElementById('chorus-toggle').addEventListener('change', (e) => {
229
+ chorus.wet.value = e.target.checked ? parseFloat(document.getElementById('chorus-wet').value) : 0;
230
+ });
231
+
232
+ document.getElementById('phaser-toggle').addEventListener('change', (e) => {
233
+ phaser.wet.value = e.target.checked ? parseFloat(document.getElementById('phaser-wet').value) : 0;
234
+ });
235
+
236
+ // Effect parameter updates
237
+ document.getElementById('reverb-decay').addEventListener('input', (e) => {
238
+ reverb.decay = parseFloat(e.target.value);
239
+ });
240
+
241
+ document.getElementById('reverb-wet').addEventListener('input', (e) => {
242
+ const wet = parseFloat(e.target.value);
243
+ document.getElementById('reverb-wet').nextElementSibling.textContent = `${Math.round(wet * 100)}%`;
244
+ if (document.getElementById('reverb-toggle').checked) {
245
+ reverb.wet.value = wet;
246
+ }
247
+ });
248
+
249
+ document.getElementById('delay-time').addEventListener('input', (e) => {
250
+ delay.delayTime.value = parseFloat(e.target.value);
251
+ document.getElementById('delay-time').nextElementSibling.textContent = `${parseFloat(e.target.value).toFixed(2)} s`;
252
+ });
253
+
254
+ document.getElementById('delay-feedback').addEventListener('input', (e) => {
255
+ delay.feedback.value = parseFloat(e.target.value);
256
+ document.getElementById('delay-feedback').nextElementSibling.textContent = `${Math.round(parseFloat(e.target.value) * 100)}%`;
257
+ });
258
+
259
+ document.getElementById('delay-wet').addEventListener('input', (e) => {
260
+ const wet = parseFloat(e.target.value);
261
+ document.getElementById('delay-wet').nextElementSibling.textContent = `${Math.round(wet * 100)}%`;
262
+ if (document.getElementById('delay-toggle').checked) {
263
+ delay.wet.value = wet;
264
+ }
265
+ });
266
+
267
+ document.getElementById('filter-type').addEventListener('change', (e) => {
268
+ filter.type = e.target.value;
269
+ });
270
+
271
+ document.getElementById('filter-frequency').addEventListener('input', (e) => {
272
+ const freq = parseFloat(e.target.value);
273
+ document.getElementById('filter-frequency').nextElementSibling.textContent = `${freq} Hz`;
274
+ if (document.getElementById('filter-toggle').checked) {
275
+ filter.frequency.value = freq;
276
+ }
277
+ });
278
+
279
+ document.getElementById('filter-q').addEventListener('input', (e) => {
280
+ filter.Q.value = parseFloat(e.target.value);
281
+ document.getElementById('filter-q').nextElementSibling.textContent = parseFloat(e.target.value).toFixed(1);
282
+ });
283
+
284
+ document.getElementById('distortion-drive').addEventListener('input', (e) => {
285
+ distortion.distortion = parseFloat(e.target.value);
286
+ document.getElementById('distortion-drive').nextElementSibling.textContent = `${Math.round(parseFloat(e.target.value) * 100)}%`;
287
+ });
288
+
289
+ document.getElementById('distortion-wet').addEventListener('input', (e) => {
290
+ const wet = parseFloat(e.target.value);
291
+ document.getElementById('distortion-wet').nextElementSibling.textContent = `${Math.round(wet * 100)}%`;
292
+ if (document.getElementById('distortion-toggle').checked) {
293
+ distortion.wet.value = wet;
294
+ }
295
+ });
296
+
297
+ document.getElementById('chorus-rate').addEventListener('input', (e) => {
298
+ chorus.frequency.value = parseFloat(e.target.value);
299
+ document.getElementById('chorus-rate').nextElementSibling.textContent = `${parseFloat(e.target.value).toFixed(1)} Hz`;
300
+ });
301
+
302
+ document.getElementById('chorus-depth').addEventListener('input', (e) => {
303
+ chorus.depth = parseFloat(e.target.value);
304
+ document.getElementById('chorus-depth').nextElementSibling.textContent = `${Math.round(parseFloat(e.target.value) * 100)}%`;
305
+ });
306
+
307
+ document.getElementById('chorus-wet').addEventListener('input', (e) => {
308
+ const wet = parseFloat(e.target.value);
309
+ document.getElementById('chorus-wet').nextElementSibling.textContent = `${Math.round(wet * 100)}%`;
310
+ if (document.getElementById('chorus-toggle').checked) {
311
+ chorus.wet.value = wet;
312
+ }
313
+ });
314
+
315
+ document.getElementById('phaser-rate').addEventListener('input', (e) => {
316
+ phaser.frequency.value = parseFloat(e.target.value);
317
+ document.getElementById('phaser-rate').nextElementSibling.textContent = `${parseFloat(e.target.value).toFixed(1)} Hz`;
318
+ });
319
+
320
+ document.getElementById('phaser-depth').addEventListener('input', (e) => {
321
+ phaser.depth = parseFloat(e.target.value);
322
+ document.getElementById('phaser-depth').nextElementSibling.textContent = `${Math.round(parseFloat(e.target.value) * 100)}%`;
323
+ });
324
+
325
+ document.getElementById('phaser-wet').addEventListener('input', (e) => {
326
+ const wet = parseFloat(e.target.value);
327
+ document.getElementById('phaser-wet').nextElementSibling.textContent = `${Math.round(wet * 100)}%`;
328
+ if (document.getElementById('phaser-toggle').checked) {
329
+ phaser.wet.value = wet;
330
+ }
331
+ });
332
+
333
+ // Keyboard interaction
334
+ const keys = document.querySelectorAll('.key');
335
+ const notes = ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4', 'C5', 'D5', 'E5', 'F5', 'G5', 'A5'];
336
+
337
+ keys.forEach((key, index) => {
338
+ key.addEventListener('mousedown', () => {
339
+ synth.triggerAttack(notes[index]);
340
+ key.classList.add('active');
341
+ });
342
+
343
+ key.addEventListener('mouseup', () => {
344
+ synth.triggerRelease(notes[index]);
345
+ key.classList.remove('active');
346
+ });
347
+
348
+ key.addEventListener('mouseleave', () => {
349
+ if (key.classList.contains('active')) {
350
+ synth.triggerRelease(notes[index]);
351
+ key.classList.remove('active');
352
+ }
353
+ });
354
+ });
355
+
356
+ // Touch support for keyboard
357
+ keys.forEach((key, index) => {
358
+ key.addEventListener('touchstart', (e) => {
359
+ synth.triggerAttack(notes[index]);
360
+ key.classList.add('active');
361
+ e.preventDefault();
362
+ });
363
+
364
+ key.addEventListener('touchend', (e) => {
365
+ synth.triggerRelease(notes[index]);
366
+ key.classList.remove('active');
367
+ e.preventDefault();
368
+ });
369
+ });
370
+
371
+ // Preset functionality
372
+ document.getElementById('save-preset').addEventListener('click', () => {
373
+ alert('Preset saved! (In a full implementation, this would save to localStorage or a database)');
374
+ });
375
+
376
+ document.getElementById('load-preset').addEventListener('click', () => {
377
+ alert('Preset loaded! (In a full implementation, this would load from localStorage or a database)');
378
+ });
379
+
380
+ // Initialize all sliders with their value displays
381
+ document.querySelectorAll('input[type="range"]').forEach(input => {
382
+ const valueSpan = input.nextElementSibling;
383
+ if (valueSpan && valueSpan.tagName === 'SPAN') {
384
+ const updateValue = () => {
385
+ const val = parseFloat(input.value);
386
+ if (input.id.includes('freq')) {
387
+ valueSpan.textContent = `${val} Hz`;
388
+ } else if (input.id.includes('detune')) {
389
+ valueSpan.textContent = `${val} cents`;
390
+ } else if (input.id.includes('volume')) {
391
+ valueSpan.textContent = `${val} dB`;
392
+ } else if (input.id.includes('time') || input.id.includes('attack') || input.id.includes('decay') || input.id.includes('release')) {
393
+ valueSpan.textContent = `${val.toFixed(2)} s`;
394
+ } else if (input.id.includes('q')) {
395
+ valueSpan.textContent = val.toFixed(1);
396
+ } else if (input.id.includes('rate')) {
397
+ valueSpan.textContent = `${val.toFixed(1)} Hz`;
398
+ } else {
399
+ valueSpan.textContent = `${Math.round(val * 100)}%`;
400
+ }
401
+ };
402
+
403
+ input.addEventListener('input', updateValue);
404
+ updateValue(); // Initial update
405
+ }
406
+ });
style.css CHANGED
@@ -1,28 +1,173 @@
 
 
 
 
 
 
 
 
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
+ @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;700&family=Roboto:wght@300;400;500&display=swap');
2
+
3
+ :root {
4
+ --xbox-green: #107c10;
5
+ --xbox-dark: #1d232b;
6
+ --xbox-light: #5dc21e;
7
+ }
8
+
9
  body {
10
+ font-family: 'Roboto', sans-serif;
11
+ background-color: #0a0a0a;
12
+ background-image:
13
+ radial-gradient(circle at 10% 20%, rgba(16, 124, 16, 0.1) 0%, transparent 20%),
14
+ radial-gradient(circle at 90% 80%, rgba(16, 124, 16, 0.1) 0%, transparent 20%);
15
+ background-attachment: fixed;
16
+ }
17
+
18
+ h1, h2, h3, h4, h5, h6 {
19
+ font-family: 'Orbitron', sans-serif;
20
+ letter-spacing: 1px;
21
+ }
22
+
23
+ /* XBOX-style switch */
24
+ .switch {
25
+ position: relative;
26
+ display: inline-block;
27
+ width: 50px;
28
+ height: 24px;
29
+ }
30
+
31
+ .switch input {
32
+ opacity: 0;
33
+ width: 0;
34
+ height: 0;
35
+ }
36
+
37
+ .slider {
38
+ position: absolute;
39
+ cursor: pointer;
40
+ top: 0;
41
+ left: 0;
42
+ right: 0;
43
+ bottom: 0;
44
+ background-color: #ccc;
45
+ transition: .4s;
46
+ border-radius: 24px;
47
+ }
48
+
49
+ .slider:before {
50
+ position: absolute;
51
+ content: "";
52
+ height: 16px;
53
+ width: 16px;
54
+ left: 4px;
55
+ bottom: 4px;
56
+ background-color: white;
57
+ transition: .4s;
58
+ border-radius: 50%;
59
+ }
60
+
61
+ input:checked + .slider {
62
+ background-color: var(--xbox-green);
63
+ }
64
+
65
+ input:checked + .slider:before {
66
+ transform: translateX(26px);
67
+ }
68
+
69
+ /* Keyboard styling */
70
+ .keyboard-container {
71
+ position: relative;
72
+ height: 160px;
73
+ margin: 20px auto;
74
+ padding: 0 10px;
75
  }
76
 
77
+ .key {
78
+ display: inline-block;
79
+ cursor: pointer;
80
+ user-select: none;
81
+ transition: all 0.1s ease;
82
  }
83
 
84
+ .white-key {
85
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
 
 
 
86
  }
87
 
88
+ .white-key:active,
89
+ .white-key.active {
90
+ background-color: #e0e0e0;
91
+ transform: translateY(2px);
92
+ box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1);
 
93
  }
94
 
95
+ .black-key {
96
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
97
  }
98
+
99
+ .black-key:active,
100
+ .black-key.active {
101
+ background-color: #333;
102
+ transform: translateY(2px);
103
+ box-shadow: 0 2px 3px rgba(0, 0, 0, 0.2);
104
+ }
105
+
106
+ /* XY Pad styling */
107
+ #xy-pad {
108
+ background:
109
+ linear-gradient(to bottom, rgba(16, 124, 16, 0.1) 0%, transparent 100%),
110
+ linear-gradient(to right, rgba(16, 124, 16, 0.1) 0%, transparent 100%);
111
+ position: relative;
112
+ overflow: hidden;
113
+ }
114
+
115
+ #xy-pad::before {
116
+ content: '';
117
+ position: absolute;
118
+ top: 50%;
119
+ left: 0;
120
+ right: 0;
121
+ height: 1px;
122
+ background: rgba(16, 124, 16, 0.3);
123
+ }
124
+
125
+ #xy-pad::after {
126
+ content: '';
127
+ position: absolute;
128
+ left: 50%;
129
+ top: 0;
130
+ bottom: 0;
131
+ width: 1px;
132
+ background: rgba(16, 124, 16, 0.3);
133
+ }
134
+
135
+ #xy-handle {
136
+ box-shadow: 0 0 10px rgba(16, 124, 16, 0.7);
137
+ border: 2px solid white;
138
+ }
139
+
140
+ /* Custom scrollbar */
141
+ ::-webkit-scrollbar {
142
+ width: 8px;
143
+ }
144
+
145
+ ::-webkit-scrollbar-track {
146
+ background: #1a202c;
147
+ }
148
+
149
+ ::-webkit-scrollbar-thumb {
150
+ background: var(--xbox-green);
151
+ border-radius: 4px;
152
+ }
153
+
154
+ ::-webkit-scrollbar-thumb:hover {
155
+ background: #5dc21e;
156
+ }
157
+
158
+ /* Responsive adjustments */
159
+ @media (max-width: 768px) {
160
+ .keyboard-container {
161
+ transform: scale(0.8);
162
+ }
163
+
164
+ #xy-pad {
165
+ height: 200px;
166
+ }
167
+
168
+ .grid-cols-1.md\:grid-cols-3,
169
+ .grid-cols-1.md\:grid-cols-4,
170
+ .grid-cols-1.md\:grid-cols-2 {
171
+ grid-template-columns: repeat(1, minmax(0, 1fr));
172
+ }
173
+ }