NativeAngels commited on
Commit
1050e61
·
verified ·
1 Parent(s): 5d6643f

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +568 -19
  3. prompts.txt +1 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Music Theory Assistant
3
- emoji: 🔥
4
- colorFrom: gray
5
- colorTo: indigo
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: music-theory-assistant
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: red
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,568 @@
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>Piano Theory Master - Circle of Fifths</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
11
+
12
+ body {
13
+ font-family: 'Inter', sans-serif;
14
+ background-color: #f8fafc;
15
+ color: #1e293b;
16
+ }
17
+
18
+ .circle-container {
19
+ position: relative;
20
+ width: 400px;
21
+ height: 400px;
22
+ margin: 0 auto;
23
+ }
24
+
25
+ .key-btn {
26
+ position: absolute;
27
+ width: 60px;
28
+ height: 60px;
29
+ border-radius: 50%;
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ font-weight: 600;
34
+ cursor: pointer;
35
+ transition: all 0.3s ease;
36
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
37
+ }
38
+
39
+ .key-btn:hover {
40
+ transform: scale(1.1);
41
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
42
+ }
43
+
44
+ .major { background-color: #6366f1; color: white; }
45
+ .minor { background-color: #8b5cf6; color: white; }
46
+ .active { transform: scale(1.2); box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.5); }
47
+
48
+ .piano-key {
49
+ position: relative;
50
+ height: 120px;
51
+ border: 1px solid #1e293b;
52
+ border-radius: 0 0 6px 6px;
53
+ cursor: pointer;
54
+ transition: all 0.1s ease;
55
+ }
56
+
57
+ .white-key {
58
+ background-color: white;
59
+ width: 40px;
60
+ z-index: 1;
61
+ }
62
+
63
+ .black-key {
64
+ background-color: #1e293b;
65
+ width: 24px;
66
+ height: 70px;
67
+ margin-left: -12px;
68
+ margin-right: -12px;
69
+ z-index: 2;
70
+ color: white;
71
+ }
72
+
73
+ .pressed {
74
+ background-color: #6366f1;
75
+ color: white;
76
+ }
77
+
78
+ .note-label {
79
+ position: absolute;
80
+ bottom: 8px;
81
+ width: 100%;
82
+ text-align: center;
83
+ font-size: 12px;
84
+ font-weight: 600;
85
+ }
86
+
87
+ .chord-progression-btn {
88
+ transition: all 0.2s ease;
89
+ }
90
+
91
+ .chord-progression-btn:hover {
92
+ transform: translateY(-2px);
93
+ }
94
+
95
+ .highlight {
96
+ animation: highlight 0.5s ease;
97
+ }
98
+
99
+ @keyframes highlight {
100
+ 0% { background-color: rgba(99, 102, 241, 0.3); }
101
+ 100% { background-color: transparent; }
102
+ }
103
+ </style>
104
+ </head>
105
+ <body class="min-h-screen">
106
+ <div class="container mx-auto px-4 py-8">
107
+ <header class="text-center mb-8">
108
+ <h1 class="text-4xl font-bold text-indigo-600 mb-2">Piano Theory Master</h1>
109
+ <p class="text-lg text-slate-600">Interactive Circle of Fifths & Music Theory Tool</p>
110
+ </header>
111
+
112
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
113
+ <!-- Circle of Fifths Section -->
114
+ <div class="bg-white rounded-xl shadow-md p-6 lg:col-span-2">
115
+ <h2 class="text-2xl font-semibold mb-4 text-center">Circle of Fifths</h2>
116
+
117
+ <div class="circle-container mb-6">
118
+ <!-- Circle of Fifths will be generated by JavaScript -->
119
+ </div>
120
+
121
+ <div class="flex justify-center space-x-4 mb-6">
122
+ <button id="toggle-mode" class="px-4 py-2 bg-indigo-100 text-indigo-700 rounded-lg font-medium hover:bg-indigo-200 transition">
123
+ <i class="fas fa-exchange-alt mr-2"></i> Toggle Major/Minor
124
+ </button>
125
+ <button id="reset-btn" class="px-4 py-2 bg-slate-100 text-slate-700 rounded-lg font-medium hover:bg-slate-200 transition">
126
+ <i class="fas fa-redo mr-2"></i> Reset
127
+ </button>
128
+ </div>
129
+
130
+ <div class="bg-indigo-50 rounded-lg p-4">
131
+ <h3 class="font-semibold text-indigo-800 mb-2">Key Signature Info</h3>
132
+ <div id="key-info" class="text-slate-700">
133
+ Select a key from the circle to see details
134
+ </div>
135
+ </div>
136
+ </div>
137
+
138
+ <!-- Piano & Chords Section -->
139
+ <div class="bg-white rounded-xl shadow-md p-6">
140
+ <h2 class="text-2xl font-semibold mb-4 text-center">Interactive Piano</h2>
141
+
142
+ <div class="flex justify-center mb-6">
143
+ <div class="flex" id="piano">
144
+ <!-- Piano keys will be generated by JavaScript -->
145
+ </div>
146
+ </div>
147
+
148
+ <div class="mb-6">
149
+ <h3 class="font-semibold mb-2">Current Key: <span id="current-key" class="text-indigo-600">None selected</span></h3>
150
+ <div id="scale-notes" class="text-sm text-slate-600 mb-4">
151
+ Select a key from the circle to see scale notes
152
+ </div>
153
+ </div>
154
+
155
+ <div class="mb-6">
156
+ <h3 class="font-semibold mb-2">Common Chord Progressions</h3>
157
+ <div class="grid grid-cols-2 gap-2">
158
+ <button class="chord-progression-btn px-3 py-2 bg-emerald-100 text-emerald-800 rounded text-sm font-medium">
159
+ I - IV - V
160
+ </button>
161
+ <button class="chord-progression-btn px-3 py-2 bg-emerald-100 text-emerald-800 rounded text-sm font-medium">
162
+ I - V - vi - IV
163
+ </button>
164
+ <button class="chord-progression-btn px-3 py-2 bg-emerald-100 text-emerald-800 rounded text-sm font-medium">
165
+ ii - V - I
166
+ </button>
167
+ <button class="chord-progression-btn px-3 py-2 bg-emerald-100 text-emerald-800 rounded text-sm font-medium">
168
+ I - vi - IV - V
169
+ </button>
170
+ </div>
171
+ </div>
172
+
173
+ <div class="bg-slate-50 rounded-lg p-4">
174
+ <h3 class="font-semibold text-slate-800 mb-2">Chord Builder</h3>
175
+ <div class="flex items-center mb-2">
176
+ <select id="chord-type" class="mr-2 px-2 py-1 border rounded text-sm">
177
+ <option value="major">Major</option>
178
+ <option value="minor">Minor</option>
179
+ <option value="dim">Diminished</option>
180
+ <option value="aug">Augmented</option>
181
+ <option value="7">Dominant 7th</option>
182
+ <option value="maj7">Major 7th</option>
183
+ <option value="m7">Minor 7th</option>
184
+ </select>
185
+ <button id="build-chord" class="px-3 py-1 bg-indigo-600 text-white rounded text-sm hover:bg-indigo-700">
186
+ Build Chord
187
+ </button>
188
+ </div>
189
+ <div id="chord-notes" class="text-sm text-slate-700">
190
+ Select a root note and chord type
191
+ </div>
192
+ </div>
193
+ </div>
194
+ </div>
195
+
196
+ <!-- Theory Reference Section -->
197
+ <div class="bg-white rounded-xl shadow-md p-6 mt-8">
198
+ <h2 class="text-2xl font-semibold mb-4 text-center">Music Theory Reference</h2>
199
+
200
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
201
+ <div class="bg-violet-50 p-4 rounded-lg">
202
+ <h3 class="font-semibold text-violet-800 mb-2"><i class="fas fa-question-circle mr-2"></i>What is the Circle of Fifths?</h3>
203
+ <p class="text-sm text-slate-700">
204
+ The circle of fifths is a diagram showing the relationship between the 12 tones of the chromatic scale, their corresponding key signatures, and the associated major and minor keys.
205
+ </p>
206
+ </div>
207
+
208
+ <div class="bg-blue-50 p-4 rounded-lg">
209
+ <h3 class="font-semibold text-blue-800 mb-2"><i class="fas fa-music mr-2"></i>How to Use This Tool</h3>
210
+ <ul class="text-sm text-slate-700 space-y-1">
211
+ <li>• Click keys on the circle to see their scales and chords</li>
212
+ <li>• Play notes on the virtual piano</li>
213
+ <li>• Explore common chord progressions</li>
214
+ <li>• Build custom chords with the chord builder</li>
215
+ </ul>
216
+ </div>
217
+
218
+ <div class="bg-emerald-50 p-4 rounded-lg">
219
+ <h3 class="font-semibold text-emerald-800 mb-2"><i class="fas fa-lightbulb mr-2"></i>Practice Tips</h3>
220
+ <ul class="text-sm text-slate-700 space-y-1">
221
+ <li>• Practice scales in all 12 keys</li>
222
+ <li>• Memorize the order of sharps and flats</li>
223
+ <li>• Identify relative major/minor pairs</li>
224
+ <li>• Experiment with chord progressions</li>
225
+ </ul>
226
+ </div>
227
+ </div>
228
+ </div>
229
+ </div>
230
+
231
+ <script>
232
+ document.addEventListener('DOMContentLoaded', function() {
233
+ // Circle of Fifths Data
234
+ const circleData = [
235
+ { key: 'C', major: 'C', minor: 'Am', pos: { x: 50, y: 50 }, sharps: 0, flats: 0 },
236
+ { key: 'G', major: 'G', minor: 'Em', pos: { x: 75, y: 20 }, sharps: 1, flats: 0 },
237
+ { key: 'D', major: 'D', minor: 'Bm', pos: { x: 90, y: 50 }, sharps: 2, flats: 0 },
238
+ { key: 'A', major: 'A', minor: 'F#m', pos: { x: 75, y: 80 }, sharps: 3, flats: 0 },
239
+ { key: 'E', major: 'E', minor: 'C#m', pos: { x: 50, y: 90 }, sharps: 4, flats: 0 },
240
+ { key: 'B', major: 'B', minor: 'G#m', pos: { x: 25, y: 80 }, sharps: 5, flats: 0 },
241
+ { key: 'F#', major: 'F#', minor: 'D#m', pos: { x: 10, y: 50 }, sharps: 6, flats: 0 },
242
+ { key: 'C#', major: 'C#', minor: 'A#m', pos: { x: 25, y: 20 }, sharps: 7, flats: 0 },
243
+ { key: 'F', major: 'F', minor: 'Dm', pos: { x: 50, y: 10 }, sharps: 0, flats: 1 },
244
+ { key: 'Bb', major: 'Bb', minor: 'Gm', pos: { x: 75, y: 10 }, sharps: 0, flats: 2 },
245
+ { key: 'Eb', major: 'Eb', minor: 'Cm', pos: { x: 90, y: 20 }, sharps: 0, flats: 3 },
246
+ { key: 'Ab', major: 'Ab', minor: 'Fm', pos: { x: 90, y: 80 }, sharps: 0, flats: 4 },
247
+ { key: 'Db', major: 'Db', minor: 'Bbm', pos: { x: 75, y: 90 }, sharps: 0, flats: 5 },
248
+ { key: 'Gb', major: 'Gb', minor: 'Ebm', pos: { x: 50, y: 90 }, sharps: 0, flats: 6 },
249
+ { key: 'Cb', major: 'Cb', minor: 'Abm', pos: { x: 25, y: 90 }, sharps: 0, flats: 7 }
250
+ ];
251
+
252
+ // Scale and chord data
253
+ const scales = {
254
+ major: ['C', 'D', 'E', 'F', 'G', 'A', 'B'],
255
+ minor: ['A', 'B', 'C', 'D', 'E', 'F', 'G']
256
+ };
257
+
258
+ const chordFormulas = {
259
+ major: [0, 4, 7],
260
+ minor: [0, 3, 7],
261
+ dim: [0, 3, 6],
262
+ aug: [0, 4, 8],
263
+ '7': [0, 4, 7, 10],
264
+ 'maj7': [0, 4, 7, 11],
265
+ 'm7': [0, 3, 7, 10]
266
+ };
267
+
268
+ const noteOrder = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
269
+
270
+ // State variables
271
+ let currentMode = 'major';
272
+ let selectedKey = null;
273
+
274
+ // DOM Elements
275
+ const circleContainer = document.querySelector('.circle-container');
276
+ const keyInfo = document.getElementById('key-info');
277
+ const currentKeyDisplay = document.getElementById('current-key');
278
+ const scaleNotesDisplay = document.getElementById('scale-notes');
279
+ const pianoContainer = document.getElementById('piano');
280
+ const chordNotesDisplay = document.getElementById('chord-notes');
281
+ const toggleModeBtn = document.getElementById('toggle-mode');
282
+ const resetBtn = document.getElementById('reset-btn');
283
+ const buildChordBtn = document.getElementById('build-chord');
284
+ const chordTypeSelect = document.getElementById('chord-type');
285
+
286
+ // Initialize the app
287
+ initCircleOfFifths();
288
+ initPiano();
289
+ setupEventListeners();
290
+
291
+ function initCircleOfFifths() {
292
+ circleContainer.innerHTML = '';
293
+
294
+ circleData.forEach(item => {
295
+ const btn = document.createElement('div');
296
+ btn.className = `key-btn ${currentMode === 'major' ? 'major' : 'minor'}`;
297
+ btn.style.left = `${item.pos.x}%`;
298
+ btn.style.top = `${item.pos.y}%`;
299
+ btn.textContent = currentMode === 'major' ? item.major : item.minor;
300
+ btn.dataset.key = currentMode === 'major' ? item.major : item.minor;
301
+ btn.dataset.mode = currentMode;
302
+ btn.dataset.sharps = item.sharps;
303
+ btn.dataset.flats = item.flats;
304
+
305
+ btn.addEventListener('click', () => {
306
+ selectKey(btn, item);
307
+ });
308
+
309
+ circleContainer.appendChild(btn);
310
+ });
311
+ }
312
+
313
+ function selectKey(btn, data) {
314
+ // Remove active class from all buttons
315
+ document.querySelectorAll('.key-btn').forEach(b => b.classList.remove('active'));
316
+
317
+ // Add active class to selected button
318
+ btn.classList.add('active');
319
+
320
+ // Update selected key
321
+ selectedKey = btn.dataset.key;
322
+ currentKeyDisplay.textContent = selectedKey;
323
+
324
+ // Update key info
325
+ const sharps = parseInt(btn.dataset.sharps);
326
+ const flats = parseInt(btn.dataset.flats);
327
+
328
+ let keySignature = '';
329
+ if (sharps > 0) {
330
+ keySignature = `${sharps} sharp${sharps > 1 ? 's' : ''}: `;
331
+ const sharpNotes = getSharpNotes(sharps);
332
+ keySignature += sharpNotes.join(', ');
333
+ } else if (flats > 0) {
334
+ keySignature = `${flats} flat${flats > 1 ? 's' : ''}: `;
335
+ const flatNotes = getFlatNotes(flats);
336
+ keySignature += flatNotes.join(', ');
337
+ } else {
338
+ keySignature = 'No sharps or flats';
339
+ }
340
+
341
+ keyInfo.innerHTML = `
342
+ <p><strong>Key:</strong> ${selectedKey} ${btn.dataset.mode === 'major' ? 'Major' : 'Minor'}</p>
343
+ <p><strong>Key Signature:</strong> ${keySignature}</p>
344
+ <p><strong>Relative ${btn.dataset.mode === 'major' ? 'Minor' : 'Major'}:</strong> ${btn.dataset.mode === 'major' ? data.minor : data.major}</p>
345
+ `;
346
+
347
+ // Update scale notes
348
+ const scaleNotes = getScaleNotes(selectedKey, btn.dataset.mode);
349
+ scaleNotesDisplay.innerHTML = `
350
+ <strong>Scale Notes:</strong> ${scaleNotes.join(' - ')}
351
+ `;
352
+
353
+ // Highlight piano keys for this scale
354
+ highlightPianoKeys(scaleNotes);
355
+ }
356
+
357
+ function getSharpNotes(count) {
358
+ const sharpOrder = ['F', 'C', 'G', 'D', 'A', 'E', 'B'];
359
+ return sharpOrder.slice(0, count);
360
+ }
361
+
362
+ function getFlatNotes(count) {
363
+ const flatOrder = ['B', 'E', 'A', 'D', 'G', 'C', 'F'];
364
+ return flatOrder.slice(0, count);
365
+ }
366
+
367
+ function getScaleNotes(root, mode) {
368
+ // Find the index of the root note
369
+ let rootIndex = noteOrder.indexOf(root.replace('b', '#') === 'F#' ? 'Gb' : root.replace('b', '#'));
370
+
371
+ // Adjust for enharmonic equivalents
372
+ if (rootIndex === -1) {
373
+ if (root === 'Gb') rootIndex = noteOrder.indexOf('F#');
374
+ if (root === 'Db') rootIndex = noteOrder.indexOf('C#');
375
+ if (root === 'Cb') rootIndex = noteOrder.indexOf('B');
376
+ }
377
+
378
+ const scalePattern = mode === 'major' ? [0, 2, 4, 5, 7, 9, 11] : [0, 2, 3, 5, 7, 8, 10];
379
+ const notes = [];
380
+
381
+ for (let i = 0; i < 7; i++) {
382
+ const noteIndex = (rootIndex + scalePattern[i]) % 12;
383
+ notes.push(noteOrder[noteIndex]);
384
+ }
385
+
386
+ return notes;
387
+ }
388
+
389
+ function initPiano() {
390
+ const whiteKeys = ['C', 'D', 'E', 'F', 'G', 'A', 'B'];
391
+ const blackKeys = ['C#', 'D#', '', 'F#', 'G#', 'A#', ''];
392
+
393
+ // Create white keys
394
+ whiteKeys.forEach((note, i) => {
395
+ const key = document.createElement('div');
396
+ key.className = 'piano-key white-key';
397
+ key.dataset.note = note;
398
+
399
+ const label = document.createElement('div');
400
+ label.className = 'note-label';
401
+ label.textContent = note;
402
+
403
+ key.appendChild(label);
404
+
405
+ key.addEventListener('mousedown', () => playNote(note, key));
406
+ key.addEventListener('mouseup', () => stopNote(key));
407
+ key.addEventListener('mouseleave', () => stopNote(key));
408
+
409
+ pianoContainer.appendChild(key);
410
+ });
411
+
412
+ // Create black keys
413
+ blackKeys.forEach((note, i) => {
414
+ if (note) {
415
+ const key = document.createElement('div');
416
+ key.className = 'piano-key black-key';
417
+ key.dataset.note = note;
418
+
419
+ const label = document.createElement('div');
420
+ label.className = 'note-label';
421
+ label.textContent = note;
422
+
423
+ key.appendChild(label);
424
+
425
+ key.addEventListener('mousedown', () => playNote(note, key));
426
+ key.addEventListener('mouseup', () => stopNote(key));
427
+ key.addEventListener('mouseleave', () => stopNote(key));
428
+
429
+ pianoContainer.insertBefore(key, pianoContainer.children[i * 2 + 1]);
430
+ }
431
+ });
432
+ }
433
+
434
+ function playNote(note, keyElement) {
435
+ // In a real app, you would play the note using Web Audio API
436
+ keyElement.classList.add('pressed');
437
+
438
+ // Highlight corresponding note in scale if a key is selected
439
+ if (selectedKey) {
440
+ const scaleNotes = getScaleNotes(selectedKey, currentMode);
441
+ if (scaleNotes.includes(note)) {
442
+ keyElement.classList.add('highlight');
443
+ setTimeout(() => {
444
+ keyElement.classList.remove('highlight');
445
+ }, 500);
446
+ }
447
+ }
448
+ }
449
+
450
+ function stopNote(keyElement) {
451
+ keyElement.classList.remove('pressed');
452
+ }
453
+
454
+ function highlightPianoKeys(notes) {
455
+ // Reset all keys
456
+ document.querySelectorAll('.piano-key').forEach(key => {
457
+ key.classList.remove('highlight');
458
+ });
459
+
460
+ // Highlight scale notes
461
+ notes.forEach(note => {
462
+ const key = document.querySelector(`.piano-key[data-note="${note}"]`);
463
+ if (key) {
464
+ key.classList.add('highlight');
465
+ setTimeout(() => {
466
+ key.classList.remove('highlight');
467
+ }, 1000);
468
+ }
469
+ });
470
+ }
471
+
472
+ function buildChord() {
473
+ if (!selectedKey) {
474
+ chordNotesDisplay.textContent = 'Please select a key first';
475
+ return;
476
+ }
477
+
478
+ const chordType = chordTypeSelect.value;
479
+ const rootNote = selectedKey;
480
+ const formula = chordFormulas[chordType];
481
+
482
+ // Find root note index
483
+ let rootIndex = noteOrder.indexOf(rootNote.replace('b', '#') === 'F#' ? 'Gb' : rootNote.replace('b', '#'));
484
+
485
+ // Adjust for enharmonic equivalents
486
+ if (rootIndex === -1) {
487
+ if (rootNote === 'Gb') rootIndex = noteOrder.indexOf('F#');
488
+ if (rootNote === 'Db') rootIndex = noteOrder.indexOf('C#');
489
+ if (rootNote === 'Cb') rootIndex = noteOrder.indexOf('B');
490
+ }
491
+
492
+ // Get chord notes
493
+ const chordNotes = formula.map(interval => {
494
+ return noteOrder[(rootIndex + interval) % 12];
495
+ });
496
+
497
+ chordNotesDisplay.innerHTML = `
498
+ <strong>${rootNote} ${chordType} chord:</strong> ${chordNotes.join(' - ')}
499
+ `;
500
+
501
+ // Highlight chord notes on piano
502
+ highlightChordOnPiano(chordNotes);
503
+ }
504
+
505
+ function highlightChordOnPiano(notes) {
506
+ // Reset all keys
507
+ document.querySelectorAll('.piano-key').forEach(key => {
508
+ key.classList.remove('highlight');
509
+ });
510
+
511
+ // Highlight chord notes
512
+ notes.forEach(note => {
513
+ const key = document.querySelector(`.piano-key[data-note="${note}"]`);
514
+ if (key) {
515
+ key.classList.add('highlight');
516
+ }
517
+ });
518
+ }
519
+
520
+ function setupEventListeners() {
521
+ toggleModeBtn.addEventListener('click', () => {
522
+ currentMode = currentMode === 'major' ? 'minor' : 'major';
523
+ initCircleOfFifths();
524
+ if (selectedKey) {
525
+ // Find and select the same key in the new mode
526
+ const currentItem = circleData.find(item =>
527
+ currentMode === 'major' ? item.major === selectedKey : item.minor === selectedKey
528
+ );
529
+
530
+ if (currentItem) {
531
+ const newKey = currentMode === 'major' ? currentItem.major : currentItem.minor;
532
+ const btn = document.querySelector(`.key-btn[data-key="${newKey}"]`);
533
+ if (btn) {
534
+ selectKey(btn, currentItem);
535
+ }
536
+ }
537
+ }
538
+ });
539
+
540
+ resetBtn.addEventListener('click', () => {
541
+ selectedKey = null;
542
+ currentKeyDisplay.textContent = 'None selected';
543
+ keyInfo.textContent = 'Select a key from the circle to see details';
544
+ scaleNotesDisplay.textContent = 'Select a key from the circle to see scale notes';
545
+ document.querySelectorAll('.key-btn').forEach(b => b.classList.remove('active'));
546
+ document.querySelectorAll('.piano-key').forEach(k => k.classList.remove('highlight'));
547
+ });
548
+
549
+ buildChordBtn.addEventListener('click', buildChord);
550
+
551
+ // Chord progression buttons
552
+ document.querySelectorAll('.chord-progression-btn').forEach(btn => {
553
+ btn.addEventListener('click', function() {
554
+ if (!selectedKey) {
555
+ alert('Please select a key first');
556
+ return;
557
+ }
558
+
559
+ const progression = this.textContent.trim();
560
+ alert(`Playing ${progression} progression in ${selectedKey} ${currentMode}`);
561
+ // In a real app, you would play the chord progression
562
+ });
563
+ });
564
+ }
565
+ });
566
+ </script>
567
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=NativeAngels/music-theory-assistant" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
568
+ </html>
prompts.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ Create an interactive music theory app for a piano players with a circle of fifths