|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="UTF-8"> |
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
|
<title>Song Workout Assistant - Identify Keys & Chords by Ear</title> |
|
<script src="https://cdn.tailwindcss.com"></script> |
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
|
<style> |
|
.piano-key { |
|
position: relative; |
|
width: 40px; |
|
height: 160px; |
|
border: 1px solid #333; |
|
border-radius: 0 0 5px 5px; |
|
cursor: pointer; |
|
user-select: none; |
|
display: flex; |
|
justify-content: center; |
|
align-items: flex-end; |
|
padding-bottom: 10px; |
|
font-weight: bold; |
|
transition: all 0.1s; |
|
} |
|
|
|
.white-key { |
|
background-color: white; |
|
color: #333; |
|
z-index: 1; |
|
} |
|
|
|
.black-key { |
|
width: 28px; |
|
height: 100px; |
|
background-color: #333; |
|
color: white; |
|
margin-left: -14px; |
|
margin-right: -14px; |
|
z-index: 2; |
|
padding-bottom: 5px; |
|
} |
|
|
|
.white-key.active { |
|
background-color: #e2e8f0; |
|
box-shadow: inset 0 0 10px rgba(0,0,0,0.3); |
|
} |
|
|
|
.black-key.active { |
|
background-color: #111; |
|
box-shadow: inset 0 0 10px rgba(255,255,255,0.2); |
|
} |
|
|
|
.chord-box { |
|
width: 60px; |
|
height: 60px; |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
border-radius: 8px; |
|
font-weight: bold; |
|
transition: all 0.3s; |
|
} |
|
|
|
.key-indicator { |
|
width: 120px; |
|
height: 120px; |
|
border-radius: 50%; |
|
display: flex; |
|
justify-content: center; |
|
align-items: center; |
|
font-size: 24px; |
|
font-weight: bold; |
|
margin: 0 auto; |
|
transition: all 0.3s; |
|
} |
|
|
|
.note-badge { |
|
display: inline-flex; |
|
align-items: center; |
|
justify-content: center; |
|
width: 32px; |
|
height: 32px; |
|
border-radius: 50%; |
|
margin: 2px; |
|
font-weight: bold; |
|
cursor: pointer; |
|
} |
|
|
|
.progression-step { |
|
transition: all 0.3s; |
|
transform: scale(1); |
|
} |
|
|
|
.progression-step.active { |
|
transform: scale(1.1); |
|
box-shadow: 0 4px 6px rgba(0,0,0,0.1); |
|
} |
|
|
|
@keyframes pulse { |
|
0% { transform: scale(1); } |
|
50% { transform: scale(1.05); } |
|
100% { transform: scale(1); } |
|
} |
|
|
|
.pulse { |
|
animation: pulse 1.5s infinite; |
|
} |
|
</style> |
|
</head> |
|
<body class="bg-gray-100 min-h-screen"> |
|
<div class="container mx-auto px-4 py-8"> |
|
<header class="text-center mb-12"> |
|
<h1 class="text-4xl font-bold text-indigo-800 mb-2">Song Workout Assistant</h1> |
|
<p class="text-lg text-gray-600">Train your ear to identify song keys and chord progressions</p> |
|
</header> |
|
|
|
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8"> |
|
|
|
<div class="bg-white rounded-xl shadow-lg p-6"> |
|
<h2 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center"> |
|
<i class="fas fa-music mr-2 text-indigo-600"></i> Note Input |
|
</h2> |
|
|
|
<div class="mb-6"> |
|
<h3 class="text-lg font-medium text-gray-700 mb-3">Piano Keyboard</h3> |
|
<div class="flex justify-center mb-4"> |
|
<div class="flex relative" id="piano-keyboard"> |
|
|
|
<div class="piano-key white-key" data-note="C">C</div> |
|
<div class="piano-key black-key" data-note="C#">C#</div> |
|
<div class="piano-key white-key" data-note="D">D</div> |
|
<div class="piano-key black-key" data-note="D#">D#</div> |
|
<div class="piano-key white-key" data-note="E">E</div> |
|
<div class="piano-key white-key" data-note="F">F</div> |
|
<div class="piano-key black-key" data-note="F#">F#</div> |
|
<div class="piano-key white-key" data-note="G">G</div> |
|
<div class="piano-key black-key" data-note="G#">G#</div> |
|
<div class="piano-key white-key" data-note="A">A</div> |
|
<div class="piano-key black-key" data-note="A#">A#</div> |
|
<div class="piano-key white-key" data-note="B">B</div> |
|
</div> |
|
</div> |
|
|
|
<div class="flex justify-center space-x-2 mb-4"> |
|
<button id="play-chord-btn" class="bg-indigo-600 hover:bg-indigo-700 text-white px-4 py-2 rounded-lg flex items-center"> |
|
<i class="fas fa-play mr-2"></i> Play Chord |
|
</button> |
|
<button id="clear-notes-btn" class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-2 rounded-lg flex items-center"> |
|
<i class="fas fa-trash-alt mr-2"></i> Clear |
|
</button> |
|
</div> |
|
</div> |
|
|
|
<div class="mb-6"> |
|
<h3 class="text-lg font-medium text-gray-700 mb-3">Selected Notes</h3> |
|
<div id="selected-notes" class="flex flex-wrap min-h-12 bg-gray-50 rounded-lg p-3 border border-gray-200"> |
|
<p class="text-gray-400 italic">Click piano keys or buttons below to add notes</p> |
|
</div> |
|
|
|
<div class="grid grid-cols-7 gap-2 mt-3"> |
|
<button class="note-btn bg-white border border-gray-300 rounded p-2 hover:bg-gray-100" data-note="C">C</button> |
|
<button class="note-btn bg-white border border-gray-300 rounded p-2 hover:bg-gray-100" data-note="C#">C#</button> |
|
<button class="note-btn bg-white border border-gray-300 rounded p-2 hover:bg-gray-100" data-note="D">D</button> |
|
<button class="note-btn bg-white border border-gray-300 rounded p-2 hover:bg-gray-100" data-note="D#">D#</button> |
|
<button class="note-btn bg-white border border-gray-300 rounded p-2 hover:bg-gray-100" data-note="E">E</button> |
|
<button class="note-btn bg-white border border-gray-300 rounded p-2 hover:bg-gray-100" data-note="F">F</button> |
|
<button class="note-btn bg-white border border-gray-300 rounded p-2 hover:bg-gray-100" data-note="F#">F#</button> |
|
<button class="note-btn bg-white border border-gray-300 rounded p-2 hover:bg-gray-100" data-note="G">G</button> |
|
<button class="note-btn bg-white border border-gray-300 rounded p-2 hover:bg-gray-100" data-note="G#">G#</button> |
|
<button class="note-btn bg-white border border-gray-300 rounded p-2 hover:bg-gray-100" data-note="A">A</button> |
|
<button class="note-btn bg-white border border-gray-300 rounded p-2 hover:bg-gray-100" data-note="A#">A#</button> |
|
<button class="note-btn bg-white border border-gray-300 rounded p-2 hover:bg-gray-100" data-note="B">B</button> |
|
</div> |
|
</div> |
|
|
|
<div> |
|
<h3 class="text-lg font-medium text-gray-700 mb-3">Chord Progression</h3> |
|
<div id="chord-progression" class="flex flex-wrap gap-2 mb-3 min-h-16 bg-gray-50 rounded-lg p-3 border border-gray-200"> |
|
<p class="text-gray-400 italic">Add chords to build a progression</p> |
|
</div> |
|
<button id="analyze-progression-btn" class="w-full bg-green-600 hover:bg-green-700 text-white px-4 py-3 rounded-lg font-medium flex items-center justify-center"> |
|
<i class="fas fa-search mr-2"></i> Analyze Progression |
|
</button> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="bg-white rounded-xl shadow-lg p-6"> |
|
<h2 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center"> |
|
<i class="fas fa-chart-bar mr-2 text-indigo-600"></i> Analysis Results |
|
</h2> |
|
|
|
<div class="mb-8"> |
|
<h3 class="text-lg font-medium text-gray-700 mb-3">Detected Chord</h3> |
|
<div id="chord-result" class="flex flex-col items-center justify-center p-6 bg-gray-50 rounded-lg border border-gray-200"> |
|
<div id="chord-name" class="text-4xl font-bold text-indigo-700 mb-2">-</div> |
|
<div id="chord-notes" class="text-gray-600">No chord analyzed yet</div> |
|
</div> |
|
</div> |
|
|
|
<div class="mb-8"> |
|
<h3 class="text-lg font-medium text-gray-700 mb-3">Possible Key</h3> |
|
<div id="key-result" class="flex flex-col items-center justify-center p-6 bg-gray-50 rounded-lg border border-gray-200"> |
|
<div id="key-indicator" class="key-indicator bg-gray-200 text-gray-500 mb-3"> |
|
? |
|
</div> |
|
<div id="key-info" class="text-center"> |
|
<p class="text-gray-600">Play 3 or more notes to detect possible key</p> |
|
<div id="key-chords" class="mt-3 hidden"> |
|
<p class="font-medium text-gray-700">Chords in this key:</p> |
|
<div class="flex flex-wrap justify-center gap-1 mt-2" id="key-chords-list"></div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div> |
|
<h3 class="text-lg font-medium text-gray-700 mb-3">Progression Analysis</h3> |
|
<div id="progression-result" class="p-6 bg-gray-50 rounded-lg border border-gray-200"> |
|
<div id="likely-key" class="mb-4"> |
|
<p class="font-medium text-gray-700">Likely Key:</p> |
|
<p id="progression-key" class="text-xl font-bold text-indigo-700 mt-1">-</p> |
|
</div> |
|
|
|
<div id="roman-analysis" class="mb-4"> |
|
<p class="font-medium text-gray-700">Roman Numeral Analysis:</p> |
|
<div id="roman-numerals" class="flex flex-wrap gap-2 mt-2"></div> |
|
</div> |
|
|
|
<div id="common-progressions" class="hidden"> |
|
<p class="font-medium text-gray-700">Common Progressions:</p> |
|
<div class="mt-2 space-y-2"> |
|
<div class="p-2 bg-blue-50 rounded border border-blue-100"> |
|
<p class="text-blue-800 font-medium">I - V - vi - IV</p> |
|
<p class="text-sm text-blue-600">Popular in pop music (e.g., "Let It Be", "Someone Like You")</p> |
|
</div> |
|
<div class="p-2 bg-blue-50 rounded border border-blue-100"> |
|
<p class="text-blue-800 font-medium">ii - V - I</p> |
|
<p class="text-sm text-blue-600">Jazz standard progression</p> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
|
|
<div class="bg-white rounded-xl shadow-lg p-6"> |
|
<h2 class="text-2xl font-semibold text-gray-800 mb-4 flex items-center"> |
|
<i class="fas fa-book mr-2 text-indigo-600"></i> Music Theory Reference |
|
</h2> |
|
|
|
<div class="mb-8"> |
|
<h3 class="text-lg font-medium text-gray-700 mb-3">Chord Types</h3> |
|
<div class="space-y-3"> |
|
<div class="p-3 bg-purple-50 rounded-lg border border-purple-100"> |
|
<p class="font-medium text-purple-800">Major (C)</p> |
|
<p class="text-sm text-purple-600">Root, Major 3rd, Perfect 5th (C-E-G)</p> |
|
</div> |
|
<div class="p-3 bg-purple-50 rounded-lg border border-purple-100"> |
|
<p class="font-medium text-purple-800">Minor (Cm)</p> |
|
<p class="text-sm text-purple-600">Root, Minor 3rd, Perfect 5th (C-E♭-G)</p> |
|
</div> |
|
<div class="p-3 bg-purple-50 rounded-lg border border-purple-100"> |
|
<p class="font-medium text-purple-800">Dominant 7th (C7)</p> |
|
<p class="text-sm text-purple-600">Major triad + Minor 7th (C-E-G-B♭)</p> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div class="mb-8"> |
|
<h3 class="text-lg font-medium text-gray-700 mb-3">Circle of Fifths</h3> |
|
<div class="flex justify-center"> |
|
<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/3/33/Circle_of_fifths_deluxe_4.svg/1200px-Circle_of_fifths_deluxe_4.svg.png" alt="Circle of Fifths" class="w-full h-auto rounded-lg border border-gray-200"> |
|
</div> |
|
</div> |
|
|
|
<div> |
|
<h3 class="text-lg font-medium text-gray-700 mb-3">How to Use</h3> |
|
<div class="space-y-3"> |
|
<div class="flex items-start"> |
|
<div class="bg-indigo-100 text-indigo-800 rounded-full w-6 h-6 flex items-center justify-center mr-3 mt-1 flex-shrink-0">1</div> |
|
<p class="text-gray-700">Play notes on the piano or click note buttons to build a chord</p> |
|
</div> |
|
<div class="flex items-start"> |
|
<div class="bg-indigo-100 text-indigo-800 rounded-full w-6 h-6 flex items-center justify-center mr-3 mt-1 flex-shrink-0">2</div> |
|
<p class="text-gray-700">The app will analyze possible chords and keys</p> |
|
</div> |
|
<div class="flex items-start"> |
|
<div class="bg-indigo-100 text-indigo-800 rounded-full w-6 h-6 flex items-center justify-center mr-3 mt-1 flex-shrink-0">3</div> |
|
<p class="text-gray-700">Build a chord progression by adding multiple chords</p> |
|
</div> |
|
<div class="flex items-start"> |
|
<div class="bg-indigo-100 text-indigo-800 rounded-full w-6 h-6 flex items-center justify-center mr-3 mt-1 flex-shrink-0">4</div> |
|
<p class="text-gray-700">Analyze the progression to see likely keys and patterns</p> |
|
</div> |
|
</div> |
|
|
|
<div class="mt-6 p-4 bg-yellow-50 border border-yellow-200 rounded-lg"> |
|
<h4 class="font-medium text-yellow-800 mb-2 flex items-center"> |
|
<i class="fas fa-lightbulb mr-2"></i> Ear Training Tip |
|
</h4> |
|
<p class="text-yellow-700">Try to sing the root note of each chord to help identify the key. The note that feels most resolved is often the tonic (key note).</p> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<script> |
|
document.addEventListener('DOMContentLoaded', function() { |
|
// Audio context for playing sounds |
|
const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); |
|
|
|
// Store selected notes and chords |
|
let selectedNotes = []; |
|
let chordProgression = []; |
|
|
|
// Musical reference data |
|
const noteFrequencies = { |
|
'C': 261.63, 'C#': 277.18, 'D': 293.66, 'D#': 311.13, |
|
'E': 329.63, 'F': 349.23, 'F#': 369.99, 'G': 392.00, |
|
'G#': 415.30, 'A': 440.00, 'A#': 466.16, 'B': 493.88 |
|
}; |
|
|
|
const chordTypes = { |
|
'major': [0, 4, 7], |
|
'minor': [0, 3, 7], |
|
'diminished': [0, 3, 6], |
|
'augmented': [0, 4, 8], |
|
'sus2': [0, 2, 7], |
|
'sus4': [0, 5, 7], |
|
'7': [0, 4, 7, 10], |
|
'maj7': [0, 4, 7, 11], |
|
'm7': [0, 3, 7, 10], |
|
'm7b5': [0, 3, 6, 10] |
|
}; |
|
|
|
const keySignatures = { |
|
'C': ['C', 'Dm', 'Em', 'F', 'G', 'Am', 'Bdim'], |
|
'G': ['G', 'Am', 'Bm', 'C', 'D', 'Em', 'F#dim'], |
|
'D': ['D', 'Em', 'F#m', 'G', 'A', 'Bm', 'C#dim'], |
|
'A': ['A', 'Bm', 'C#m', 'D', 'E', 'F#m', 'G#dim'], |
|
'E': ['E', 'F#m', 'G#m', 'A', 'B', 'C#m', 'D#dim'], |
|
'B': ['B', 'C#m', 'D#m', 'E', 'F#', 'G#m', 'A#dim'], |
|
'F#': ['F#', 'G#m', 'A#m', 'B', 'C#', 'D#m', 'E#dim'], |
|
'C#': ['C#', 'D#m', 'E#m', 'F#', 'G#', 'A#m', 'B#dim'], |
|
'F': ['F', 'Gm', 'Am', 'Bb', 'C', 'Dm', 'Edim'], |
|
'Bb': ['Bb', 'Cm', 'Dm', 'Eb', 'F', 'Gm', 'Adim'], |
|
'Eb': ['Eb', 'Fm', 'Gm', 'Ab', 'Bb', 'Cm', 'Ddim'], |
|
'Ab': ['Ab', 'Bbm', 'Cm', 'Db', 'Eb', 'Fm', 'Gdim'], |
|
'Db': ['Db', 'Ebm', 'Fm', 'Gb', 'Ab', 'Bbm', 'Cdim'], |
|
'Gb': ['Gb', 'Abm', 'Bbm', 'Cb', 'Db', 'Ebm', 'Fdim'], |
|
'Cb': ['Cb', 'Dbm', 'Ebm', 'Fb', 'Gb', 'Abm', 'Bbdim'], |
|
'Am': ['Am', 'Bdim', 'C', 'Dm', 'Em', 'F', 'G'], |
|
'Em': ['Em', 'F#dim', 'G', 'Am', 'Bm', 'C', 'D'], |
|
'Bm': ['Bm', 'C#dim', 'D', 'Em', 'F#m', 'G', 'A'], |
|
'F#m': ['F#m', 'G#dim', 'A', 'Bm', 'C#m', 'D', 'E'], |
|
'C#m': ['C#m', 'D#dim', 'E', 'F#m', 'G#m', 'A', 'B'], |
|
'G#m': ['G#m', 'A#dim', 'B', 'C#m', 'D#m', 'E', 'F#'], |
|
'D#m': ['D#m', 'E#dim', 'F#', 'G#m', 'A#m', 'B', 'C#'], |
|
'A#m': ['A#m', 'B#dim', 'C#', 'D#m', 'E#m', 'F#', 'G#'], |
|
'Dm': ['Dm', 'Edim', 'F', 'Gm', 'Am', 'Bb', 'C'], |
|
'Gm': ['Gm', ' |
|
</html> |