From 1833f24f038dd3a2f051aec421a384328c54eebe Mon Sep 17 00:00:00 2001 From: rdrew Date: Wed, 16 Sep 2026 14:44:07 -0300 Subject: [PATCH] tweak --- robs_metronome.html | 134 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 3 deletions(-) diff --git a/robs_metronome.html b/robs_metronome.html index 5e1c50c..66cf287 100644 --- a/robs_metronome.html +++ b/robs_metronome.html @@ -263,6 +263,55 @@ margin-top: 10px; min-height: 1em; } + + .tuner-grid { + display: grid; + grid-template-columns: repeat(6, 1fr); + gap: 8px; + } + .string-btn { + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; + background: var(--panel); + border: 1px solid var(--rule); + border-radius: 6px; + padding: 12px 4px; + cursor: pointer; + font-family: "Work Sans"; + color: var(--ink); + transition: + background 0.12s ease, + border-color 0.12s ease; + } + .string-btn .note { + font-family: "Fraunces", serif; + font-size: 1.3rem; + font-weight: 600; + color: var(--walnut); + } + .string-btn .hz { + font-size: 0.68rem; + color: var(--ink-soft); + font-variant-numeric: tabular-nums; + } + .string-btn:hover { + border-color: var(--brass); + } + .string-btn.active { + background: var(--brass-light); + border-color: var(--brass); + } + .string-btn.active .note { + color: var(--walnut); + } + .tuner-hint { + text-align: center; + font-size: 0.8rem; + color: var(--ink-soft); + margin-top: 12px; + } @@ -304,6 +353,14 @@
+

Tuner

+
+

+ Tap a string to play its tone. Tap again to stop. +

+ +
+

Your tunes

Loading…
@@ -330,10 +387,8 @@ accept="application/json" style="display: none" /> - - -
+
@@ -455,6 +510,79 @@ flashPad(false); }); + // ---------- Guitar tuner ---------- + const GUITAR_STRINGS = [ + { note: "E", octave: 2, freq: 82.41 }, + { note: "A", octave: 2, freq: 110.0 }, + { note: "D", octave: 3, freq: 146.83 }, + { note: "G", octave: 3, freq: 196.0 }, + { note: "B", octave: 3, freq: 246.94 }, + { note: "E", octave: 4, freq: 329.63 }, + ]; + + const tunerGrid = document.getElementById("tunerGrid"); + let tuneOsc = null; + let tuneGain = null; + let activeStringBtn = null; + + function stopTunerTone() { + if (tuneOsc) { + const g = tuneGain; + const now = audioCtx.currentTime; + g.gain.cancelScheduledValues(now); + g.gain.setValueAtTime(g.gain.value, now); + g.gain.exponentialRampToValueAtTime(0.0001, now + 0.06); + tuneOsc.stop(now + 0.08); + } + if (activeStringBtn) activeStringBtn.classList.remove("active"); + tuneOsc = null; + tuneGain = null; + activeStringBtn = null; + } + + function playTunerTone(freq, btn) { + ensureAudio(); + if (audioCtx.state === "suspended") audioCtx.resume(); + + // Toggle off if this string is already sounding + if (activeStringBtn === btn) { + stopTunerTone(); + return; + } + + stopTunerTone(); + + const osc = audioCtx.createOscillator(); + const gain = audioCtx.createGain(); + osc.type = "sine"; + osc.frequency.value = freq; + const now = audioCtx.currentTime; + gain.gain.setValueAtTime(0.0001, now); + gain.gain.exponentialRampToValueAtTime(0.28, now + 0.03); + osc.connect(gain); + gain.connect(audioCtx.destination); + osc.start(); + + tuneOsc = osc; + tuneGain = gain; + activeStringBtn = btn; + btn.classList.add("active"); + } + + GUITAR_STRINGS.forEach((s) => { + const btn = document.createElement("button"); + btn.className = "string-btn"; + btn.innerHTML = + '' + + s.note + + s.octave + + '' + + s.freq.toFixed(2) + + " Hz"; + btn.addEventListener("click", () => playTunerTone(s.freq, btn)); + tunerGrid.appendChild(btn); + }); + // ---------- Tune storage (browser-local cache + JSON export/import) ---------- const tuneList = document.getElementById("tuneList"); const emptyMsg = document.getElementById("emptyMsg");