diff --git a/index.html b/index.html
index e587f17..bbe9388 100644
--- a/index.html
+++ b/index.html
@@ -909,6 +909,9 @@ function GuitarTuner({ onBack, onMenu }) {
const [detectedFreq, setDetectedFreq] = useState(0);
const [cents, setCents] = useState(0);
const [closestString, setClosestString] = useState(null);
+ const [lockedString, setLockedString] = useState(null);
+ const [tuned, setTuned] = useState({});
+ const [currentStringIdx, setCurrentStringIdx] = useState(0);
const ctxRef = useRef(null);
const streamRef = useRef(null);
const rafRef = useRef(null);
@@ -945,7 +948,7 @@ function GuitarTuner({ onBack, onMenu }) {
setDetectedNote(name+oct);
setDetectedFreq(Math.round(freq*10)/10);
setCents(c);
- const closest = OPEN_STRINGS.reduce((best,s)=>{
+ const closest = lockedString || OPEN_STRINGS.reduce((best,s)=>{
const diff = Math.abs(freq - s.freq);
return diff < Math.abs(freq - best.freq) ? s : best;
}, OPEN_STRINGS[0]);
@@ -958,12 +961,14 @@ function GuitarTuner({ onBack, onMenu }) {
useEffect(()=>()=>stopMic(),[]);
- const inTune = Math.abs(cents) <= 5;
- const slightlyOff = Math.abs(cents) <= 15;
+ const inTune = Math.abs(cents) <= 8;
+ const slightlyOff = Math.abs(cents) <= 20;
const needleAngle = Math.max(-45, Math.min(45, cents * 0.9));
const needleColor = inTune ? C.sage : slightlyOff ? C.amber : C.coral;
const tuneMsg = inTune ? "In tune!" : cents < 0 ? "Tune up" : "Tune down";
const tuneMsgColor = inTune ? C.sage : slightlyOff ? C.amber : C.coral;
+ const allTuned = Object.keys(tuned).length === OPEN_STRINGS.length;
+ const activeString = lockedString || OPEN_STRINGS[currentStringIdx];
return (
@@ -1038,19 +1043,38 @@ function GuitarTuner({ onBack, onMenu }) {
)}
+ {/* String selector tabs */}
+
+ {OPEN_STRINGS.map((s,i)=>{
+ const isActive = activeString && activeString.note===s.note;
+ const isDone = tuned[s.note];
+ return (
+
{setLockedString(s);setCurrentStringIdx(i);}}
+ style={{flex:1,padding:"8px 4px",borderRadius:8,border:"1px solid "+(isDone?C.sage:isActive?C.amber:C.fret),
+ background:isDone?"rgba(107,143,110,0.15)":isActive?"rgba(212,168,71,0.1)":"transparent",
+ cursor:"pointer",fontFamily:"sans-serif",textAlign:"center",minWidth:44}}>
+
+ {s.name==="Low E"?"E":s.name==="High e"?"e":s.name}
+
+ {isDone?"✓":s.note}
+
+ );
+ })}
+
+
{/* Guitar string visual */}
-
-
0?C.sage:C.fret),marginBottom:12,transition:"border-color 0.3s"}}>
+
- {closestString ? (
- Pluck the
- {closestString.name} string
+ {detectedFreq>0 ? (
+ Plucking
+ {activeString.name} string
{inTune ? "- In tune!" : (cents<0?"- Tune up (too flat)":"- Tune down (too sharp)")}
- ) : "Pluck any string to tune"}
+ ) : Tap a string above, then pluck it on your guitar }
{OPEN_STRINGS.map((s,i)=>{
- const isActive = closestString && closestString.note===s.note;
+ const isActive = activeString && activeString.note===s.note;
const x1 = 20, x2 = 320;
const y = 18 + i*19;
const thickness = i===0?2.5:i===1?2.2:i===2?1.8:i===3?1.4:i===4?1.1:0.9;
@@ -1093,6 +1117,31 @@ function GuitarTuner({ onBack, onMenu }) {
+ {/* Mark tuned + Next */}
+
+ {detectedFreq>0 && inTune && (
+ {
+ setTuned(t=>({...t,[activeString.note]:true}));
+ const nextIdx = currentStringIdx+1 < OPEN_STRINGS.length ? currentStringIdx+1 : currentStringIdx;
+ setCurrentStringIdx(nextIdx);
+ setLockedString(nextIdx!==currentStringIdx ? OPEN_STRINGS[nextIdx] : lockedString);
+ }}
+ style={{flex:1,padding:"11px",borderRadius:10,border:"2px solid "+C.sage,
+ background:"rgba(107,143,110,0.1)",color:C.sage,fontFamily:"sans-serif",
+ fontSize:14,fontWeight:700,cursor:"pointer"}}>
+ Mark as tuned - Next string
+
+ )}
+ {allTuned && (
+
+ All tuned! Back to menu
+
+ )}
+
+
{/* Mic button */}