Files

1660 lines
114 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>GuitarTeacher</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/7.23.2/babel.min.js"></script>
<style>* { box-sizing: border-box; margin: 0; padding: 0; } body { background: #1A1A1F; font-family: sans-serif; } input::placeholder { color: #9A9490; } textarea::placeholder { color: #9A9490; } p + p { margin-top: 10px; }</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { useState, useEffect, useRef, useCallback } = React;
const C = {
bg:"#1A1A1F", bg2:"#22222A", bg3:"#2A2A35",
amber:"#D4A847", sage:"#6B8F6E", coral:"#E05C4B",
text:"#F0EDE6", muted:"#9A9490", string:"#8A8070", fret:"#3A3A48",
};
const NOTE_NAMES = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"];
function freqToNote(freq) {
if (freq < 60 || freq > 1200) return null;
const midi = Math.round(12 * Math.log2(freq / 440) + 69);
const name = NOTE_NAMES[midi % 12];
const octave = Math.floor(midi / 12) - 1;
const hz = 440 * Math.pow(2, (midi - 69) / 12);
const cents = Math.round(1200 * Math.log2(freq / hz));
return { name: name + octave, cents };
}
function autoCorrelate(buf, sr) {
const N = buf.length, MAX = Math.floor(N / 2);
let rms = 0;
for (let i = 0; i < N; i++) rms += buf[i] * buf[i];
if (Math.sqrt(rms / N) < 0.01) return -1;
let best = -1, bestC = 0, lastC = 1;
for (let o = 0; o < MAX; o++) {
let c = 0;
for (let i = 0; i < MAX; i++) c += Math.abs(buf[i] - buf[i + o]);
c = 1 - c / MAX;
if (c > 0.9 && c > lastC && c > bestC) { bestC = c; best = o; }
lastC = c;
}
return best > 0 && bestC > 0.01 ? sr / best : -1;
}
const CHORDS = {
Em:{ dots:[{s:2,f:2,n:"2"},{s:3,f:2,n:"3"}], open:[0,1,4,5], muted:[], label:"0 0 2 2 0 0" },
Am:{ dots:[{s:2,f:2,n:"2"},{s:3,f:2,n:"3"},{s:4,f:2,n:"1"}], open:[1,5], muted:[0], label:"x 0 2 2 1 0" },
G: { dots:[{s:0,f:3,n:"2"},{s:1,f:2,n:"1"},{s:4,f:3,n:"3"},{s:5,f:3,n:"4"}], open:[2,3], muted:[], label:"3 2 0 0 3 3" },
C: { dots:[{s:1,f:3,n:"3"},{s:2,f:2,n:"2"},{s:4,f:1,n:"1"}], open:[3,5], muted:[0], label:"x 3 2 0 1 0" },
D: { dots:[{s:3,f:2,n:"1"},{s:4,f:3,n:"3"},{s:5,f:2,n:"2"}], open:[2], muted:[0,1], label:"x x 0 2 3 2" },
};
const SONG_LIBRARY = [
{"title":"Knockin on Heavens Door","artist":"Bob Dylan","genre":"Classic Rock","diff":"Beginner","chords":["G","D","Am"],"parts":[{"name":"Verse","tab":{"e":"--3---2---0---","B":"--3---3---1---","G":"--0---2---2---","D":"--0---0---2---","A":"--2-------0---","E":"3---2---------"},"notes":["G4","D3","A2","E4"]}],"tips":"4/4 strum pattern. G and D are the main two chords \u2014 Am only comes in on the second half."},
{"title":"Wish You Were Here","artist":"Pink Floyd","genre":"Classic Rock","diff":"Intermediate","chords":["G","C","D","Am"],"parts":[{"name":"Intro Riff","tab":{"e":"----------3-5-3-0-","B":"------5-3---------","G":"--5-4-------------","D":"5-----------------","A":"------------------","E":"------------------"},"notes":["D3","A2","G4","B3","E4"]},{"name":"Verse","tab":{"e":"--3---0---2---3---","B":"--3---1---3---3---","G":"--0---0---2---0---","D":"--0---2---0---0---","A":"--2---3-------2---","E":"3---------2---3---"},"notes":["G4","E4","D3","G3"]}],"tips":"The intro riff is what everyone recognizes. Learn it slowly on the top 3 strings first."},
{"title":"Sweet Home Alabama","artist":"Lynyrd Skynyrd","genre":"Classic Rock","diff":"Intermediate","chords":["D","C","G"],"parts":[{"name":"Main Riff","tab":{"e":"--2---0---3---2---","B":"--3---1---3---3---","G":"--2---0---0---2---","D":"--0---2---0---0---","A":"------3---2-------","E":"----------3---2---"},"notes":["D3","C4","G4","D3"]}],"tips":"The D-C-G three-chord slide is the whole song. Keep strumming rhythmic and loose."},
{"title":"Brown Eyed Girl","artist":"Van Morrison","genre":"Classic Rock","diff":"Beginner","chords":["G","C","D","Em"],"parts":[{"name":"Verse / Chorus","tab":{"e":"--3---0---2---0---","B":"--3---1---3---0---","G":"--0---0---2---0---","D":"--0---2---0---2---","A":"--2---3-------2---","E":"3---------2---0---"},"notes":["G4","C4","D3","E3"]}],"tips":"Shuffle rhythm, slightly swung strumming. Goes G-C-G-D then G-C-Em-D for chorus."},
{"title":"Wonderful Tonight","artist":"Eric Clapton","genre":"Classic Rock","diff":"Beginner","chords":["G","D","C","Em"],"parts":[{"name":"Intro Melody","tab":{"e":"--3---5---3---2---0-------","B":"--3-----------3---3---1---","G":"--0---0---0---2---0---0---","D":"--0---0---0---0---0---2---","A":"--2-----------0---2---3---","E":"3---3---3---------0-------"},"notes":["G4","A4","G4","D3","E4","C4"]}],"tips":"Fingerpick or very gentle strumming. The melody on the high strings is what makes this song."},
{"title":"House of the Rising Sun","artist":"The Animals","genre":"Classic Rock","diff":"Intermediate","chords":["Am","C","D","F","E"],"parts":[{"name":"Intro Arpeggio","tab":{"e":"0---1---0---1---0---","B":"1---1---3---1---0---","G":"2---0---2---2---1---","D":"2---2---0---3---2---","A":"0---3-------3---2---","E":"--------1---1---0---"},"notes":["A2","C4","D3","F3","E2"]}],"tips":"Fingerpick each note one at a time \u2014 this is an arpeggio pattern. Very satisfying once you have it."},
{"title":"More Than Words","artist":"Extreme","genre":"Classic Rock","diff":"Intermediate","chords":["G","Cadd9","Am","D","Em"],"parts":[{"name":"Intro","tab":{"e":"3---3---0---0---0---","B":"3---3---1---3---0---","G":"0---0---0---2---0---","D":"0---2---2---0---2---","A":"2---3---3-------2---","E":"3---0---0-------0---"},"notes":["G4","G4","C4","D3","E3"]}],"tips":"Fingerpick or hybrid pick. Let notes ring into each other. The beauty is in letting chords overlap."},
{"title":"Wonderwall","artist":"Oasis","genre":"Pop/Rock","diff":"Intermediate","chords":["Em7","G","Dsus4","A7sus4"],"parts":[{"name":"Intro / Verse","tab":{"e":"3-3-3-3-3-3-3-3-","B":"3-3-3-3-3-3-3-3-","G":"0-0-0-0-0-0-0-0-","D":"0-0-2-2-0-0-2-2-","A":"2-2-3-3-2-2-3-3-","E":"0-0-0-0-3-3-0-0-"},"notes":["G4","B3","G3","D3","A2"]}],"tips":"Capo 2 for original key. The Em7 and A7sus4 shapes are unique to this song but easy once you know them."},
{"title":"Let Her Go","artist":"Passenger","genre":"Pop/Rock","diff":"Beginner","chords":["G","D","Em","C"],"parts":[{"name":"Intro Picking","tab":{"e":"3---2---0---0---3---","B":"3---3---0---1---3---","G":"0---2---0---0---0---","D":"0---0---2---2---0---","A":"2-------2---3---2---","E":"3-----------0---3---"},"notes":["G4","D3","E3","C4","G4"]}],"tips":"Fingerpick the intro, strum the chorus. G-D-Em-C is one of the most common progressions in pop."},
{"title":"Thinking Out Loud","artist":"Ed Sheeran","genre":"Pop","diff":"Intermediate","chords":["D","G","A","Em"],"parts":[{"name":"Verse","tab":{"e":"--2---3---0---0---","B":"--3---3---2---0---","G":"--2---0---2---0---","D":"--0---0---2---2---","A":"------2---0---2---","E":"--2---3-------0---"},"notes":["D3","G4","A2","E3"]}],"tips":"Slow 12/8 feel. Each chord gets two slow downstrokes. The key is keeping it smooth and unhurried."},
{"title":"Riptide","artist":"Vance Joy","genre":"Pop","diff":"Beginner","chords":["Am","G","C"],"parts":[{"name":"Verse / Chorus","tab":{"e":"0---3---0---","B":"1---3---1---","G":"2---0---0---","D":"2---0---2---","A":"0---2---3---","E":"----3-------"},"notes":["A2","G4","C4","E3"]}],"tips":"Just three chords the whole song. Capo 1 for original key. Down-down-up-up-down-up strumming."},
{"title":"Stay With Me","artist":"Sam Smith","genre":"Pop","diff":"Beginner","chords":["Am","F","C"],"parts":[{"name":"Verse / Chorus","tab":{"e":"0---1---0---","B":"1---1---1---","G":"2---2---0---","D":"2---3---2---","A":"0---3---3---","E":"----1-------"},"notes":["A2","F3","C4","E3"]}],"tips":"Three chords, simple strumming. Great beginner pop song."},
{"title":"Shallow","artist":"Lady Gaga and Bradley Cooper","genre":"Pop","diff":"Intermediate","chords":["Em","D","G","C","Am"],"parts":[{"name":"Intro Picking","tab":{"e":"0---2---3---0---","B":"0---3---3---1---","G":"0---2---0---0---","D":"2---0---0---2---","A":"2-------2---3---","E":"0-----------0---"},"notes":["E3","D3","G4","C4","A2"]},{"name":"Chorus","tab":{"e":"--0---3---3---0---","B":"--0---3---0---1---","G":"--0---0---0---0---","D":"--2---0---0---2---","A":"--2---2---2---3---","E":"--0---3---3---0---"},"notes":["E3","G4","D3","C4"]}],"tips":"The intro uses a picked Em-D-G pattern. Chorus strums. Capo 5 for original key."},
{"title":"Horse With No Name","artist":"America","genre":"Rock","diff":"Beginner","chords":["Em","D6"],"parts":[{"name":"Main Riff","tab":{"e":"0-2-0-2-0-2-0-2-","B":"0-3-0-3-0-3-0-3-","G":"0-2-0-2-0-2-0-2-","D":"2-0-2-0-2-0-2-0-","A":"2-0-2-0-2-0-2-0-","E":"0---------------"},"notes":["E4","B3","G3","E3"]}],"tips":"Only two chord shapes the whole song. Great for getting comfortable with the Em shape."},
{"title":"Creep","artist":"Radiohead","genre":"Rock","diff":"Beginner","chords":["G","B","C","Cm"],"parts":[{"name":"Verse / Chorus","tab":{"e":"3---2---0---0---","B":"3---2---1---0---","G":"0---4---0---0---","D":"0---4---2---1---","A":"2---2---3---3---","E":"3---2---0---3---"},"notes":["G4","B2","C4","C3"]}],"tips":"The Cm is the secret ingredient \u2014 it gives the song its brooding feel. Strum hard on the chorus."},
{"title":"Come As You Are","artist":"Nirvana","genre":"Rock","diff":"Beginner","chords":["Em","Am","C"],"parts":[{"name":"Main Riff","tab":{"e":"0---0---0---1---","B":"0---0---0---1---","G":"0---0---0---0---","D":"2---2---1---2---","A":"2---1---3---3---","E":"0---0---0---1---"},"notes":["E4","E3","A2","F3"]}],"tips":"Slow, deliberate picking. Let each note ring. Capo 1 for original key."},
{"title":"Seven Nation Army","artist":"The White Stripes","genre":"Rock","diff":"Beginner","chords":[],"parts":[{"name":"Main Riff","tab":{"e":"----------------","B":"----------------","G":"----------------","D":"----------------","A":"7-7-10-7-5-3-2--","E":"----------------"},"notes":["E2","E2","A2","E2","D2","B1","A1"]}],"tips":"Single string riff on the A string. Perfect first riff for beginners. Memorize the fret numbers: 7-7-10-7-5-3-2."},
{"title":"Smoke on the Water","artist":"Deep Purple","genre":"Rock","diff":"Beginner","chords":[],"parts":[{"name":"Main Riff","tab":{"e":"------------------","B":"------------------","G":"0-3-5-0-3-6-5-----","D":"0-3-5-0-3-6-5-----","A":"------------------","E":"------------------"},"notes":["G3","A3","C4","G3","A3","D4","C4"]}],"tips":"Two strings at once on G and D. This is one of the most famous riffs in rock history."},
{"title":"Hotel California","artist":"Eagles","genre":"Rock","diff":"Advanced","chords":["Am","E","G","D","F","C","Dm"],"parts":[{"name":"Intro Arpeggio","tab":{"e":"0---0---3---2---1---0---2---0---","B":"1---0---0---3---1---1---3---1---","G":"2---1---0---2---2---0---2---2---","D":"2---2---0---0---3---2---0---2---","A":"0---2---2---0---3---3---0---0---","E":"----0---3---2---1---0---2-------"},"notes":["A2","E2","G4","D3","F3","C4","Dm3"]}],"tips":"This intro arpeggio is advanced but iconic. Learn it chord by chord, then connect them slowly."},
{"title":"Fast Car","artist":"Tracy Chapman","genre":"Folk/Indie","diff":"Beginner","chords":["C","G","Am","F"],"parts":[{"name":"Verse / Chorus","tab":{"e":"0---3---0---1---","B":"1---3---1---1---","G":"0---0---2---2---","D":"2---0---2---3---","A":"3---2---0---3---","E":"----3-------1---"},"notes":["C4","G4","A2","F3"]}],"tips":"Capo 2 for original key. Simple four-chord loop. Great song to practice steady rhythm."},
{"title":"Ho Hey","artist":"The Lumineers","genre":"Folk/Indie","diff":"Beginner","chords":["C","F","G","Am"],"parts":[{"name":"Verse / Chorus","tab":{"e":"0---1---3---0---","B":"1---1---3---1---","G":"0---2---0---2---","D":"2---3---0---2---","A":"3---3---2---0---","E":"----1---3-------"},"notes":["C4","F3","G4","A2"]}],"tips":"Stomp and clap rhythm. The F chord is the trickiest part for beginners \u2014 use a simplified F (x33210)."},
{"title":"Banana Pancakes","artist":"Jack Johnson","genre":"Folk/Indie","diff":"Beginner","chords":["G","C","D","E7"],"parts":[{"name":"Verse","tab":{"e":"3---0---2---0---","B":"3---1---3---3---","G":"0---0---2---1---","D":"0---2---0---0---","A":"2---3-------2---","E":"3---0---2---0---"},"notes":["G4","C4","D3","E3"]}],"tips":"Mellow fingerpicking groove. The E7 adds a bluesy twist. Very relaxed tempo."},
{"title":"Blackbird","artist":"The Beatles","genre":"Folk/Indie","diff":"Intermediate","chords":["G","Am","C","D"],"parts":[{"name":"Intro","tab":{"e":"0-2-3-2-0-3-2-0-","B":"3-----------3---","G":"0-----------0---","D":"0-----------0---","A":"2-----------2---","E":"3-----------3---"},"notes":["G4","A4","B4","A4","G4","B4","A4","G4"]}],"tips":"Fingerstyle \u2014 thumb plays bass, fingers pick melody simultaneously. One of the most beautiful guitar pieces."},
{"title":"Iris","artist":"Goo Goo Dolls","genre":"Rock","diff":"Intermediate","chords":["Bm","Bm/A","G","D","Em"],"capo":5,"parts":[{"name":"Intro / Verse","tab":{"e":"7---7---7---7---7---7---","B":"8---8---7---7---8---8---","G":"7---7---7---7---7---7---","D":"9---9---0---0---9---9---","A":"0---0---0---0---0---0---","E":"------------------------"},"notes":["B3","D4","G4","D4","B3","E4"]},{"name":"Chorus","tab":{"e":"7---7---7---10--10--7---","B":"8---8---8---10--10--8---","G":"7---7---7---11--11--7---","D":"9---9---0---0---0---0---","A":"0---0---0---0---0---0---","E":"------------------------"},"notes":["B3","G4","D4","A4","G4"]}],"tips":"Capo 5. Uses an open D tuning on top strings. The signature sound is the droning open A and D strings ringing behind the chord shapes."},
{"title":"Girls","artist":"Beastie Boys","genre":"Rock","diff":"Beginner","chords":[],"capo":0,"parts":[{"name":"Main Riff","tab":{"e":"--------------------","B":"--------------------","G":"--------------------","D":"--5-5-5-3-----------","A":"--3-3-3-1-5-5-5-3---","E":"----------3-3-3-1---"},"notes":["A2","A2","G2","C3","C3","A2"]},{"name":"Verse Groove","tab":{"e":"--------------------","B":"--------------------","G":"--------------------","D":"--5-5---3-3---------","A":"--3-3---1-1-5-5-3-3-","E":"--------0-0-3-3-1-1-"},"notes":["A2","G2","C3","A2","G2"]}],"tips":"Mostly a bass-driven song \u2014 these riffs work great on the low strings. Keep it tight and punchy, short notes not long ones."},
{"title":"Whiskey Glasses","artist":"Morgan Wallen","genre":"Country","diff":"Beginner","chords":["G","C","D","Em"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---3---0---0---","B":"--3---3---1---0---","G":"--0---0---0---0---","D":"--0---0---2---2---","A":"--2---2---3---2---","E":"3---3---0---0-----"},"notes":["G4","G4","C4","E3"]},{"name":"Chorus","tab":{"e":"--3---0---2---0---","B":"--3---1---3---0---","G":"--0---0---2---0---","D":"--0---2---0---2---","A":"--2---3-------2---","E":"3---------2---0---"},"notes":["G4","C4","D3","E3"]}],"tips":"Capo 2 for original key. Down-strum on beats 1 and 3, add up-strums on 2 and 4."},
{"title":"Cowgirls","artist":"Morgan Wallen ft. ERNEST","genre":"Country","diff":"Intermediate","chords":["G","C","D","Em"],"capo":2,"parts":[{"name":"Intro / Main Riff","tab":{"e":"--3---3---3---3---","B":"--3---3---3---3---","G":"--0---0---0---0---","D":"--0---2---0---2---","A":"--2---3---2---3---","E":"3-----------3-----"},"notes":["G4","B3","G3","D3"]},{"name":"Chorus","tab":{"e":"--3---0---2---3---","B":"--3---1---3---3---","G":"--0---0---2---0---","D":"--0---2---0---0---","A":"--2---3-------2---","E":"3---------2---3---"},"notes":["G4","C4","D3","G4"]}],"tips":"Capo 2. The G chord shape anchors the whole song. Focus on smooth G to C transitions."},
{"title":"7 Summers","artist":"Morgan Wallen","genre":"Country","diff":"Beginner","chords":["G","D","Em","C"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---2---0---0---","B":"--3---3---0---1---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------2---3---","E":"3---2---0---------"},"notes":["G4","D3","E3","C4"]},{"name":"Chorus","tab":{"e":"--3---2---0---0---","B":"--3---3---0---1---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------2---3---","E":"3---2---0---------"},"notes":["G4","D3","E3","C4"]}],"tips":"Classic G-D-Em-C four-chord loop. Slow ballad tempo, let the chords ring out fully."},
{"title":"Sand In My Boots","artist":"Morgan Wallen","genre":"Country","diff":"Beginner","chords":["G","C","D"],"capo":2,"parts":[{"name":"Verse / Chorus","tab":{"e":"--3---0---2---3---","B":"--3---1---3---3---","G":"--0---0---2---0---","D":"--0---2---0---0---","A":"--2---3-------2---","E":"3---------2---3---"},"notes":["G4","C4","D3","G4"]}],"tips":"Capo 2. Three chords the whole song. Perfect slow strum practice."},
{"title":"Wasted On You","artist":"Morgan Wallen","genre":"Country","diff":"Beginner","chords":["Em","C","G","D"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--0---0---3---2---","B":"--0---1---3---3---","G":"--0---0---0---2---","D":"--2---2---0---0---","A":"--2---3---2-------","E":"0---------3---2---"},"notes":["E3","C4","G4","D3"]},{"name":"Chorus","tab":{"e":"--0---0---3---2---","B":"--0---1---3---3---","G":"--0---0---0---2---","D":"--2---2---0---0---","A":"--2---3---2-------","E":"0---------3---2---"},"notes":["E3","C4","G4","D3"]}],"tips":"Em-C-G-D repeating loop. Fingerpick the verse, strum the chorus for dynamics."},
{"title":"More Than My Hometown","artist":"Morgan Wallen","genre":"Country","diff":"Beginner","chords":["G","C","D","Em"],"capo":2,"parts":[{"name":"Verse","tab":{"e":"--3---0---3---0---","B":"--3---1---3---0---","G":"--0---0---0---0---","D":"--0---2---0---2---","A":"--2---3---2---2---","E":"3---0---3---0-----"},"notes":["G4","C4","G4","E3"]},{"name":"Chorus","tab":{"e":"--3---2---0---0---","B":"--3---3---0---1---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------2---3---","E":"3---2---0---------"},"notes":["G4","D3","E3","C4"]}],"tips":"Capo 2 for original key. The verse alternates G and C. Chorus breaks into the four-chord loop."},
{"title":"Last Night","artist":"Morgan Wallen","genre":"Country","diff":"Intermediate","chords":["Am","F","C","G"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--0---1---0---3---","B":"--1---1---1---3---","G":"--2---2---0---0---","D":"--2---3---2---0---","A":"--0---3---3---2---","E":"----1---0---3-----"},"notes":["A2","F3","C4","G4"]},{"name":"Chorus","tab":{"e":"--0---1---0---3---","B":"--1---1---1---3---","G":"--2---2---0---0---","D":"--2---3---2---0---","A":"--0---3---3---2---","E":"----1---0---3-----"},"notes":["A2","F3","C4","G4"]}],"tips":"Am-F-C-G is one of pop-country's most common progressions. Driving eighth-note strum pattern."},
{"title":"You Proof","artist":"Morgan Wallen","genre":"Country","diff":"Beginner","chords":["G","C","D"],"capo":0,"parts":[{"name":"Verse / Chorus","tab":{"e":"--3---0---2---3---","B":"--3---1---3---3---","G":"--0---0---2---0---","D":"--0---2---0---0---","A":"--2---3-------2---","E":"3---------2---3---"},"notes":["G4","C4","D3","G4"]}],"tips":"Simple G-C-D country strum. Great song for practicing steady down-up rhythm."},
{"title":"One Thing At A Time","artist":"Morgan Wallen","genre":"Country","diff":"Beginner","chords":["G","C","D","Em"],"capo":2,"parts":[{"name":"Verse","tab":{"e":"--3---3---0---0---","B":"--3---3---1---0---","G":"--0---0---0---0---","D":"--0---0---2---2---","A":"--2---2---3---2---","E":"3---3---0---0-----"},"notes":["G4","G4","C4","E3"]},{"name":"Chorus","tab":{"e":"--3---2---0---0---","B":"--3---3---0---1---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------2---3---","E":"3---2---0---------"},"notes":["G4","D3","E3","C4"]}],"tips":"Capo 2. Title track from his biggest album. The verse sits on G and C before the chorus opens up."},
{"title":"Thought You Should Know","artist":"Morgan Wallen","genre":"Country","diff":"Beginner","chords":["G","C","D"],"capo":2,"parts":[{"name":"Verse / Chorus","tab":{"e":"--3---0---2---","B":"--3---1---3---","G":"--0---0---2---","D":"--0---2---0---","A":"--2---3-------","E":"3---------2---"},"notes":["G4","C4","D3"]}],"tips":"Capo 2. A heartfelt ballad. Let each chord breathe with slow, gentle strumming."},
{"title":"Chasin You","artist":"Morgan Wallen","genre":"Country","diff":"Beginner","chords":["G","C","D","Em"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---0---2---0---","B":"--3---1---3---0---","G":"--0---0---2---0---","D":"--0---2---0---2---","A":"--2---3-------2---","E":"3---------2---0---"},"notes":["G4","C4","D3","E3"]}],"tips":"Smooth fingerpicking or light strumming works great here. Very singable and beginner-friendly."},
{"title":"Neon Eyes","artist":"Morgan Wallen","genre":"Country","diff":"Intermediate","chords":["G","D","Em","C"],"capo":0,"parts":[{"name":"Intro / Verse","tab":{"e":"--3---2---0---0---","B":"--3---3---0---1---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------2---3---","E":"3---2---0---------"},"notes":["G4","D3","E3","C4"]}],"tips":"From his debut album. The four-chord loop drives the whole song at a mid-tempo groove."},
{"title":"Burnin It Down","artist":"Jason Aldean","genre":"Country","diff":"Beginner","chords":["G","Em","C","D"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---0---0---2---","B":"--3---0---1---3---","G":"--0---0---0---2---","D":"--0---2---2---0---","A":"--2---2---3-------","E":"3---0---0---2-----"},"notes":["G4","E3","C4","D3"]},{"name":"Chorus","tab":{"e":"--3---0---0---2---","B":"--3---0---1---3---","G":"--0---0---0---2---","D":"--0---2---2---0---","A":"--2---2---3-------","E":"3---0---0---2-----"},"notes":["G4","E3","C4","D3"]}],"tips":"Slow burn country. Let each chord ring. The G-Em-C-D loop runs the whole song."},
{"title":"Love Somebody","artist":"Morgan Wallen","genre":"Country","diff":"Beginner","chords":["G","C","D","Em"],"capo":2,"parts":[{"name":"Verse","tab":{"e":"--3---0---3---0---","B":"--3---1---3---0---","G":"--0---0---0---0---","D":"--0---2---0---2---","A":"--2---3---2---2---","E":"3---0---3---0-----"},"notes":["G4","C4","G4","E3"]},{"name":"Chorus","tab":{"e":"--3---2---0---0---","B":"--3---3---1---0---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------3---2---","E":"3---2---0---0-----"},"notes":["G4","D3","C4","E3"]}],"tips":"Capo 2. From One Thing At A Time. Midtempo strum, gentle and smooth."},
{"title":"One Man Band","artist":"Old Dominion","genre":"Country","diff":"Beginner","chords":["G","C","D","Em"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---0---0---2---","B":"--3---1---0---3---","G":"--0---0---0---2---","D":"--0---2---2---0---","A":"--2---3---2-------","E":"3---0---0---2-----"},"notes":["G4","C4","E3","D3"]},{"name":"Chorus","tab":{"e":"--3---2---0---0---","B":"--3---3---0---1---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------2---3---","E":"3---2---0---------"},"notes":["G4","D3","E3","C4"]}],"tips":"Capo 2 for original key. Smooth country pop. Alternate between fingerpicking the verse and strumming the chorus."},
{"title":"Your Man","artist":"Josh Turner","genre":"Country","diff":"Beginner","chords":["D","G","A"],"capo":0,"parts":[{"name":"Verse / Chorus","tab":{"e":"--2---3---0---2---","B":"--3---3---2---3---","G":"--2---0---2---2---","D":"--0---0---2---0---","A":"------2---0-------","E":"--2---3---0---2---"},"notes":["D3","G4","A2","D3"]}],"tips":"Deep voice, low-key groove. The D-G-A loop stays the same throughout. Nice and easy."},
{"title":"Black","artist":"Dierks Bentley","genre":"Country","diff":"Intermediate","chords":["Em","D","C","G"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--0---2---0---3---","B":"--0---3---1---3---","G":"--0---2---0---0---","D":"--2---0---2---0---","A":"--2-------3---2---","E":"0---2---0---3-----"},"notes":["E3","D3","C4","G4"]},{"name":"Chorus","tab":{"e":"--0---3---0---2---","B":"--0---3---1---3---","G":"--0---0---0---2---","D":"--2---0---2---0---","A":"--2---2---3-------","E":"0---3---0---2-----"},"notes":["E3","G4","C4","D3"]}],"tips":"Dark, moody feel. Em to D is the core movement. Let the Em chord breathe on beat 1."},
{"title":"Play It Again","artist":"Luke Bryan","genre":"Country","diff":"Beginner","chords":["G","C","D","Em"],"capo":2,"parts":[{"name":"Verse","tab":{"e":"--3---0---3---2---","B":"--3---1---3---3---","G":"--0---0---0---2---","D":"--0---2---0---0---","A":"--2---3---2-------","E":"3---0---3---2-----"},"notes":["G4","C4","G4","D3"]},{"name":"Chorus","tab":{"e":"--3---0---2---0---","B":"--3---1---3---0---","G":"--0---0---2---0---","D":"--0---2---0---2---","A":"--2---3-------2---","E":"3---------2---0---"},"notes":["G4","C4","D3","E3"]}],"tips":"Capo 2. Luke Bryan summertime feel. Down-down-up-up-down strum pattern throughout."},
{"title":"Beautiful Crazy","artist":"Luke Combs","genre":"Country","diff":"Beginner","chords":["G","C","D","Em"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---0---0---2---","B":"--3---1---0---3---","G":"--0---0---0---2---","D":"--0---2---2---0---","A":"--2---3---2-------","E":"3---0---0---2-----"},"notes":["G4","C4","E3","D3"]},{"name":"Chorus","tab":{"e":"--3---2---0---0---","B":"--3---3---1---0---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------3---2---","E":"3---2---0---------"},"notes":["G4","D3","C4","E3"]}],"tips":"One of the prettiest Luke Combs songs. Slow strum, let the chords ring. Great fingerpicking song too."},
{"title":"One Thing Right","artist":"Kane Brown","genre":"Country","diff":"Beginner","chords":["G","C","D","Em"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---0---3---2---","B":"--3---1---3---3---","G":"--0---0---0---2---","D":"--0---2---0---0---","A":"--2---3---2-------","E":"3---0---3---2-----"},"notes":["G4","C4","G4","D3"]},{"name":"Chorus","tab":{"e":"--3---0---0---2---","B":"--3---1---0---3---","G":"--0---0---0---2---","D":"--0---2---2---0---","A":"--2---3---2-------","E":"3---0---0---2-----"},"notes":["G4","C4","E3","D3"]}],"tips":"Ft. Marshmello. Pop-country crossover with a big chorus. Four chords the whole way through."},
{"title":"Blue Aint Your Color","artist":"Keith Urban","genre":"Country","diff":"Intermediate","chords":["Em","G","C","D"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--0---3---0---2---","B":"--0---3---1---3---","G":"--0---0---0---2---","D":"--2---0---2---0---","A":"--2---2---3-------","E":"0---3---0---2-----"},"notes":["E3","G4","C4","D3"]},{"name":"Chorus","tab":{"e":"--0---3---0---2---","B":"--0---3---1---3---","G":"--0---0---0---2---","D":"--2---0---2---0---","A":"--2---2---3-------","E":"0---3---0---2-----"},"notes":["E3","G4","C4","D3"]}],"tips":"Capo 4 for original key. Smooth and soulful. Great song to practice transitioning Em to G cleanly."},
{"title":"Need You Now","artist":"Lady Antebellum","genre":"Country","diff":"Beginner","chords":["G","D","Em","C"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---2---0---0---","B":"--3---3---0---1---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------2---3---","E":"3---2---0---------"},"notes":["G4","D3","E3","C4"]},{"name":"Chorus","tab":{"e":"--3---2---0---0---","B":"--3---3---0---1---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------2---3---","E":"3---2---0---------"},"notes":["G4","D3","E3","C4"]}],"tips":"Classic country-pop ballad. G-D-Em-C the whole way. Slow strumming, perfect for singing along."},
{"title":"Beer Cant Fix","artist":"Thomas Rhett","genre":"Country","diff":"Beginner","chords":["G","C","D"],"capo":0,"parts":[{"name":"Verse / Chorus","tab":{"e":"--3---0---2---3---","B":"--3---1---3---3---","G":"--0---0---2---0---","D":"--0---2---0---0---","A":"--2---3-------2---","E":"3---------2---3---"},"notes":["G4","C4","D3","G4"]}],"tips":"Ft. Jon Pardi. Upbeat and fun. Three chords, driving strum. Great crowd-pleaser."},
{"title":"Die A Happy Man","artist":"Thomas Rhett","genre":"Country","diff":"Beginner","chords":["G","C","D","Em"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---0---3---2---","B":"--3---1---3---3---","G":"--0---0---0---2---","D":"--0---2---0---0---","A":"--2---3---2-------","E":"3---0---3---2-----"},"notes":["G4","C4","G4","D3"]},{"name":"Chorus","tab":{"e":"--3---0---0---2---","B":"--3---1---0---3---","G":"--0---0---0---2---","D":"--0---2---2---0---","A":"--2---3---2-------","E":"3---0---0---2-----"},"notes":["G4","C4","E3","D3"]}],"tips":"One of the most romantic country songs. Slow and heartfelt. Let each chord ring fully."},
{"title":"In Case You Didnt Know","artist":"Brett Young","genre":"Country","diff":"Beginner","chords":["G","D","Em","C"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---2---0---0---","B":"--3---3---0---1---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------2---3---","E":"3---2---0---------"},"notes":["G4","D3","E3","C4"]},{"name":"Chorus","tab":{"e":"--3---2---0---0---","B":"--3---3---0---1---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------2---3---","E":"3---2---0---------"},"notes":["G4","D3","E3","C4"]}],"tips":"Beautiful beginner ballad. Slow strum or fingerpick. Capo 1 for original key."},
{"title":"When It Rains It Pours","artist":"Luke Combs","genre":"Country","diff":"Beginner","chords":["G","C","D"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---0---2---3---","B":"--3---1---3---3---","G":"--0---0---2---0---","D":"--0---2---0---0---","A":"--2---3-------2---","E":"3---------2---3---"},"notes":["G4","C4","D3","G4"]},{"name":"Chorus","tab":{"e":"--3---0---2---3---","B":"--3---1---3---3---","G":"--0---0---2---0---","D":"--0---2---0---0---","A":"--2---3-------2---","E":"3---------2---3---"},"notes":["G4","C4","D3","G4"]}],"tips":"Luke Combs debut hit. Three chords, upbeat strum pattern. Very singable and fun to play."},
{"title":"Thinkin Bout Me","artist":"Morgan Wallen","genre":"Country","diff":"Beginner","chords":["G","C","D","Em"],"capo":2,"parts":[{"name":"Verse","tab":{"e":"--3---0---0---3---","B":"--3---1---0---3---","G":"--0---0---0---0---","D":"--0---2---2---0---","A":"--2---3---2---2---","E":"3---0---0---3-----"},"notes":["G4","C4","E3","G4"]},{"name":"Chorus","tab":{"e":"--3---2---0---0---","B":"--3---3---1---0---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------3---2---","E":"3---2---0---------"},"notes":["G4","D3","C4","E3"]}],"tips":"Capo 2. From One Thing At A Time. Bright and catchy, mid-tempo strum."},
{"title":"Choosing Texas","artist":"Ella Langley","genre":"Country","diff":"Beginner","chords":["G","C","D","Em"],"capo":2,"parts":[{"name":"Verse","tab":{"e":"--3---3---0---0---","B":"--3---3---1---0---","G":"--0---0---0---0---","D":"--0---0---2---2---","A":"--2---2---3---2---","E":"3---3---0---0-----"},"notes":["G4","G4","C4","E3"]},{"name":"Chorus","tab":{"e":"--3---0---2---3---","B":"--3---1---3---3---","G":"--0---0---2---0---","D":"--0---2---0---0---","A":"--2---3-------2---","E":"3---------2---3---"},"notes":["G4","C4","D3","G4"]}],"tips":"Capo 2. Smooth modern country with a Texas feel. The G-C-D-Em loop is the whole song."},
{"title":"Heaven","artist":"Kane Brown","genre":"Country","diff":"Beginner","chords":["G","D","Em","C"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---2---0---0---","B":"--3---3---0---1---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------2---3---","E":"3---2---0---------"},"notes":["G4","D3","E3","C4"]},{"name":"Chorus","tab":{"e":"--3---2---0---0---","B":"--3---3---0---1---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------2---3---","E":"3---2---0---------"},"notes":["G4","D3","E3","C4"]}],"tips":"Capo 1 for original key. Slow, heartfelt ballad. G-D-Em-C the whole song. Perfect for singing along."},
{"title":"One Of Them Girls","artist":"Lee Brice","genre":"Country","diff":"Beginner","chords":["G","C","D","Em"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---0---2---0---","B":"--3---1---3---0---","G":"--0---0---2---0---","D":"--0---2---0---2---","A":"--2---3-------2---","E":"3---------2---0---"},"notes":["G4","C4","D3","E3"]},{"name":"Chorus","tab":{"e":"--3---2---0---0---","B":"--3---3---1---0---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------3---2---","E":"3---2---0---------"},"notes":["G4","D3","C4","E3"]}],"tips":"Smooth mid-tempo country. G-C-D-Em four chord loop. Great singalong song."},
{"title":"Hurricane","artist":"Luke Combs","genre":"Country","diff":"Beginner","chords":["G","C","D"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---0---2---3---","B":"--3---1---3---3---","G":"--0---0---2---0---","D":"--0---2---0---0---","A":"--2---3-------2---","E":"3---------2---3---"},"notes":["G4","C4","D3","G4"]},{"name":"Chorus","tab":{"e":"--3---0---2---3---","B":"--3---1---3---3---","G":"--0---0---2---0---","D":"--0---2---0---0---","A":"--2---3-------2---","E":"3---------2---3---"},"notes":["G4","C4","D3","G4"]}],"tips":"Luke Combs breakout hit. Driving G-C-D strum pattern. Keep energy high on the chorus."},
{"title":"Rock and A Hard Place","artist":"Bailey Zimmerman","genre":"Country","diff":"Beginner","chords":["G","D","Em","C"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"--3---2---0---0---","B":"--3---3---0---1---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------2---3---","E":"3---2---0---------"},"notes":["G4","D3","E3","C4"]},{"name":"Chorus","tab":{"e":"--3---2---0---0---","B":"--3---3---0---1---","G":"--0---2---0---0---","D":"--0---0---2---2---","A":"--2-------2---3---","E":"3---2---0---------"},"notes":["G4","D3","E3","C4"]}],"tips":"His breakout hit. G-D-Em-C the whole song. Big driving strum on the chorus, softer on the verse."},
{"title":"Get Low (Acoustic)","artist":"Dan Henig (orig. Lil Jon)","genre":"Rap","diff":"Intermediate","chords":["Em","D","C","B7"],"capo":0,"parts":[{"name":"Main Riff (Em - D - C - B7)","tab":{"e":"0-------2-0-----","B":"0-------3-1-0---","G":"0-------2-0-0---","D":"2-------0-2-0---","A":"2-------0-3-2---","E":"0-------0---0---"},"notes":["E3","D3","C4","B2"]},{"name":"Verse Riff","tab":{"e":"3-2-0---3-2-0---","B":"0-0-0---0-0-0---","G":"0-2-0---0-2-0---","D":"0-0-2---0-0-2---","A":"0-0-3---0-0-3---","E":"0-------0-------"},"notes":["G4","D3","E3","G4","D3","E3"]}],"tips":"Dan Henig plays this in Em. The magic is the Em-D-C-B7 descending bassline. Pick each string cleanly and let notes ring into each other. Start very slowly."},
{"title":"The Scientist","artist":"Coldplay","genre":"Pop/Rock","diff":"Beginner","chords":["Dm","Bb","F","C"],"capo":0,"parts":[{"name":"Intro / Verse","tab":{"e":"1-------1---0---","B":"3-------3---1---","G":"2-------3---0---","D":"0-------3---2---","A":"0-------1---3---","E":"1---------------"},"notes":["D3","Bb2","F3","C4"]},{"name":"Chorus","tab":{"e":"0---1---0---0---","B":"1---3---1---1---","G":"0---2---2---0---","D":"2---0---3---2---","A":"3---0---3---3---","E":"0---1---1---0---"},"notes":["C4","D3","Bb2","F3"]}],"tips":"One of Coldplay's most iconic songs. The Dm-Bb-F-C loop runs the whole song. Fingerpick the intro slowly."},
{"title":"Yellow","artist":"Coldplay","genre":"Pop/Rock","diff":"Beginner","chords":["B","Badd11","F#","E","Emaj7"],"capo":0,"parts":[{"name":"Intro Riff","tab":{"e":"2---2---2---2---","B":"4---4---2---0---","G":"4---4---2---1---","D":"4---4---2---2---","A":"2---2---0---2---","E":"2---2---2---0---"},"notes":["B2","F#3","E3","B3"]},{"name":"Verse / Chorus","tab":{"e":"2---2---0---0---","B":"4---4---0---0---","G":"4---4---1---1---","D":"4---4---2---2---","A":"2---2---2---2---","E":"2---2---0---0---"},"notes":["B2","B2","E3","E3"]}],"tips":"Capo 0, key of B. The whole song sits on B and E chords with small variations. Big strumming on the chorus."},
{"title":"Fix You","artist":"Coldplay","genre":"Pop/Rock","diff":"Beginner","chords":["C","G","Am","F"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"0---3---0---1---","B":"1---3---1---1---","G":"0---0---2---2---","D":"2---0---2---3---","A":"3---2---0---3---","E":"0---3---0---1---"},"notes":["C4","G4","A2","F3"]},{"name":"Chorus","tab":{"e":"0---3---0---1---","B":"1---3---1---1---","G":"0---0---2---2---","D":"2---0---2---3---","A":"3---2---0---3---","E":"0---3---0---1---"},"notes":["C4","G4","A2","F3"]}],"tips":"C-G-Am-F all the way through. Gentle fingerpicking on the verse, build to big strums on the chorus climax."},
{"title":"Clocks","artist":"Coldplay","genre":"Pop/Rock","diff":"Intermediate","chords":["Eb","Bbm","Fm"],"capo":0,"parts":[{"name":"Main Riff","tab":{"e":"----------------","B":"----------------","G":"8---5---3---5---","D":"8---5---3---5---","A":"6---3---1---3---","E":"----------------"},"notes":["Eb3","Bb2","F2","Bb2"]},{"name":"Verse","tab":{"e":"3---1---6---1---","B":"4---1---6---1---","G":"3---1---5---1---","D":"5---3---8---3---","A":"6---3---8---3---","E":"3---1---6---1---"},"notes":["Eb3","Bbm2","Fm2","Bbm2"]}],"tips":"The piano riff adapted for guitar. The G-D-A string triplet pattern is the signature. Start slow and keep it even."},
{"title":"A Sky Full Of Stars","artist":"Coldplay","genre":"Pop/Rock","diff":"Beginner","chords":["Am","F","C","G"],"capo":0,"parts":[{"name":"Verse / Chorus","tab":{"e":"0---1---0---3---","B":"1---1---1---3---","G":"2---2---0---0---","D":"2---3---2---0---","A":"0---3---3---2---","E":"0---1---0---3---"},"notes":["A2","F3","C4","G4"]}],"tips":"Am-F-C-G the whole song. Big driving strum for that anthemic festival feel. Capo 0."},
{"title":"Sparks","artist":"Coldplay","genre":"Pop/Rock","diff":"Beginner","chords":["F","Am","G","C"],"capo":0,"parts":[{"name":"Intro / Verse","tab":{"e":"1---0---3---0---","B":"1---1---3---1---","G":"2---2---0---0---","D":"3---2---0---2---","A":"3---0---2---3---","E":"1---0---3---0---"},"notes":["F3","A2","G4","C4"]}],"tips":"Hidden gem from Parachute. Soft fingerpicking, very gentle. Great song for practicing clean chord changes."},
{"title":"In My Place","artist":"Coldplay","genre":"Pop/Rock","diff":"Beginner","chords":["A","E","D","F#m"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"0---0---2---2---","B":"2---0---3---2---","G":"2---1---2---2---","D":"2---2---0---4---","A":"0---2---0---4---","E":"0---0---2---2---"},"notes":["A2","E2","D3","F#2"]},{"name":"Chorus","tab":{"e":"0---0---2---2---","B":"2---0---3---2---","G":"2---1---2---2---","D":"2---2---0---4---","A":"0---2---0---4---","E":"0---0---2---2---"},"notes":["A2","E2","D3","F#2"]}],"tips":"Driving eighth note strumming. The A-E-D-F#m loop powers the whole track. Strum hard and rhythmically."},
{"title":"Shiver","artist":"Coldplay","genre":"Pop/Rock","diff":"Intermediate","chords":["G","D","Am","C","Em"],"capo":0,"parts":[{"name":"Intro Riff","tab":{"e":"3-2-0---0---0---","B":"3-3-3---1---0---","G":"0-2-2---0---0---","D":"0-0-2---2---2---","A":"2---0---3---2---","E":"3-------0---0---"},"notes":["G4","D3","C4","Em3"]}],"tips":"Great guitar-driven Coldplay track. The intro riff walks down from G through D to Am. Keep picking clean and steady."},
{"title":"The Bad Touch","artist":"Bloodhound Gang","genre":"Rock","diff":"Beginner","chords":["Em","G","D","A"],"capo":0,"parts":[{"name":"Main Riff","tab":{"e":"0---0---3---2---","B":"0---0---3---3---","G":"0---0---0---2---","D":"2---0---0---0---","A":"2---2---2---0---","E":"0---3---3---2---"},"notes":["E3","G4","D3","A2"]},{"name":"Verse","tab":{"e":"0---3---2---0---","B":"0---3---3---2---","G":"0---0---2---2---","D":"2---0---0---2---","A":"2---2---0---0---","E":"0---3---2---0---"},"notes":["E3","G4","D3","A2"]}],"tips":"Fun late 90s rock. Em-G-D-A drives the whole song. Keep the strum punchy and upbeat."},
{"title":"Zombie","artist":"The Cranberries","genre":"Rock","diff":"Beginner","chords":["Em","C","G","D"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"0---0---3---2---","B":"0---1---3---3---","G":"0---0---0---2---","D":"2---2---0---0---","A":"2---3---2---0---","E":"0---0---3---2---"},"notes":["E3","C4","G4","D3"]},{"name":"Chorus","tab":{"e":"0---0---3---2---","B":"0---1---3---3---","G":"0---0---0---2---","D":"2---2---0---0---","A":"2---3---2---0---","E":"0---0---3---2---"},"notes":["E3","C4","G4","D3"]}],"tips":"Em-C-G-D the whole song. Power chords on the chorus for extra punch. One of the most recognisable riffs of the 90s."},
{"title":"Linger","artist":"The Cranberries","genre":"Pop/Rock","diff":"Beginner","chords":["D","Dsus2","G","A"],"capo":0,"parts":[{"name":"Intro / Verse","tab":{"e":"2---0---3---0---","B":"3---3---3---2---","G":"2---2---0---2---","D":"0---0---0---2---","A":"0---0---2---0---","E":"2---2---3---0---"},"notes":["D3","D3","G4","A2"]},{"name":"Chorus","tab":{"e":"2---0---3---0---","B":"3---3---3---2---","G":"2---2---0---2---","D":"0---0---0---2---","A":"0---0---2---0---","E":"2---2---3---0---"},"notes":["D3","D3","G4","A2"]}],"tips":"Beautiful gentle song. The D to Dsus2 movement is the signature \u2014 just lift your index finger off the B string. Fingerpick or light strum."},
{"title":"Somebody That I Used To Know","artist":"Gotye ft. Kimbra","genre":"Pop/Rock","diff":"Intermediate","chords":["Dm","C","Bb","F"],"capo":0,"parts":[{"name":"Intro Riff","tab":{"e":"0---3---1---0---","B":"1---3---3---1---","G":"2---0---3---2---","D":"0---0---3---3---","A":"0---2---1---3---","E":"1---3---1---1---"},"notes":["D3","G4","Bb2","F3"]},{"name":"Verse","tab":{"e":"0---3---1---0---","B":"1---3---3---1---","G":"2---0---3---2---","D":"0---0---3---3---","A":"0---2---1---3---","E":"1---3---1---1---"},"notes":["D3","G4","Bb2","F3"]},{"name":"Chorus","tab":{"e":"1---0---3---1---","B":"3---1---3---3---","G":"3---2---0---3---","D":"3---3---0---3---","A":"1---3---2---1---","E":"1---1---3---1---"},"notes":["Bb2","F3","G4","Bb2"]}],"tips":"The xylophone melody from the original translates beautifully to guitar. Dm-C-Bb-F is the main loop. Fingerpick the intro, strum the chorus."},
{"title":"Interstellar Theme","artist":"Hans Zimmer","genre":"Cinematic","diff":"Beginner","chords":["Am","F","C","G"],"capo":0,"parts":[{"name":"Main Theme","tab":{"e":"0---1---0---3---","B":"1---1---1---3---","G":"2---2---0---0---","D":"2---3---2---0---","A":"0---3---3---2---","E":"----1---0---3---"},"notes":["A2","F3","C4","G4"]},{"name":"Fingerpicking Pattern","tab":{"e":"0-0-1-1-0-0-3-3-","B":"1-1-1-1-1-1-3-3-","G":"2-2-2-2-0-0-0-0-","D":"2-2-3-3-2-2-0-0-","A":"0---3---3---2---","E":"----1---0---3---"},"notes":["A2","F3","C4","G4","A2","F3"]}],"tips":"Play fingerstyle with a slow, measured pace. Let every note ring fully into the next. The Am-F-C-G loop creates that iconic haunting feel."},
{"title":"Pink Pony Club","artist":"Chappell Roan","genre":"Pop","diff":"Beginner","chords":["G","D","Em","C"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"3---2---0---0---","B":"3---3---0---1---","G":"0---2---0---0---","D":"0---0---2---2---","A":"2-------2---3---","E":"3---2---0-------"},"notes":["G4","D3","E3","C4"]},{"name":"Chorus","tab":{"e":"3---2---0---0---","B":"3---3---0---1---","G":"0---2---0---0---","D":"0---0---2---2---","A":"2-------2---3---","E":"3---2---0-------"},"notes":["G4","D3","E3","C4"]}],"tips":"G-D-Em-C is the whole song. Big energetic strum on the chorus. Capo 2 to match the original key."},
{"title":"Self Aware","artist":"Temper City","genre":"Rock","diff":"Intermediate","chords":["Em","C","G","D"],"capo":0,"parts":[{"name":"Verse","tab":{"e":"0---0---3---2---","B":"0---1---3---3---","G":"0---0---0---2---","D":"2---2---0---0---","A":"2---3---2-------","E":"0---0---3---2---"},"notes":["E3","C4","G4","D3"]},{"name":"Chorus","tab":{"e":"3---0---0---2---","B":"3---1---0---3---","G":"0---0---0---2---","D":"0---2---2---0---","A":"2---3---2-------","E":"3---0---0---2---"},"notes":["G4","C4","E3","D3"]}],"tips":"Em-C-G-D drives the whole track. Energetic down strumming on the chorus."}
];
function ChordDiagram({ name }) {
const sh = CHORDS[name];
if (!sh) return null;
// Three columns left of nut:
// col1=12 (string letter), col2=34 (open/muted symbol), nut at LEFT=60
const NUM_FRETS=5, NS=6, W=320, H=210;
const TOP=44, BOT=H-22, LEFT=60, RIGHT=W-10;
const strGap=(BOT-TOP)/(NS-1);
const fretGap=(RIGHT-LEFT)/NUM_FRETS;
const STRING_LABELS=["E","A","D","G","B","e"];
const COL_LABEL=12, COL_MARKER=36;
return (
<div style={{background:"#0A0A0E",borderRadius:10,padding:"10px 6px",
border:"1px solid "+C.fret,marginBottom:8}}>
<div style={{textAlign:"center",marginBottom:6,fontWeight:700,fontSize:15,color:C.amber}}>{name}</div>
<svg width={W} height={H} style={{display:"block",margin:"0 auto"}}>
{/* Nut */}
<line x1={LEFT} y1={TOP-2} x2={LEFT} y2={BOT+2} stroke={C.text} strokeWidth={3}/>
{/* Frets */}
{Array.from({length:NUM_FRETS+1},(_,f)=>(
<line key={f} x1={LEFT+f*fretGap} y1={TOP} x2={LEFT+f*fretGap} y2={BOT} stroke={C.fret} strokeWidth={1}/>
))}
{/* Strings */}
{Array.from({length:NS},(_,s)=>(
<line key={s} x1={LEFT} y1={TOP+s*strGap} x2={RIGHT} y2={TOP+s*strGap}
stroke={C.string} strokeWidth={s===0?2:s===5?0.8:1.2}/>
))}
{/* Col 1: string name labels */}
{STRING_LABELS.map((n,s)=>(
<text key={s} x={COL_LABEL} y={TOP+s*strGap+4} textAnchor="middle"
fontSize={11} fill={C.amber} fontFamily="monospace" fontWeight="700">{n}</text>
))}
{/* Col 2: open or muted markers */}
{Array.from({length:NS},(_,s)=>{
const cy=TOP+s*strGap;
const isOpen = sh.open.includes(s);
const isMuted = sh.muted.includes(s);
if(isOpen) return (
<g key={s}>
<circle cx={COL_MARKER} cy={cy} r={7} fill="none" stroke={C.sage} strokeWidth={2}/>
</g>
);
if(isMuted) return (
<g key={s}>
<line x1={COL_MARKER-6} y1={cy-6} x2={COL_MARKER+6} y2={cy+6} stroke={C.coral} strokeWidth={2.5}/>
<line x1={COL_MARKER+6} y1={cy-6} x2={COL_MARKER-6} y2={cy+6} stroke={C.coral} strokeWidth={2.5}/>
</g>
);
return null;
})}
{/* Fret numbers */}
{Array.from({length:NUM_FRETS},(_,f)=>(
<text key={f} x={LEFT+(f+0.5)*fretGap} y={BOT+16} textAnchor="middle"
fontSize={9} fill={C.muted} fontFamily="monospace">{f+1}</text>
))}
{/* Fretted finger dots */}
{sh.dots.map(({s,f,n},i)=>{
const cx = LEFT+(f-0.5)*fretGap;
const cy = TOP+s*strGap;
return (
<g key={i}>
<circle cx={cx} cy={cy} r={14} fill="rgba(212,168,71,0.12)"/>
<circle cx={cx} cy={cy} r={11} fill={C.amber}/>
<text x={cx} y={cy+5} textAnchor="middle" fontSize={13}
fill="#1A1A1F" fontWeight="900" fontFamily="monospace">{n}</text>
</g>
);
})}
</svg>
<div style={{textAlign:"center",fontFamily:"monospace",fontSize:10,color:C.muted,marginTop:4}}>{sh.label}</div>
</div>
);
}
const NOTE_FRET_MAP = {
"E2":{str:0,fret:0},"F2":{str:0,fret:1},"F#2":{str:0,fret:2},"G2":{str:0,fret:3},
"G#2":{str:0,fret:4},"A2":{str:1,fret:0},"A#2":{str:1,fret:1},"Bb2":{str:1,fret:1},
"B2":{str:1,fret:2},"C3":{str:1,fret:3},"C#3":{str:1,fret:4},"D3":{str:2,fret:0},
"D#3":{str:2,fret:1},"E3":{str:2,fret:2},"F3":{str:2,fret:3},"F#3":{str:2,fret:4},
"G3":{str:3,fret:0},"G#3":{str:3,fret:1},"A3":{str:3,fret:2},"A#3":{str:3,fret:3},
"Bb3":{str:3,fret:3},"B3":{str:4,fret:0},"C4":{str:4,fret:1},"C#4":{str:4,fret:2},
"D4":{str:4,fret:3},"D#4":{str:4,fret:4},"E4":{str:5,fret:0},"F4":{str:5,fret:1},
"F#4":{str:5,fret:2},"G4":{str:5,fret:3},"G#4":{str:5,fret:4},"A4":{str:5,fret:5},
};
const STRING_NAMES = ["E","A","D","G","B","e"];
function Tip({ children }) {
return (
<div style={{background:"rgba(212,168,71,0.08)",borderLeft:"3px solid "+C.amber,borderRadius:"0 8px 8px 0",padding:"12px 16px",fontSize:14,color:"#D4C9A8",margin:"18px 0"}}>
{children}
</div>
);
}
function Btn({ onClick, children, variant, disabled }) {
const styles = {
primary: {background:C.amber, color:"#1A1A1F", border:"none"},
secondary: {background:C.bg3, color:C.text, border:"1px solid "+C.fret},
success: {background:C.sage, color:"#fff", border:"none"},
danger: {background:C.coral, color:"#fff", border:"none"},
};
const s = styles[variant] || styles.primary;
return (
<button onClick={onClick} disabled={disabled}
style={{...s, padding:"10px 20px", borderRadius:8, fontFamily:"sans-serif", fontSize:14, fontWeight:600,
cursor:disabled?"wait":"pointer", opacity:disabled?0.6:1, display:"inline-flex", alignItems:"center", gap:6}}>
{children}
</button>
);
}
function MicPill({ listening }) {
return (
<div style={{display:"inline-flex",alignItems:"center",gap:7,padding:"5px 11px",borderRadius:20,
background:listening?"rgba(107,143,110,0.12)":"rgba(58,58,72,0.4)",
border:"1px solid "+(listening?C.sage:C.fret)}}>
<div style={{width:7,height:7,borderRadius:"50%",background:listening?C.sage:C.fret,flexShrink:0}}/>
<span style={{fontSize:12,fontWeight:600,color:listening?C.sage:C.muted}}>
{listening ? "Mic on" : "Mic off"}
</span>
</div>
);
}
function LyricsPanel({ title, artist }) {
const query = encodeURIComponent(artist + " " + title + " lyrics");
const geniusSearch = "https://genius.com/search?q=" + encodeURIComponent(artist + " " + title);
const googleSearch = "https://www.google.com/search?q=" + query;
return (
<div style={{marginTop:16,padding:16,background:"#0E0E12",borderRadius:10,border:"1px solid "+C.fret}}>
<div style={{display:"flex",alignItems:"center",gap:8,marginBottom:12}}>
<span style={{fontSize:18}}>🎤</span>
<span style={{fontWeight:600,fontSize:14,color:C.text}}>Lyrics</span>
</div>
<p style={{fontSize:13,color:C.muted,marginBottom:12,lineHeight:1.5}}>
Tap a link below to open the lyrics in your browser and sing along while you play:
</p>
<div style={{display:"flex",flexDirection:"column",gap:8}}>
<a href={geniusSearch} target="_blank" rel="noreferrer"
style={{display:"flex",alignItems:"center",gap:10,padding:"10px 14px",
background:C.bg2,border:"1px solid "+C.fret,borderRadius:8,textDecoration:"none",color:C.text}}>
<span style={{fontSize:16}}>🔍</span>
<div>
<div style={{fontWeight:600,fontSize:13}}>Search on Genius</div>
<div style={{fontSize:11,color:C.muted,marginTop:1}}>{artist} - {title}</div>
</div>
</a>
<a href={googleSearch} target="_blank" rel="noreferrer"
style={{display:"flex",alignItems:"center",gap:10,padding:"10px 14px",
background:C.bg2,border:"1px solid "+C.fret,borderRadius:8,textDecoration:"none",color:C.text}}>
<span style={{fontSize:16}}>🌐</span>
<div>
<div style={{fontWeight:600,fontSize:13}}>Search on Google</div>
<div style={{fontSize:11,color:C.muted,marginTop:1}}>{title} lyrics</div>
</div>
</a>
</div>
<div style={{marginTop:10,fontSize:11,color:C.muted,textAlign:"right"}}>
Lyrics via <a href="https://genius.com" target="_blank" rel="noreferrer"
style={{color:C.amber,textDecoration:"none"}}>Genius.com</a>
</div>
</div>
);
}
const ENCOURAGEMENTS = ["Great job!","You nailed it!","Awesome!","Nicely done!","Keep it up!","Sounding good!","That was perfect!","You rock!"];
function SongPlayer({ parts, detected, listening, onStartMic }) {
const [partIdx, setPartIdx] = useState(0);
const [active, setActive] = useState(false);
const [noteIdx, setNoteIdx] = useState(0);
const [dotStates, setDotStates] = useState({});
const [done, setDone] = useState(false);
const [encouragement, setEncouragement] = useState("");
const timerRef = useRef(null);
const allParts = parts || [];
const currentPart = allParts[partIdx] || {};
const currentNotes = currentPart.notes || [];
// Build a flat sequence: [{partIdx, noteIdx, note}]
const sequence = allParts.flatMap((p,pi)=>
(p.notes||[]).map((note,ni)=>({pi, ni, note}))
);
const [seqIdx, setSeqIdx] = useState(0);
const stop = useCallback(()=>{
setActive(false);
if(timerRef.current) clearTimeout(timerRef.current);
setSeqIdx(0); setNoteIdx(0); setPartIdx(0);
setDotStates({}); setDone(false); setEncouragement("");
},[]);
const advance = useCallback((si, seq)=>{
if(si >= seq.length){
setActive(false);
setDone(true);
setEncouragement(ENCOURAGEMENTS[Math.floor(Math.random()*ENCOURAGEMENTS.length)]);
return;
}
const {pi, ni, note} = seq[si];
setSeqIdx(si);
setPartIdx(pi);
setNoteIdx(ni);
setDotStates(s=>({...s,[pi+"-"+ni]:"cur"}));
},[]);
const start = useCallback(()=>{
stop();
if(!listening && onStartMic) onStartMic();
setTimeout(()=>{
setActive(true);
setDone(false);
setDotStates({});
advance(0, sequence);
}, 300);
},[sequence, stop, advance, listening, onStartMic]);
useEffect(()=>{
if(!active || !detected) return;
const cur = sequence[seqIdx];
if(!cur) return;
if(detected === cur.note){
if(timerRef.current) clearTimeout(timerRef.current);
setDotStates(s=>({...s,[cur.pi+"-"+cur.ni]:"hit"}));
setTimeout(()=>advance(seqIdx+1, sequence), 400);
}
},[detected, active, seqIdx, sequence, advance]);
useEffect(()=>()=>{ if(timerRef.current) clearTimeout(timerRef.current); },[]);
const NS=6, H=260;
const TOP=44, BOT=H-24, LEFT=44;
const strGap=(BOT-TOP)/(NS-1);
const renderFretboard = (pIdx) => {
const pNotes = (allParts[pIdx]||{}).notes||[];
const N = pNotes.length || 1;
const W = Math.max(320, LEFT + N*52 + 20);
const noteX = (ni) => LEFT + ni*52 + 26;
return (
<div style={{overflowX:"auto"}}>
<svg width={W} height={H} style={{display:"block",minWidth:W}}>
{/* Strings */}
{Array.from({length:NS},(_,s)=>(
<line key={s} x1={LEFT-20} y1={TOP+s*strGap} x2={W-10} y2={TOP+s*strGap}
stroke={C.string} strokeWidth={s===0?2:s===5?0.8:1.2}/>
))}
{/* String labels */}
{STRING_NAMES.map((n,s)=>(
<text key={s} x={LEFT-26} y={TOP+s*strGap+4} textAnchor="middle"
fontSize={10} fill={C.amber} fontFamily="monospace" fontWeight="600">{n}</text>
))}
{/* Vertical beat lines */}
{pNotes.map((_,ni)=>(
<line key={ni} x1={noteX(ni)} y1={TOP-8} x2={noteX(ni)} y2={BOT+8}
stroke={C.fret} strokeWidth={1} opacity={0.3}/>
))}
{/* Notes - left to right timeline */}
{pNotes.map((note,ni)=>{
const p = NOTE_FRET_MAP[note];
if(!p) return null;
const isOpen = p.fret===0;
const cx = noteX(ni);
const cy = TOP+p.str*strGap;
const stateKey = pIdx+"-"+ni;
const state = dotStates[stateKey];
const isCur = active && state==="cur";
const isHit = state==="hit";
const isMiss = state==="miss";
const fill = isHit?C.sage:isMiss?C.coral:C.amber;
const opacity = isHit||isCur||isMiss?1:0.7;
return (
<g key={ni}>
<circle cx={cx} cy={cy} r={18} fill={isHit?"rgba(107,143,110,0.15)":isCur?"rgba(212,168,71,0.25)":"rgba(212,168,71,0.1)"}/>
<circle cx={cx} cy={cy} r={14}
fill={isOpen?(isHit?C.sage:isCur?C.amber:"transparent"):fill}
stroke={isOpen?(isHit?C.sage:isCur?C.amber:C.amber):"none"}
strokeWidth={isOpen?2.5:0}
opacity={opacity}/>
{!isOpen && (
<text x={cx} y={cy+5} textAnchor="middle" fontSize={13}
fill="#1A1A1F" fontWeight="900" fontFamily="monospace">
{p.fret}
</text>
)}
{/* Fret label above strings area */}
<text x={cx} y={TOP-6} textAnchor="middle" fontSize={9}
fill={isCur?C.amber:C.muted} fontFamily="monospace">
{isOpen?"open":("fr"+p.fret)}
</text>
{isCur && (
<text x={cx} y={TOP-16} textAnchor="middle" fontSize={20}>🎸</text>
)}
</g>
);
})}
</svg>
</div>
);
};
return (
<div style={{marginBottom:16}}>
{allParts.length > 1 && (
<div style={{display:"flex",gap:6,marginBottom:10,flexWrap:"wrap"}}>
{allParts.map((p,i)=>(
<button key={i} onClick={()=>!active&&setPartIdx(i)}
style={{padding:"3px 12px",borderRadius:14,fontSize:11,fontWeight:600,
cursor:active?"default":"pointer",fontFamily:"sans-serif",
border:"1px solid "+(i===partIdx?C.amber:C.fret),
background:i===partIdx?"rgba(212,168,71,0.1)":"transparent",
color:i===partIdx?C.amber:C.muted}}>
{p.name}
</button>
))}
</div>
)}
{allParts.length===1 && (
<div style={{fontSize:11,fontWeight:600,letterSpacing:"1.5px",textTransform:"uppercase",
color:C.amber,marginBottom:10}}>{currentPart.name}</div>
)}
<div style={{background:"#0A0A0E",borderRadius:10,padding:"10px 6px",border:"1px solid "+(active?"rgba(212,168,71,0.3)":C.fret),overflowX:"auto",
transition:"border-color 0.3s"}}>
{renderFretboard(partIdx)}
{currentNotes.length > 0 && (
<div style={{display:"flex",gap:6,flexWrap:"wrap",justifyContent:"center",marginTop:4,padding:"0 8px"}}>
{currentNotes.map((n,i)=>{
const p = NOTE_FRET_MAP[n];
return (
<div key={i} style={{display:"flex",alignItems:"center",gap:3,fontSize:10,color:C.muted}}>
{p&&p.fret!==0&&<span style={{color:C.amber,fontWeight:700}}>{i+1}</span>}
<span style={{fontFamily:"monospace",color:C.text}}>{n}</span>
{p&&<span style={{color:C.muted}}>{p.fret===0?"(open "+STRING_NAMES[p.str]+")":"("+STRING_NAMES[p.str]+" fr"+p.fret+")"}</span>}
</div>
);
})}
</div>
)}
</div>
<div style={{display:"flex",alignItems:"center",gap:10,margin:"14px 0",flexWrap:"wrap"}}>
<MicPill listening={listening}/>
{!active && !done && (
<Btn onClick={start} variant="success">🎸 Play Along</Btn>
)}
{active && (
<Btn onClick={stop} variant="danger">Stop</Btn>
)}
{done && (
<>
<span style={{fontSize:18,fontWeight:700,color:C.sage}}>{encouragement}</span>
<Btn onClick={start} variant="secondary">Play Again</Btn>
</>
)}
{active && !listening && (
<span style={{fontSize:12,color:C.coral}}>Enable mic below first</span>
)}
</div>
</div>
);
}
const OPEN_NOTES = [
{label:"Low E string",note:"E2"},{label:"A string",note:"A2"},{label:"D string",note:"D3"},
{label:"G string",note:"G3"},{label:"B string",note:"B3"},{label:"High e string",note:"E4"},
];
function OpenStringPractice({ detected, onTarget, onComplete }) {
const [idx, setIdx] = useState(0);
const [heard, setHeard] = useState(false);
const [doneSet, setDoneSet] = useState({});
const [finished, setFinished] = useState(false);
const [kudos] = useState(()=>{
const msgs = ["Great job!", "You nailed it!", "Awesome work!", "Sounding good!", "Perfect!", "Nice playing!"];
return msgs[Math.floor(Math.random()*msgs.length)];
});
const current = OPEN_NOTES[idx];
useEffect(()=>{
if (!finished) onTarget(current ? current.note : null);
},[idx, finished]);
useEffect(()=>{
if (!detected || heard || finished || !current) return;
if (detected === current.note) setHeard(true);
},[detected, heard, finished, current]);
const handleNext = () => {
setDoneSet(d=>({...d,[idx]:true}));
setHeard(false);
if (idx + 1 >= OPEN_NOTES.length) {
setFinished(true);
onTarget(null);
if (onComplete) onComplete();
} else {
setIdx(i=>i+1);
}
};
const NUM_FRETS=5, NS=6, W=320, H=200;
const TOP=44, BOT=H-24, LEFT=44, RIGHT=W-12;
const strGap=(BOT-TOP)/(NS-1);
const fretGap=(RIGHT-LEFT)/NUM_FRETS;
const STRING_LABELS=["E","A","D","G","B","e"];
return (
<div style={{margin:"20px 0"}}>
{!finished ? (
<>
<div style={{background:"#0A0A0E",borderRadius:12,padding:"16px 6px",border:"1px solid "+(heard?C.sage:C.fret),marginBottom:12,transition:"border-color 0.4s"}}>
<div style={{textAlign:"center",marginBottom:10}}>
<div style={{fontSize:13,color:C.muted,marginBottom:4}}>Pluck this string:</div>
<div style={{fontSize:32,fontWeight:800,color:heard?C.sage:C.amber,lineHeight:1,transition:"color 0.3s"}}>{current.label}</div>
{!heard && <div style={{fontSize:12,color:C.muted,marginTop:4}}>Target: {current.note}</div>}
{heard && <div style={{fontSize:13,color:C.sage,fontWeight:600,marginTop:4}}>Heard it!</div>}
</div>
<svg width={W} height={H} style={{display:"block",margin:"0 auto"}}>
<line x1={LEFT} y1={TOP-2} x2={LEFT} y2={BOT+2} stroke={C.text} strokeWidth={3}/>
{Array.from({length:NUM_FRETS+1},(_,f)=>(
<line key={f} x1={LEFT+f*fretGap} y1={TOP} x2={LEFT+f*fretGap} y2={BOT} stroke={C.fret} strokeWidth={1}/>
))}
{Array.from({length:NS},(_,s)=>{
const isTarget = s === current.note && false;
const strIdx = NS-1-s;
const targetStrIdx = OPEN_NOTES.indexOf(current);
const isHighlighted = s === idx;
return (
<g key={s}>
{isHighlighted && (
<rect x={LEFT} y={TOP+s*strGap-8} width={RIGHT-LEFT} height={16}
fill={heard?"rgba(107,143,110,0.12)":"rgba(212,168,71,0.08)"} rx={4}/>
)}
<line x1={LEFT} y1={TOP+s*strGap} x2={RIGHT} y2={TOP+s*strGap}
stroke={isHighlighted?(heard?C.sage:C.amber):C.string}
strokeWidth={isHighlighted?(heard?3:2.5):(s===0?0.8:s===5?2:1.2)}
style={{filter:isHighlighted?("drop-shadow(0 0 4px "+(heard?C.sage:C.amber)+")"):"none"}}/>
<text x={LEFT-10} y={TOP+s*strGap+4} textAnchor="middle"
fontSize={10} fill={isHighlighted?(heard?C.sage:C.amber):C.amber}
fontFamily="monospace" fontWeight={isHighlighted?"800":"400"}>
{STRING_LABELS[s]}
</text>
{doneSet[s] && (
<text x={RIGHT+10} y={TOP+s*strGap+4} textAnchor="middle"
fontSize={12} fill={C.sage}>✓</text>
)}
</g>
);
})}
{Array.from({length:NUM_FRETS},(_,f)=>(
<text key={f} x={LEFT+(f+0.5)*fretGap} y={BOT+16} textAnchor="middle"
fontSize={9} fill={C.muted} fontFamily="monospace">{f+1}</text>
))}
</svg>
</div>
{heard && (
<button onClick={handleNext}
style={{width:"100%",padding:"13px",borderRadius:10,border:"2px solid "+C.sage,
background:C.sage,color:"#fff",fontFamily:"sans-serif",fontSize:15,
fontWeight:700,cursor:"pointer",marginBottom:8}}>
{idx + 1 >= OPEN_NOTES.length ? "All done! See how I did" : "Next string →"}
</button>
)}
<div style={{display:"flex",gap:6,justifyContent:"center",flexWrap:"wrap",marginTop:8}}>
{OPEN_NOTES.map((s,i)=>(
<div key={i} style={{padding:"3px 10px",borderRadius:6,fontFamily:"monospace",fontSize:12,
border:"1px solid "+(doneSet[i]?C.sage:i===idx?C.amber:C.fret),
color:doneSet[i]?C.sage:i===idx?C.amber:C.muted,
background:doneSet[i]?"rgba(107,143,110,0.1)":i===idx?"rgba(212,168,71,0.1)":"transparent"}}>
{doneSet[i]?"✓":s.note}
</div>
))}
</div>
</>
) : (
<div style={{background:"#0A0A0E",borderRadius:12,padding:28,border:"1px solid "+C.sage,textAlign:"center"}}>
<div style={{fontSize:40,marginBottom:12}}>🎸</div>
<div style={{fontSize:22,fontWeight:800,color:C.sage,marginBottom:8}}>{kudos}</div>
<div style={{fontSize:14,color:C.muted}}>You just played all 6 open strings. Click Next to continue.</div>
<div style={{display:"flex",gap:6,justifyContent:"center",flexWrap:"wrap",marginTop:16}}>
{OPEN_NOTES.map((s,i)=>(
<div key={i} style={{padding:"3px 10px",borderRadius:6,fontFamily:"monospace",fontSize:12,
border:"1px solid "+C.sage,color:C.sage,background:"rgba(107,143,110,0.1)"}}>✓</div>
))}
</div>
</div>
)}
</div>
);
}
const LESSON6_SONGS = SONG_LIBRARY.slice(0,4);
function PlayAlong({ detected, listening }) {
const [sel, setSel] = useState(null);
const songs = LESSON6_SONGS;
const notes = sel !== null ? songs[sel].parts.flatMap(p=>p.notes||[]) : [];
return (
<div>
<div style={{display:"grid",gridTemplateColumns:"1fr 1fr",gap:12,margin:"16px 0"}}>
{songs.map((s,i)=>(
<div key={i} onClick={()=>setSel(i)}
style={{background:sel===i?"rgba(212,168,71,0.06)":"#111115",
border:"1px solid "+(sel===i?C.amber:C.fret),borderRadius:10,padding:14,cursor:"pointer"}}>
<div style={{fontWeight:600,fontSize:14,marginBottom:3}}>{s.title}</div>
<div style={{fontSize:12,color:C.muted}}>{s.artist}</div>
<div style={{fontSize:11,color:C.amber,fontWeight:600,marginTop:6}}>{s.diff}</div>
</div>
))}
</div>
{sel !== null && (
<div>
<SongPlayer parts={songs[sel].parts} detected={detected} listening={listening}/>
</div>
)}
</div>
);
}
function SongSearch({ detected, listening, onStartMic }) {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
const [song, setSong] = useState(null);
const GENRES = [...new Set(SONG_LIBRARY.map(s=>s.genre))];
const doSearch = (q) => {
setQuery(q);
if (!q.trim()){ setResults([]); return; }
const lower = q.toLowerCase();
const matches = SONG_LIBRARY.filter(s=>
s.title.toLowerCase().includes(lower) ||
s.artist.toLowerCase().includes(lower) ||
s.genre.toLowerCase().includes(lower) ||
s.diff.toLowerCase().includes(lower)
).slice(0,50);
setResults(matches);
};
const pick = (s) => { setSong(s); setResults([]); setQuery(s.title); };
const reset = () => { setSong(null); setQuery(""); setResults([]); };
const diffColor = (d) => d==="Beginner"?C.sage:d==="Intermediate"?C.amber:C.coral;
const diffBg = (d) => d==="Beginner"?"rgba(107,143,110,0.15)":d==="Intermediate"?"rgba(212,168,71,0.15)":"rgba(224,92,75,0.15)";
const FEATURED = ["Whiskey Glasses","Cowgirls","Last Night","Beautiful Crazy","Die A Happy Man","In Case You Didnt Know","Need You Now","7 Summers","Iris","Wonderwall"];
return (
<div style={{marginTop:20}}>
<div style={{fontSize:11,fontWeight:600,letterSpacing:"2px",textTransform:"uppercase",color:C.muted,marginBottom:12}}>
Search any song
</div>
<div style={{position:"relative",marginBottom:4}}>
<input value={query} onChange={e=>doSearch(e.target.value)}
placeholder="Song title, artist, or genre..."
style={{width:"100%",background:"#111115",border:"1px solid "+C.fret,borderRadius:8,
padding:"11px 36px 11px 14px",fontFamily:"sans-serif",fontSize:14,color:C.text,outline:"none"}}
/>
{query && (
<button onClick={reset}
style={{position:"absolute",right:10,top:"50%",transform:"translateY(-50%)",
background:"none",border:"none",color:C.muted,fontSize:20,cursor:"pointer",lineHeight:1,padding:0}}>
x
</button>
)}
</div>
{results.length > 0 && (
<div style={{background:"#111115",border:"1px solid "+C.fret,borderRadius:8,marginBottom:12,overflow:"hidden"}}>
{results.map((s,i)=>(
<div key={i} onClick={()=>pick(s)}
style={{padding:"10px 14px",cursor:"pointer",
borderBottom:i<results.length-1?"1px solid "+C.fret:"none",
display:"flex",justifyContent:"space-between",alignItems:"center"}}>
<div>
<div style={{fontWeight:600,fontSize:14}}>{s.title}</div>
<div style={{fontSize:12,color:C.muted,marginTop:2}}>{s.artist}</div>
</div>
<div style={{fontSize:11,color:C.amber,fontWeight:600,flexShrink:0,marginLeft:12}}>{s.genre}</div>
</div>
))}
</div>
)}
{!query && !song && (
<div style={{marginTop:12}}>
<div style={{fontSize:12,color:C.muted,marginBottom:8}}>Browse by difficulty:</div>
<div style={{display:"flex",gap:8,flexWrap:"wrap",marginBottom:16}}>
{["Beginner","Intermediate","Advanced"].map(d=>(
<button key={d} onClick={()=>doSearch(d)}
style={{padding:"4px 12px",borderRadius:16,fontSize:12,fontWeight:600,
cursor:"pointer",fontFamily:"sans-serif",
border:"1px solid "+(d==="Beginner"?C.sage:d==="Intermediate"?C.amber:C.coral),
background:"transparent",
color:d==="Beginner"?C.sage:d==="Intermediate"?C.amber:C.coral}}>
{d}
</button>
))}
</div>
<div style={{fontSize:12,color:C.muted,marginBottom:8}}>Browse by genre:</div>
<div style={{display:"flex",gap:8,flexWrap:"wrap",marginBottom:16}}>
{GENRES.map(g=>(
<button key={g} onClick={()=>doSearch(g)}
style={{padding:"4px 12px",borderRadius:16,fontSize:12,fontWeight:500,
cursor:"pointer",fontFamily:"sans-serif",border:"1px solid "+C.fret,
background:"transparent",color:C.muted}}>
{g}
</button>
))}
</div>
<div style={{fontSize:12,color:C.muted,marginBottom:8}}>Popular picks:</div>
<div style={{display:"flex",flexDirection:"column",gap:6}}>
{FEATURED.map(t=>{
const s = SONG_LIBRARY.find(x=>x.title===t);
if (!s) return null;
return (
<div key={t} onClick={()=>pick(s)}
style={{background:"#111115",border:"1px solid "+C.fret,borderRadius:8,
padding:"10px 14px",cursor:"pointer",display:"flex",
justifyContent:"space-between",alignItems:"center"}}>
<div>
<div style={{fontWeight:600,fontSize:14}}>{s.title}</div>
<div style={{fontSize:12,color:C.muted}}>{s.artist}</div>
</div>
<div style={{padding:"3px 9px",borderRadius:5,fontSize:11,fontWeight:600,
flexShrink:0,marginLeft:12,background:diffBg(s.diff),color:diffColor(s.diff)}}>
{s.diff}
</div>
</div>
);
})}
</div>
</div>
)}
{song && (
<div style={{marginTop:16}}>
<div style={{display:"flex",alignItems:"flex-start",justifyContent:"space-between",gap:12,marginBottom:12,flexWrap:"wrap"}}>
<div>
<div style={{fontWeight:700,fontSize:20,lineHeight:1.2}}>{song.title}</div>
<div style={{fontSize:13,color:C.muted,marginTop:3}}>{song.artist}</div>
<div style={{display:"flex",gap:6,flexWrap:"wrap",marginTop:8}}>
{(song.chords||[]).map(ch=>(
<span key={ch} style={{padding:"2px 9px",borderRadius:10,fontSize:12,fontWeight:600,
background:"rgba(212,168,71,0.12)",color:C.amber,border:"1px solid rgba(212,168,71,0.3)"}}>
{ch}
</span>
))}
{song.capo > 0 && (
<span style={{padding:"2px 9px",borderRadius:10,fontSize:12,fontWeight:600,
background:"rgba(212,168,71,0.12)",color:C.amber,border:"1px solid rgba(212,168,71,0.3)"}}>
Capo {song.capo}
</span>
)}
</div>
</div>
<div style={{padding:"4px 10px",borderRadius:6,fontSize:12,fontWeight:600,
flexShrink:0,background:diffBg(song.diff),color:diffColor(song.diff)}}>
{song.diff}
</div>
</div>
{song.tips && <Tip>Tip: {song.tips}</Tip>}
<SongPlayer parts={song.parts} detected={detected} listening={listening} onStartMic={onStartMic}/>
<LyricsPanel title={song.title} artist={song.artist}/>
<div style={{marginTop:16}}>
<button onClick={reset}
style={{fontSize:13,color:C.muted,background:"none",border:"none",cursor:"pointer",
padding:0,fontFamily:"sans-serif"}}>
Back to search
</button>
</div>
</div>
)}
</div>
);
}
function ChordPractice({ chords, detected, onTarget }) {
const CHORD_ROOTS = {Em:"E2", Am:"A2", G:"G2", C:"C3", D:"D3"};
const KUDOS = ["Sounds good!","Nice strum!","That sounds right!","Great job!","Keep it up!","Well done!"];
const [activeChord, setActiveChord] = useState(null);
const [heard, setHeard] = useState({});
const [kudos, setKudos] = useState({});
useEffect(()=>{
if (activeChord) onTarget(CHORD_ROOTS[activeChord]);
else onTarget(null);
},[activeChord]);
useEffect(()=>{
if (!detected || !activeChord || heard[activeChord]) return;
const root = CHORD_ROOTS[activeChord];
if (detected === root) {
setHeard(h=>({...h,[activeChord]:true}));
setKudos(k=>({...k,[activeChord]:KUDOS[Math.floor(Math.random()*KUDOS.length)]}));
}
},[detected, activeChord, heard]);
return (
<div>
<div style={{fontSize:12,color:C.muted,marginBottom:10}}>
Tap a chord to practice it, then strum on your guitar:
</div>
<div style={{display:"flex",gap:8,flexWrap:"wrap",marginBottom:16}}>
{chords.map(name=>(
<button key={name} onClick={()=>setActiveChord(activeChord===name?null:name)}
style={{padding:"6px 16px",borderRadius:20,fontFamily:"sans-serif",fontSize:13,
fontWeight:600,cursor:"pointer",
border:"1px solid "+(heard[name]?C.sage:activeChord===name?C.amber:C.fret),
background:heard[name]?"rgba(107,143,110,0.15)":activeChord===name?"rgba(212,168,71,0.1)":"transparent",
color:heard[name]?C.sage:activeChord===name?C.amber:C.muted}}>
{heard[name]?"✓ ":""}{name}
</button>
))}
</div>
{activeChord && (
<div style={{marginBottom:12}}>
<ChordDiagram name={activeChord}/>
<div style={{marginTop:10,padding:"10px 14px",borderRadius:8,
background:heard[activeChord]?"rgba(107,143,110,0.12)":"rgba(212,168,71,0.06)",
border:"1px solid "+(heard[activeChord]?C.sage:"rgba(212,168,71,0.2)"),
fontSize:13,color:heard[activeChord]?C.sage:C.muted,textAlign:"center"}}>
{heard[activeChord]
? <span style={{fontWeight:700,fontSize:15}}>🎸 {kudos[activeChord]}</span>
: <span>Strum the {activeChord} chord - listening for the root note ({CHORD_ROOTS[activeChord]})</span>}
</div>
</div>
)}
</div>
);
}
const LESSONS = [
{title:"How to Hold a Guitar", eyebrow:"Lesson 1 - Getting Started", next:"Next: Reading Tab"},
{title:"Reading Guitar Tab", eyebrow:"Lesson 2 - Tab Basics", next:"Next: Your First Notes"},
{title:"Your First Notes", eyebrow:"Lesson 3 - Open Strings", next:"Next: Your First Chord"},
{title:"Your First Chord: Em", eyebrow:"Lesson 4 - Chords", next:"Next: More Chords"},
{title:"G, C, and D Chords", eyebrow:"Lesson 5 - Essential Chords", next:"Next: Play a Song"},
{title:"Play-Along Songs", eyebrow:"Lesson 6 - Songs", next:"Next: Keep Practicing"},
{title:"Keep Going", eyebrow:"Lesson 7 - Next Steps", next:"Search Any Song"},
{title:"Search Any Song", eyebrow:"Lesson 8 - Free Play", next:null},
];
function LessonBody({ id, detected, onTarget, listening, onStartMic }) {
if (id === 0) return (
<div style={{color:"#C8C5BE",fontSize:15,lineHeight:1.75}}>
<p>Before you play a single note, get your posture right. Good posture prevents pain and speeds up progress.</p>
<Tip>Sit with the guitar waist on your right leg. Back straight, neck angled slightly up. Thumb behind the neck, fingers curling over the fretboard.</Tip>
<p>Your strumming hand hovers over the soundhole. For now use your thumb, sweeping downward across the strings.</p>
<div style={{height:1,background:C.fret,margin:"20px 0"}}/>
<p style={{fontWeight:600,color:C.text,marginBottom:8}}>Finger numbering</p>
<p style={{marginBottom:12}}>Throughout this app, the gold circles on chord diagrams show you which finger to use. Here is how they are numbered:</p>
<div style={{background:"#0A0A0E",borderRadius:12,border:"1px solid "+C.fret,padding:"24px 16px 16px",marginBottom:16,textAlign:"center"}}>
<div style={{position:"relative",display:"inline-block"}}>
{/* Gold numbers above fingers - positioned to align with emoji fingertips */}
<div style={{display:"flex",justifyContent:"center",alignItems:"flex-end",
gap:0,marginBottom:4,paddingLeft:48}}>
{[
{num:"1", ml:0},
{num:"2", ml:8},
{num:"3", ml:6},
{num:"4", ml:4},
].map(({num,ml})=>(
<div key={num} style={{marginLeft:ml,display:"flex",flexDirection:"column",alignItems:"center"}}>
<div style={{width:36,height:36,borderRadius:"50%",
background:C.amber,display:"flex",alignItems:"center",
justifyContent:"center",fontSize:18,fontWeight:900,
color:"#1A1A1F",fontFamily:"sans-serif",
boxShadow:"0 0 10px rgba(212,168,71,0.4)"}}>
{num}
</div>
<div style={{width:2,height:15,background:"rgba(212,168,71,0.4)",margin:"2px auto 0"}}/>
</div>
))}
</div>
<div style={{fontSize:260,lineHeight:1}}>🤚</div>
</div>
<div style={{display:"flex",justifyContent:"center",gap:16,marginTop:12,flexWrap:"wrap",paddingLeft:18}}>
{[["1","Index"],["2","Middle"],["3","Ring"],["4","Pinky"]].map(([num,name])=>(
<div key={num} style={{textAlign:"center",minWidth:48}}>
<div style={{fontSize:11,color:C.muted,fontFamily:"sans-serif"}}>{name}</div>
</div>
))}
</div>
</div>
<Tip>When a chord diagram shows a <strong style={{color:C.amber}}>1</strong> on a string, press that string with your index finger. <strong style={{color:C.amber}}>2</strong> = middle, <strong style={{color:C.amber}}>3</strong> = ring, <strong style={{color:C.amber}}>4</strong> = pinky.</Tip>
<div style={{height:1,background:C.fret,margin:"20px 0"}}/>
<p style={{fontSize:14,color:C.muted}}>Strings thick to thin: <strong style={{color:C.text}}>E A D G B e</strong> - remember with "Elephants And Donkeys Grow Big Ears."</p>
</div>
);
if (id === 1) {
// Wider left margin: label col at 14, marker col at 34, nut at 64
const NUM_FRETS=5, NS=6, W=340, H=210;
const TOP=44, BOT=H-24, LEFT=64, RIGHT=W-10;
const strGap=(BOT-TOP)/(NS-1);
const fretGap=(RIGHT-LEFT)/NUM_FRETS;
const LABELS=["E","A","D","G","B","e"];
const markers=[
{s:0,type:"fretted"},{s:1,type:"fretted"},{s:2,type:"open"},
{s:3,type:"open"},{s:4,type:"fretted"},{s:5,type:"fretted"}
];
return (
<div>
<p style={{color:"#C8C5BE",fontSize:15,lineHeight:1.75,marginBottom:14}}>
Each line is a string on your guitar. Numbers tell you which fret to press. 0 means open - no fingers needed.
</p>
<div style={{background:"#0A0A0E",borderRadius:10,padding:"10px 6px",border:"1px solid "+C.fret,marginBottom:16,overflowX:"auto"}}>
<div style={{textAlign:"center",fontSize:12,color:C.muted,marginBottom:8}}>Example - a G chord</div>
<svg width={W} height={H} style={{display:"block",margin:"0 auto"}}>
{/* Nut */}
<line x1={LEFT} y1={TOP-2} x2={LEFT} y2={BOT+2} stroke={C.text} strokeWidth={3}/>
{/* Fret lines */}
{Array.from({length:NUM_FRETS+1},(_,f)=>(
<line key={f} x1={LEFT+f*fretGap} y1={TOP} x2={LEFT+f*fretGap} y2={BOT} stroke={C.fret} strokeWidth={1}/>
))}
{/* Strings */}
{Array.from({length:NS},(_,s)=>(
<line key={s} x1={LEFT} y1={TOP+s*strGap} x2={RIGHT} y2={TOP+s*strGap}
stroke={C.string} strokeWidth={s===0?2:s===5?0.8:1.2}/>
))}
{/* String name labels - col 1 */}
{LABELS.map((n,s)=>(
<text key={s} x={14} y={TOP+s*strGap+4} textAnchor="middle"
fontSize={11} fill={C.amber} fontFamily="monospace" fontWeight="700">{n}</text>
))}
{/* Open/muted markers - col 2 */}
{markers.map(({s,type})=>{
const cy=TOP+s*strGap, cx=38;
if(type==="open") return (
<g key={s}>
<circle cx={cx} cy={cy} r={8} fill="none" stroke={C.sage} strokeWidth={2}/>
<text x={cx} y={cy+4} textAnchor="middle" fontSize={9}
fill={C.sage} fontFamily="monospace" fontWeight="700">0</text>
</g>
);
if(type==="muted") return (
<g key={s}>
<line x1={cx-6} y1={cy-6} x2={cx+6} y2={cy+6} stroke={C.coral} strokeWidth={2.5}/>
<line x1={cx+6} y1={cy-6} x2={cx-6} y2={cy+6} stroke={C.coral} strokeWidth={2.5}/>
</g>
);
return null;
})}
{/* Fret numbers */}
{Array.from({length:NUM_FRETS},(_,f)=>(
<text key={f} x={LEFT+(f+0.5)*fretGap} y={BOT+16} textAnchor="middle"
fontSize={9} fill={C.muted} fontFamily="monospace">{f+1}</text>
))}
{/* Fretted dots */}
{[{s:0,f:3,n:"3"},{s:1,f:2,n:"2"},{s:4,f:2,n:"1"},{s:5,f:3,n:"4"}].map(({s,f,n},i)=>{
const cx=LEFT+(f-0.5)*fretGap, cy=TOP+s*strGap;
return (
<g key={i}>
<circle cx={cx} cy={cy} r={14} fill="rgba(212,168,71,0.12)"/>
<circle cx={cx} cy={cy} r={11} fill={C.amber}/>
<text x={cx} y={cy+5} textAnchor="middle" fontSize={13}
fill="#1A1A1F" fontWeight="900" fontFamily="monospace">{n}</text>
</g>
);
})}
</svg>
</div>
<Tip>0 = open string (no fingers). A number = press that fret. X = don't play that string.</Tip>
<p style={{color:"#C8C5BE",fontSize:14,lineHeight:1.7,marginTop:12}}>
The top string (E) is the thickest - closest to you when holding the guitar. The bottom (e) is the thinnest.
</p>
</div>
);
}
const [stringsComplete, setStringsComplete] = useState(false);
if (id === 2) return (
<div>
<p style={{color:"#C8C5BE",fontSize:15,lineHeight:1.75,marginBottom:4}}>Pluck each open string one at a time. The app listens and lights it up green when it hears you - then tap Next to move on.</p>
<OpenStringPractice detected={detected} onTarget={onTarget} onComplete={()=>setStringsComplete(true)}/>
{!stringsComplete && <Tip>Open strings means no fingers on the frets - just pluck the string.</Tip>}
{stringsComplete && (
<div style={{display:"flex",gap:10,marginTop:16}}>
<Btn onClick={()=>setStringsComplete(false)} variant="secondary">Start over</Btn>
</div>
)}
</div>
);
if (id === 3) return (
<div>
<p style={{color:"#C8C5BE",fontSize:15,lineHeight:1.75,marginBottom:14}}>Em is the easiest chord on guitar. Only 2 fingers. Am uses 3 fingers in a similar shape. Tap one to see the fingering, then strum it - the mic will confirm!</p>
<ChordPractice chords={["Em","Am"]} detected={detected} onTarget={onTarget}/>
<Tip>Em: middle finger 2nd fret A string, ring finger 2nd fret D string. Strum all 6 strings.</Tip>
</div>
);
if (id === 4) return (
<div>
<p style={{color:"#C8C5BE",fontSize:15,lineHeight:1.75,marginBottom:14}}>G, C, and D unlock hundreds of songs. Tap each chord to see the fingering and strum it - the mic will confirm when it hears you!</p>
<ChordPractice chords={["G","C","D"]} detected={detected} onTarget={onTarget}/>
<Tip>Classic progression: G - D - Em - C. Drill the transitions slowly, accuracy before speed.</Tip>
</div>
);
if (id === 5) return (
<div>
<p style={{color:"#C8C5BE",fontSize:15,lineHeight:1.75,marginBottom:4}}>You know enough to play real songs. Pick one and play along!</p>
<PlayAlong detected={detected} listening={listening}/>
</div>
);
if (id === 6) return (
<div>
<p style={{color:"#C8C5BE",fontSize:15,lineHeight:1.75}}>You made it through the basics. Here is what to work on next:</p>
<Tip>Practice 15 minutes every day. Beats a 2-hour session once a week.</Tip>
<p style={{color:"#C8C5BE",fontSize:15,lineHeight:1.75}}>
<strong style={{color:C.text}}>Weeks 1-2:</strong> Drill chord switches until automatic.<br/>
<strong style={{color:C.text}}>Weeks 3-4:</strong> Try bar chords. F and Bm are tough but worth it.<br/>
<strong style={{color:C.text}}>Month 2:</strong> Fingerpicking. Arpeggios sound beautiful.
</p>
<div style={{marginTop:20,padding:20,background:"#0E0E12",borderRadius:12,
border:"1px dashed "+C.amber,textAlign:"center"}}>
<div style={{fontSize:28,marginBottom:8}}>Search</div>
<div style={{fontWeight:600,fontSize:15,marginBottom:4}}>Ready to play your favourite songs?</div>
<div style={{fontSize:13,color:C.muted}}>Lesson 8 lets you search 32 songs and play with mic feedback.</div>
</div>
</div>
);
if (id === 7) return (
<div>
<p style={{color:"#C8C5BE",fontSize:15,lineHeight:1.75,marginBottom:4}}>Search any song from our library. Tab loads instantly with mic play-along.</p>
<SongSearch detected={detected} listening={listening} onStartMic={onStartMic}/>
</div>
);
return null;
}
function MicBar({ note, hz, feedback, listening, onToggle }) {
return (
<div style={{position:"fixed",bottom:0,left:0,right:0,
background:"rgba(26,26,31,0.97)",backdropFilter:"blur(12px)",
borderTop:"1px solid "+C.fret,padding:"14px 28px",
display:"flex",alignItems:"center",gap:16,zIndex:100}}>
<button onClick={onToggle}
style={{width:46,height:46,borderRadius:"50%",
border:"2px solid "+(listening?C.coral:C.fret),
background:listening?"rgba(224,92,75,0.15)":C.bg2,
fontSize:20,cursor:"pointer",flexShrink:0,
display:"flex",alignItems:"center",justifyContent:"center"}}>
🎙️
</button>
<div style={{flex:1,minWidth:0}}>
<div style={{fontWeight:700,fontSize:20,color:C.text,lineHeight:1}}>{note||"-"}</div>
<div style={{fontFamily:"monospace",fontSize:11,color:C.muted,marginTop:2}}>{hz||"tap mic to start"}</div>
</div>
{feedback && (
<div style={{padding:"5px 14px",borderRadius:18,fontSize:13,fontWeight:600,flexShrink:0,
background:feedback==="correct"?"rgba(107,143,110,0.2)":"rgba(224,92,75,0.15)",
color:feedback==="correct"?C.sage:C.coral,
border:"1px solid "+(feedback==="correct"?C.sage:C.coral)}}>
{feedback==="correct"?"Nice!":"Try again"}
</div>
)}
</div>
);
}
const OPEN_STRINGS = [
{note:"E2", name:"Low E", freq:82.41, string:6},
{note:"A2", name:"A", freq:110.00, string:5},
{note:"D3", name:"D", freq:146.83, string:4},
{note:"G3", name:"G", freq:196.00, string:3},
{note:"B3", name:"B", freq:246.94, string:2},
{note:"E4", name:"High e", freq:329.63, string:1},
];
function HamburgerMenu({ onTuner, onBasics, onSongSearch, onClose }) {
return (
<>
<div onClick={onClose}
style={{position:"fixed",inset:0,background:"rgba(0,0,0,0.5)",zIndex:200}}/>
<div style={{position:"fixed",top:0,right:0,bottom:0,width:260,background:C.bg2,
borderLeft:"1px solid "+C.fret,zIndex:201,display:"flex",flexDirection:"column",
boxShadow:"-8px 0 32px rgba(0,0,0,0.4)"}}>
<div style={{padding:"18px 20px",borderBottom:"1px solid "+C.fret,
display:"flex",alignItems:"center",justifyContent:"space-between"}}>
<div style={{fontWeight:800,fontSize:18}}>
<span style={{color:C.amber}}>Guitar</span><span style={{color:C.text}}>Teacher</span>
</div>
<button onClick={onClose}
style={{background:"none",border:"none",color:C.muted,fontSize:22,cursor:"pointer",
lineHeight:1,padding:4}}>
x
</button>
</div>
<div style={{padding:"12px 0",flex:1}}>
{[
{icon:"🎵", label:"Guitar Tuner", sub:"Tune before you play", action:onTuner},
{icon:"📖", label:"Guitar Basics", sub:"8 lessons for beginners", action:onBasics},
{icon:"🔍", label:"Song Search", sub:"70+ songs with tab", action:onSongSearch},
].map(({icon,label,sub,action})=>(
<button key={label} onClick={action}
style={{width:"100%",padding:"16px 20px",background:"none",border:"none",
cursor:"pointer",textAlign:"left",fontFamily:"sans-serif",
display:"flex",alignItems:"center",gap:14,
borderBottom:"1px solid "+C.fret}}>
<span style={{fontSize:22,flexShrink:0}}>{icon}</span>
<div>
<div style={{fontWeight:600,fontSize:14,color:C.text}}>{label}</div>
<div style={{fontSize:11,color:C.muted,marginTop:2}}>{sub}</div>
</div>
</button>
))}
</div>
<div style={{padding:"16px 20px",borderTop:"1px solid "+C.fret,fontSize:11,color:C.muted,textAlign:"center"}}>
GuitarTeacher - Learn at your own pace
</div>
</div>
</>
);
}
function GuitarTuner({ onBack, onGoTuner, onGoBasics, onGoSongSearch }) {
const [listening, setListening] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
const [detectedNote, setDetectedNote] = useState("");
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 [readyToMark, setReadyToMark] = useState(false);
const ctxRef = useRef(null);
const streamRef = useRef(null);
const rafRef = useRef(null);
const stopMic = useCallback(()=>{
if(streamRef.current) streamRef.current.getTracks().forEach(t=>t.stop());
if(rafRef.current) cancelAnimationFrame(rafRef.current);
if(ctxRef.current) ctxRef.current.close();
ctxRef.current=null; streamRef.current=null;
setListening(false); setDetectedNote(""); setDetectedFreq(0); setCents(0); setClosestString(null);
},[]);
const startMic = useCallback(async()=>{
if(listening){ stopMic(); return; }
try {
const stream = await navigator.mediaDevices.getUserMedia({audio:true});
const ctx = new (window.AudioContext||window.webkitAudioContext)();
const analyser = ctx.createAnalyser();
analyser.fftSize = 4096;
ctx.createMediaStreamSource(stream).connect(analyser);
ctxRef.current=ctx; streamRef.current=stream;
setListening(true);
const buf = new Float32Array(analyser.fftSize);
const loop = ()=>{
rafRef.current = requestAnimationFrame(loop);
analyser.getFloatTimeDomainData(buf);
const freq = autoCorrelate(buf, ctx.sampleRate);
if(freq > 60 && freq < 1200){
const midi = Math.round(12*Math.log2(freq/440)+69);
const name = ["C","C#","D","D#","E","F","F#","G","G#","A","A#","B"][midi%12];
const oct = Math.floor(midi/12)-1;
const exactHz = 440*Math.pow(2,(midi-69)/12);
const c = Math.round(1200*Math.log2(freq/exactHz));
setDetectedNote(name+oct);
setDetectedFreq(Math.round(freq*10)/10);
setCents(c);
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]);
setClosestString(closest);
}
};
loop();
} catch(e){ alert("Mic access denied. Please allow microphone access and try again."); }
},[listening, stopMic]);
useEffect(()=>()=>stopMic(),[]);
const inTune = Math.abs(cents) <= 8;
useEffect(()=>{ if(inTune && detectedFreq>0 && !tuned[activeString?.note]) setReadyToMark(true); },[inTune, detectedFreq]);
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 (
<div style={{background:C.bg,color:C.text,minHeight:"100vh",fontFamily:"sans-serif",display:"flex",flexDirection:"column"}}>
<div style={{padding:"18px 32px",borderBottom:"1px solid "+C.fret,display:"flex",alignItems:"center",gap:16}}>
<button onClick={onBack}
style={{background:"none",border:"none",color:C.muted,fontSize:20,cursor:"pointer",padding:0,lineHeight:1}}>
←
</button>
<div style={{fontWeight:800,fontSize:22}}>
<span style={{color:C.amber}}>Guitar</span><span>Teacher</span>
<span style={{fontWeight:600,fontSize:14,color:C.muted,marginLeft:8}}>/ Tuner</span>
</div>
<button onClick={()=>setMenuOpen(true)}
style={{background:"none",border:"none",cursor:"pointer",padding:4,display:"flex",flexDirection:"column",gap:5}}>
<div style={{width:22,height:2,background:C.muted,borderRadius:1}}/>
<div style={{width:22,height:2,background:C.muted,borderRadius:1}}/>
<div style={{width:22,height:2,background:C.muted,borderRadius:1}}/>
</button>
</div>
{menuOpen && <HamburgerMenu
onTuner={()=>{setMenuOpen(false);}}
onBasics={()=>{setMenuOpen(false);onGoBasics();}}
onSongSearch={()=>{setMenuOpen(false);onGoSongSearch();}}
onClose={()=>setMenuOpen(false)}
/>}
<div style={{flex:1,display:"flex",alignItems:"center",justifyContent:"center",padding:"24px 20px 60px"}}>
<div style={{maxWidth:480,width:"100%"}}>
<div style={{textAlign:"center",marginBottom:28}}>
<div style={{fontSize:13,color:C.muted,marginBottom:6}}>Pluck any string and tune to the closest note</div>
</div>
{/* Needle meter */}
<div style={{background:"#0A0A0E",borderRadius:16,padding:"28px 20px 20px",border:"1px solid "+C.fret,marginBottom:20,textAlign:"center"}}>
<svg width={300} height={160} style={{display:"block",margin:"0 auto",overflow:"visible"}}>
{/* Arc background */}
<path d="M 30 140 A 120 120 0 0 1 270 140" fill="none" stroke={C.fret} strokeWidth={8} strokeLinecap="round"/>
{/* Colored arc zones */}
<path d="M 30 140 A 120 120 0 0 1 75 50" fill="none" stroke="rgba(224,92,75,0.3)" strokeWidth={8} strokeLinecap="round"/>
<path d="M 75 50 A 120 120 0 0 1 118 22" fill="none" stroke="rgba(212,168,71,0.3)" strokeWidth={8} strokeLinecap="round"/>
<path d="M 118 22 A 120 120 0 0 1 182 22" fill="none" stroke="rgba(107,143,110,0.5)" strokeWidth={10} strokeLinecap="round"/>
<path d="M 182 22 A 120 120 0 0 1 225 50" fill="none" stroke="rgba(212,168,71,0.3)" strokeWidth={8} strokeLinecap="round"/>
<path d="M 225 50 A 120 120 0 0 1 270 140" fill="none" stroke="rgba(224,92,75,0.3)" strokeWidth={8} strokeLinecap="round"/>
{/* Center tick */}
<line x1={150} y1={25} x2={150} y2={40} stroke={C.sage} strokeWidth={2}/>
{/* Needle */}
{detectedFreq > 0 && (
<g transform={"rotate("+needleAngle+", 150, 140)"}>
<line x1={150} y1={140} x2={150} y2={35} stroke={needleColor} strokeWidth={2.5} strokeLinecap="round"/>
<circle cx={150} cy={140} r={6} fill={needleColor}/>
</g>
)}
{/* Center dot when no signal */}
{detectedFreq === 0 && (
<circle cx={150} cy={140} r={5} fill={C.fret}/>
)}
{/* Labels */}
<text x={18} y={155} fontSize={10} fill={C.muted} textAnchor="middle">-50</text>
<text x={150} y={18} fontSize={10} fill={C.sage} textAnchor="middle">0</text>
<text x={282} y={155} fontSize={10} fill={C.muted} textAnchor="middle">+50</text>
</svg>
{detectedFreq > 0 ? (
<>
<div style={{fontSize:48,fontWeight:800,color:needleColor,lineHeight:1,margin:"8px 0 4px"}}>{detectedNote}</div>
<div style={{fontSize:13,color:C.muted,marginBottom:6}}>{detectedFreq} Hz</div>
<div style={{fontSize:18,fontWeight:700,color:tuneMsgColor}}>{tuneMsg}</div>
{!inTune && (
<div style={{fontSize:12,color:C.muted,marginTop:4}}>
{Math.abs(cents)} cents {cents < 0 ? "flat" : "sharp"}
</div>
)}
</>
) : (
<div style={{fontSize:15,color:C.muted,marginTop:8}}>
{listening ? "Pluck a string..." : "Tap the mic to start"}
</div>
)}
</div>
{/* String selector tabs */}
<div style={{display:"flex",gap:6,marginBottom:12,flexWrap:"wrap"}}>
{OPEN_STRINGS.map((s,i)=>{
const isActive = activeString && activeString.note===s.note;
const isDone = tuned[s.note];
return (
<button key={s.note} onClick={()=>{setLockedString(s);setCurrentStringIdx(i);setReadyToMark(false);}}
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}}>
<div style={{fontSize:15,fontWeight:800,color:isDone?C.sage:isActive?C.amber:C.text}}>
{s.name==="Low E"?"E":s.name==="High e"?"e":s.name}
</div>
<div style={{fontSize:9,color:isDone?C.sage:C.muted,marginTop:1}}>{isDone?"✓":s.note}</div>
</button>
);
})}
</div>
{/* Guitar string visual */}
<div style={{background:"#0A0A0E",borderRadius:12,padding:"18px 16px",border:"1px solid "+(inTune&&detectedFreq>0?C.sage:C.fret),marginBottom:12,transition:"border-color 0.3s"}}>
<div style={{fontSize:12,fontWeight:600,textTransform:"uppercase",letterSpacing:"1px",
color:C.muted,marginBottom:14,textAlign:"center"}}>
{detectedFreq>0 ? (
<span>Plucking <span style={{color:inTune?C.sage:C.amber,fontWeight:800}}>
{activeString.name} string
</span> {inTune ? "- In tune!" : (cents<0?"- Tune up (too flat)":"- Tune down (too sharp)")}</span>
) : <span>Tap a string above, then pluck it on your guitar</span>}
</div>
<svg width="100%" viewBox="0 0 340 130" style={{display:"block"}}>
{OPEN_STRINGS.map((s,i)=>{
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;
const stringCol = isActive?(inTune?C.sage:C.amber):"#5A5A6A";
const glowOpacity = isActive ? 1 : 0;
return (
<g key={s.note}>
{isActive && (
<line x1={x1} y1={y} x2={x2} y2={y}
stroke={inTune?C.sage:C.amber} strokeWidth={thickness+6} opacity={0.15}/>
)}
<line x1={x1} y1={y} x2={x2} y2={y}
stroke={stringCol} strokeWidth={thickness}
strokeLinecap="round"
style={{filter:isActive?("drop-shadow(0 0 4px "+(inTune?C.sage:C.amber)+")"):"none"}}/>
<text x={8} y={y+4} textAnchor="middle" fontSize={9}
fill={isActive?(inTune?C.sage:C.amber):C.muted}
fontFamily="monospace" fontWeight={isActive?"800":"400"}>
{s.name==="Low E"?"E":s.name==="High e"?"e":s.name}
</text>
<text x={332} y={y+4} textAnchor="middle" fontSize={9}
fill={isActive?(inTune?C.sage:C.amber):C.muted}
fontFamily="monospace" fontWeight={isActive?"800":"400"}>
{s.note}
</text>
{isActive && (
<>
<circle cx={160} cy={y} r={8} fill={inTune?C.sage:C.amber} opacity={0.9}/>
<text x={160} y={y+4} textAnchor="middle" fontSize={9}
fill="#1A1A1F" fontWeight="900" fontFamily="monospace">
{inTune?"✓":cents<0?"↑":"↓"}
</text>
</>
)}
</g>
);
})}
<line x1={20} y1={10} x2={20} y2={120} stroke={C.muted} strokeWidth={3} strokeLinecap="round"/>
<line x1={320} y1={10} x2={320} y2={120} stroke={C.fret} strokeWidth={1}/>
</svg>
</div>
{/* Mark tuned + Next */}
<div style={{display:"flex",gap:10,marginBottom:12}}>
{readyToMark && !tuned[activeString?.note] && (
<button onClick={()=>{
setTuned(t=>({...t,[activeString.note]:true}));
setReadyToMark(false);
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
</button>
)}
{allTuned && (
<button onClick={onBack}
style={{flex:1,padding:"11px",borderRadius:10,border:"2px solid "+C.sage,
background:C.sage,color:"#fff",fontFamily:"sans-serif",
fontSize:14,fontWeight:700,cursor:"pointer"}}>
All tuned! Back to menu
</button>
)}
</div>
{/* Mic button */}
<button onClick={startMic}
style={{width:"100%",padding:"14px",borderRadius:12,border:"2px solid "+(listening?C.coral:C.amber),
background:listening?"rgba(224,92,75,0.1)":"rgba(212,168,71,0.08)",
color:listening?C.coral:C.amber,fontFamily:"sans-serif",fontSize:15,fontWeight:700,
cursor:"pointer",display:"flex",alignItems:"center",justifyContent:"center",gap:10}}>
<span style={{fontSize:20}}>🎙️</span>
{listening ? "Stop Listening" : "Start Listening"}
</button>
</div>
</div>
</div>
);
}
function SplashCards({ onTuner, onBasics, onSearch }) {
const [hovered, setHovered] = useState(null);
const cards = [
{key:"tuner", icon:"🎵", title:"Tune your guitar", desc:"Get in tune before you play. Pluck a string and the tuner guides you.", action:onTuner},
{key:"basics", icon:"📖", title:"Start from basics", desc:"New to guitar? Learn step by step from the very beginning.", action:onBasics},
{key:"search", icon:"🔍", title:"Search any song", desc:"Already know the basics? Find a song and play along with the mic.", action:onSearch},
];
return (
<div style={{display:"flex",flexDirection:"column",gap:12}}>
{cards.map(({key,icon,title,desc,action})=>(
<button key={key} onClick={action}
onMouseEnter={()=>setHovered(key)}
onMouseLeave={()=>setHovered(null)}
style={{background:C.bg2,
border:"1px solid "+(hovered===key?C.sage:C.amber),
borderRadius:16,padding:"20px 22px",
cursor:"pointer",textAlign:"left",fontFamily:"sans-serif",
display:"flex",alignItems:"center",gap:18,
transition:"border-color 0.2s"}}>
<div style={{fontSize:36,flexShrink:0}}>{icon}</div>
<div style={{flex:1}}>
<div style={{fontWeight:700,fontSize:16,color:C.text,marginBottom:3}}>{title}</div>
<div style={{fontSize:13,color:C.muted,lineHeight:1.5}}>{desc}</div>
</div>
<div style={{color:hovered===key?C.sage:C.amber,fontSize:20,flexShrink:0,transition:"color 0.2s"}}>›</div>
</button>
))}
</div>
);
}
function App() {
const [splash, setSplash] = useState(true);
const [showTuner, setShowTuner] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
const [lesson, setLesson] = useState(0);
const [listening, setListening] = useState(false);
const [pitchNote, setPitchNote] = useState("");
const [pitchHz, setPitchHz] = useState("");
const [feedback, setFeedback] = useState(null);
const [target, setTarget] = useState(null);
const [detected, setDetected] = useState(null);
const ctxRef = useRef(null);
const analyserRef = useRef(null);
const streamRef = useRef(null);
const rafRef = useRef(null);
const fbTimerRef = useRef(null);
const stopMic = useCallback(()=>{
if (streamRef.current) streamRef.current.getTracks().forEach(t=>t.stop());
if (rafRef.current) cancelAnimationFrame(rafRef.current);
if (ctxRef.current) ctxRef.current.close();
ctxRef.current=null; analyserRef.current=null; streamRef.current=null;
setListening(false); setPitchNote(""); setPitchHz(""); setDetected(null);
},[]);
const startMic = useCallback(async()=>{
if (listening){ stopMic(); return; }
try {
const stream = await navigator.mediaDevices.getUserMedia({audio:true});
const ctx = new (window.AudioContext||window.webkitAudioContext)();
const analyser = ctx.createAnalyser();
analyser.fftSize = 2048;
ctx.createMediaStreamSource(stream).connect(analyser);
streamRef.current=stream; ctxRef.current=ctx; analyserRef.current=analyser;
setListening(true); setPitchHz("listening...");
const buf = new Float32Array(analyser.fftSize);
const loop = ()=>{
rafRef.current = requestAnimationFrame(loop);
analyser.getFloatTimeDomainData(buf);
const freq = autoCorrelate(buf, ctx.sampleRate);
if (freq > 0){
const r = freqToNote(freq);
if (r){
setPitchNote(r.name);
setPitchHz(Math.round(freq)+" Hz");
setDetected(r.name);
}
}
};
loop();
} catch(e){ setPitchHz("mic denied"); }
},[listening, stopMic]);
useEffect(()=>{
if (!detected || !target) return;
if (detected === target){
if (fbTimerRef.current) return;
setFeedback("correct");
fbTimerRef.current = setTimeout(()=>{ setFeedback(null); fbTimerRef.current=null; }, 1200);
}
},[detected, target]);
useEffect(()=>()=>stopMic(),[]);
const go = (n)=>{ setLesson(n); setSplash(false); };
const prog = Math.round((lesson+1)/LESSONS.length*100);
const css = "*{box-sizing:border-box;margin:0;padding:0} body{background:#1A1A1F} input::placeholder{color:#9A9490} p+p{margin-top:10px}";
if (showTuner) return <GuitarTuner
onBack={()=>setShowTuner(false)}
onGoTuner={()=>{}}
onGoBasics={()=>{setShowTuner(false);setSplash(false);setLesson(0);}}
onGoSongSearch={()=>{setShowTuner(false);setSplash(false);setLesson(7);}}
/>;
if (splash) return (
<div style={{background:C.bg,color:C.text,minHeight:"100vh",fontFamily:"sans-serif",display:"flex",flexDirection:"column"}}>
<style>{css}</style>
<div style={{padding:"18px 32px",borderBottom:"1px solid "+C.fret,fontWeight:800,fontSize:22,
display:"flex",alignItems:"center",justifyContent:"space-between"}}>
<div><span style={{color:C.amber}}>Guitar</span><span>Teacher</span></div>
<button onClick={()=>setMenuOpen(true)}
style={{background:"none",border:"none",cursor:"pointer",padding:4,display:"flex",flexDirection:"column",gap:5}}>
<div style={{width:22,height:2,background:C.muted,borderRadius:1}}/>
<div style={{width:22,height:2,background:C.muted,borderRadius:1}}/>
<div style={{width:22,height:2,background:C.muted,borderRadius:1}}/>
</button>
</div>
{menuOpen && <HamburgerMenu
onTuner={()=>{setMenuOpen(false);setShowTuner(true);setSplash(false);}}
onBasics={()=>{setMenuOpen(false);setShowTuner(false);setSplash(false);go(0);}}
onSongSearch={()=>{setMenuOpen(false);setShowTuner(false);setSplash(false);go(7);}}
onClose={()=>setMenuOpen(false)}
/>}
<div style={{flex:1,display:"flex",flexDirection:"column",alignItems:"center",justifyContent:"center",padding:"32px 24px 60px"}}>
<div style={{maxWidth:480,width:"100%"}}>
<div style={{textAlign:"center",marginBottom:36}}>
<div style={{fontSize:56,lineHeight:1,marginBottom:16}}>🎸</div>
<h1 style={{fontWeight:800,fontSize:32,lineHeight:1.2,marginBottom:10}}>
Learn guitar, <span style={{color:C.amber}}>your way.</span>
</h1>
<p style={{fontSize:14,color:C.muted,lineHeight:1.7}}>
Pick where you want to start.
</p>
</div>
<SplashCards
onTuner={()=>setShowTuner(true)}
onBasics={()=>go(0)}
onSearch={()=>go(7)}
/>
</div>
</div>
</div>
);
return (
<div style={{background:C.bg,color:C.text,minHeight:"100vh",fontFamily:"sans-serif",overflowX:"hidden"}}>
<style>{css}</style>
<div style={{padding:"16px 28px",display:"flex",alignItems:"center",justifyContent:"space-between",borderBottom:"1px solid "+C.fret}}>
<button onClick={()=>setSplash(true)}
style={{fontWeight:800,fontSize:21,background:"none",border:"none",cursor:"pointer",color:C.text,padding:0,fontFamily:"sans-serif"}}>
<span style={{color:C.amber}}>Guitar</span><span>Teacher</span>
</button>
<div style={{display:"flex",alignItems:"center",gap:16}}>
<div style={{display:"flex",alignItems:"center",gap:8,fontSize:12,color:C.muted}}>
<span>{lesson+1}/{LESSONS.length}</span>
<div style={{width:80,height:3,background:C.bg3,borderRadius:2,overflow:"hidden"}}>
<div style={{height:"100%",background:C.amber,width:prog+"%",borderRadius:2,transition:"width 0.4s"}}/>
</div>
</div>
<button onClick={()=>setMenuOpen(true)}
style={{background:"none",border:"none",cursor:"pointer",padding:4,display:"flex",flexDirection:"column",gap:5}}>
<div style={{width:22,height:2,background:C.muted,borderRadius:1}}/>
<div style={{width:22,height:2,background:C.muted,borderRadius:1}}/>
<div style={{width:22,height:2,background:C.muted,borderRadius:1}}/>
</button>
</div>
</div>
{menuOpen && <HamburgerMenu
onTuner={()=>{setMenuOpen(false);setShowTuner(true);setSplash(false);}}
onBasics={()=>{setMenuOpen(false);setShowTuner(false);setSplash(false);setLesson(0);}}
onSongSearch={()=>{setMenuOpen(false);setShowTuner(false);setSplash(false);setLesson(7);}}
onClose={()=>setMenuOpen(false)}
/>}
<div style={{maxWidth:820,margin:"0 auto",padding:"28px 20px 150px"}}>
<div style={{display:"flex",gap:6,flexWrap:"wrap",marginBottom:24}}>
{LESSONS.map((l,i)=>(
<button key={i} onClick={()=>setLesson(i)}
style={{padding:"4px 12px",borderRadius:18,fontSize:12,fontWeight:500,cursor:"pointer",fontFamily:"sans-serif",
border:"1px solid "+(i<lesson?C.sage:i===lesson?C.amber:C.fret),
background:i===lesson?"rgba(212,168,71,0.08)":"transparent",
color:i<lesson?C.sage:i===lesson?C.amber:C.muted}}>
{i+1}. {i===7?"Song Search":l.title.split(":")[0]}
</button>
))}
</div>
<div style={{background:C.bg2,borderRadius:14,border:"1px solid "+C.fret,padding:32,marginBottom:20}}>
<div style={{fontSize:10,fontWeight:600,letterSpacing:"2px",textTransform:"uppercase",color:C.amber,marginBottom:8}}>
{LESSONS[lesson].eyebrow}
</div>
<h1 style={{fontWeight:700,fontSize:26,lineHeight:1.2,marginBottom:18}}>{LESSONS[lesson].title}</h1>
<LessonBody id={lesson} detected={detected} onTarget={setTarget} listening={listening} onStartMic={startMic}/>
<div style={{display:"flex",gap:10,flexWrap:"wrap",marginTop:24}}>
{lesson > 0 && <Btn onClick={()=>setLesson(l=>l-1)} variant="secondary">Back</Btn>}
{LESSONS[lesson].next
? <Btn onClick={()=>setLesson(l=>l+1)}>{LESSONS[lesson].next}</Btn>
: null}
</div>
</div>
</div>
<MicBar note={pitchNote} hz={pitchHz} feedback={feedback} listening={listening} onToggle={startMic}/>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App/>);
</script>
</body>
</html>