Browse Source

tweak

master
rdrew 3 weeks ago
parent
commit
3c451dc2cb
  1. 1056
      metronome.html
  2. 314
      robs_metronome.html
  3. 10
      tunes.json

1056
metronome.html

File diff suppressed because it is too large Load Diff

314
robs_metronome.html

@ -312,6 +312,84 @@
color: var(--ink-soft);
margin-top: 12px;
}
.chord-controls {
display: flex;
justify-content: center;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 18px;
}
.chord-controls label {
display: flex;
flex-direction: column;
gap: 4px;
font-size: 0.78rem;
color: var(--ink-soft);
}
.chord-controls select {
font-family: "Work Sans";
font-size: 0.9rem;
color: var(--ink);
background: var(--panel);
border: 1px solid var(--rule);
border-radius: 4px;
padding: 6px 8px;
}
.chord-block-label {
font-size: 0.78rem;
color: var(--ink-soft);
margin: 0 0 8px;
text-align: center;
}
.chord-row {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 6px;
margin-bottom: 8px;
}
.chord-box {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
background: var(--panel);
border: 1px solid var(--rule);
border-radius: 6px;
padding: 10px 2px;
}
.chord-box .chord-name {
font-family: "Fraunces", serif;
font-weight: 600;
font-size: 1.05rem;
color: var(--walnut);
}
.chord-box .chord-roman {
font-size: 0.68rem;
color: var(--ink-soft);
}
.chord-row.transposed .chord-box {
background: var(--brass-light);
border-color: var(--brass);
}
.transpose-controls {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
margin: 18px 0 20px;
}
.transpose-amount {
font-family: "Fraunces", serif;
font-weight: 600;
font-size: 1.1rem;
color: var(--walnut);
min-width: 72px;
text-align: center;
font-variant-numeric: tabular-nums;
}
</style>
</head>
<body>
@ -361,6 +439,40 @@
<hr class="divider" />
<h2>Chords</h2>
<div class="chord-controls">
<label
>Key
<select id="keyRoot"></select>
</label>
<label
>Scale
<select id="scaleType"></select>
</label>
<label
>Spelling
<select id="spellingMode">
<option value="sharp">Sharps (♯)</option>
<option value="flat">Flats (♭)</option>
</select>
</label>
</div>
<p class="chord-block-label" id="originalKeyLabel"></p>
<div class="chord-row" id="originalChordRow"></div>
<div class="transpose-controls">
<button class="stepper" id="transposeDown">–</button>
<div class="transpose-amount" id="transposeAmount">0 st</div>
<button class="stepper" id="transposeUp">+</button>
</div>
<p class="chord-block-label" id="transposedKeyLabel"></p>
<div class="chord-row transposed" id="transposedChordRow"></div>
<hr class="divider" />
<h2>Your tunes</h2>
<div class="tune-list" id="tuneList">
<div class="empty" id="emptyMsg">Loading…</div>
@ -583,6 +695,207 @@
tunerGrid.appendChild(btn);
});
// ---------- Diatonic chords / transposer ----------
const NOTE_NAMES = [
"C",
"C#",
"D",
"D#",
"E",
"F",
"F#",
"G",
"G#",
"A",
"A#",
"B",
];
const FLAT_NAMES = [
"C",
"Db",
"D",
"Eb",
"E",
"F",
"Gb",
"G",
"Ab",
"A",
"Bb",
"B",
];
const SCALES = {
Ionian: { label: "Major (Ionian)", steps: [0, 2, 4, 5, 7, 9, 11] },
Dorian: { label: "Dorian", steps: [0, 2, 3, 5, 7, 9, 10] },
Phrygian: { label: "Phrygian", steps: [0, 1, 3, 5, 7, 8, 10] },
Lydian: { label: "Lydian", steps: [0, 2, 4, 6, 7, 9, 11] },
Mixolydian: { label: "Mixolydian", steps: [0, 2, 4, 5, 7, 9, 10] },
Aeolian: { label: "Minor (Aeolian)", steps: [0, 2, 3, 5, 7, 8, 10] },
Locrian: { label: "Locrian", steps: [0, 1, 3, 5, 6, 8, 10] },
};
let spellingMode = "sharp";
function noteName(index) {
const table = spellingMode === "flat" ? FLAT_NAMES : NOTE_NAMES;
return table[((index % 12) + 12) % 12];
}
// Build the 7 diatonic triads for a given root index (0-11) and scale key.
function buildDiatonicChords(rootIndex, scaleKey) {
const steps = SCALES[scaleKey].steps;
const scaleNotes = steps.map((s) => (rootIndex + s) % 12);
return scaleNotes.map((deg, i) => {
const root = scaleNotes[i];
const third = scaleNotes[(i + 2) % 7];
const fifth = scaleNotes[(i + 4) % 7];
const rootToThird = (third - root + 12) % 12;
const thirdToFifth = (fifth - third + 12) % 12;
let quality, romanBase;
if (rootToThird === 4 && thirdToFifth === 3) {
quality = "";
romanBase = "maj";
} else if (rootToThird === 3 && thirdToFifth === 4) {
quality = "m";
romanBase = "min";
} else if (rootToThird === 3 && thirdToFifth === 3) {
quality = "dim";
romanBase = "dim";
} else if (rootToThird === 4 && thirdToFifth === 4) {
quality = "aug";
romanBase = "aug";
} else {
quality = "";
romanBase = "maj";
}
const numerals = ["I", "II", "III", "IV", "V", "VI", "VII"];
let roman = numerals[i];
if (romanBase === "min") roman = roman.toLowerCase();
if (romanBase === "dim") roman = roman.toLowerCase() + "°";
if (romanBase === "aug") roman = roman + "+";
return {
name: noteName(root) + quality,
roman: roman,
};
});
}
const keyRootSelect = document.getElementById("keyRoot");
const scaleTypeSelect = document.getElementById("scaleType");
const spellingModeSelect = document.getElementById("spellingMode");
const originalChordRow = document.getElementById("originalChordRow");
const transposedChordRow = document.getElementById("transposedChordRow");
const originalKeyLabel = document.getElementById("originalKeyLabel");
const transposedKeyLabel = document.getElementById("transposedKeyLabel");
const transposeAmountEl = document.getElementById("transposeAmount");
const transposeDownBtn = document.getElementById("transposeDown");
const transposeUpBtn = document.getElementById("transposeUp");
function populateKeyOptions() {
const prevValue = keyRootSelect.value;
keyRootSelect.innerHTML = "";
NOTE_NAMES.forEach((_, i) => {
const opt = document.createElement("option");
opt.value = i;
opt.textContent = noteName(i);
keyRootSelect.appendChild(opt);
});
if (prevValue !== "") keyRootSelect.value = prevValue;
}
populateKeyOptions();
Object.keys(SCALES).forEach((key) => {
const opt = document.createElement("option");
opt.value = key;
opt.textContent = SCALES[key].label;
scaleTypeSelect.appendChild(opt);
});
let chordRootIndex = 0; // C
let chordScaleKey = "Ionian";
let transposeSemitones = 0;
function renderChordRow(container, chords) {
container.innerHTML = "";
chords.forEach((c) => {
const box = document.createElement("div");
box.className = "chord-box";
const name = document.createElement("div");
name.className = "chord-name";
name.textContent = c.name;
const roman = document.createElement("div");
roman.className = "chord-roman";
roman.textContent = c.roman;
box.appendChild(name);
box.appendChild(roman);
container.appendChild(box);
});
}
function renderChords() {
const scaleLabel = SCALES[chordScaleKey].label;
const originalChords = buildDiatonicChords(
chordRootIndex,
chordScaleKey,
);
originalKeyLabel.textContent =
noteName(chordRootIndex) + " " + scaleLabel;
renderChordRow(originalChordRow, originalChords);
const transposedRoot = chordRootIndex + transposeSemitones;
const transposedChords = buildDiatonicChords(
transposedRoot,
chordScaleKey,
);
transposedKeyLabel.textContent =
noteName(transposedRoot) +
" " +
scaleLabel +
" (" +
(transposeSemitones > 0 ? "+" : "") +
transposeSemitones +
" semitones)";
renderChordRow(transposedChordRow, transposedChords);
transposeAmountEl.textContent =
(transposeSemitones > 0 ? "+" : "") + transposeSemitones + " st";
}
keyRootSelect.addEventListener("change", (e) => {
chordRootIndex = parseInt(e.target.value, 10);
renderChords();
});
scaleTypeSelect.addEventListener("change", (e) => {
chordScaleKey = e.target.value;
renderChords();
});
spellingModeSelect.addEventListener("change", (e) => {
spellingMode = e.target.value;
populateKeyOptions();
keyRootSelect.value = chordRootIndex;
renderChords();
});
transposeDownBtn.addEventListener("click", () => {
transposeSemitones = Math.max(-24, transposeSemitones - 1);
renderChords();
});
transposeUpBtn.addEventListener("click", () => {
transposeSemitones = Math.min(24, transposeSemitones + 1);
renderChords();
});
// ---------- Tune storage (browser-local cache + JSON export/import) ----------
const tuneList = document.getElementById("tuneList");
const emptyMsg = document.getElementById("emptyMsg");
@ -737,6 +1050,7 @@
// init
setBpm(120);
loadTunes();
renderChords();
</script>
</body>
</html>

10
tunes.json

@ -13,5 +13,15 @@
"id": "mu3deuwo",
"name": "Scales in 1/4 & 1/16",
"bpm": 95
},
{
"id": "mu4pewvu",
"name": "scales in triplets",
"bpm": 130
},
{
"id": "mu4q2q2l",
"name": "RH Carl Patterns",
"bpm": 84
}
]
Loading…
Cancel
Save