From 3aadf1afbb69ed822b1a211510cb3ddf50933d0a Mon Sep 17 00:00:00 2001 From: vadimwit Date: Mon, 9 Mar 2026 22:47:41 +0000 Subject: [PATCH] Bass fretboard addition --- src/App.jsx | 11 +-- src/components/BassFretboard.jsx | 149 +++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 5 deletions(-) create mode 100644 src/components/BassFretboard.jsx diff --git a/src/App.jsx b/src/App.jsx index 5ccf4fe..d3c118a 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -3,6 +3,7 @@ import AudioCapture from './components/AudioCapture' import ProgressionBanner from './components/ProgressionBanner' import ProgressionSuggestions from './components/ProgressionSuggestions' import Fretboard from './components/Fretboard' +import BassFretboard from './components/BassFretboard' import Tuner from './components/Tuner' import Piano from './components/Piano' import Settings from './components/Settings' @@ -42,7 +43,7 @@ export default function App() { const [isListening, setIsListening] = useState(false) // ── Instrument view + tuner ─────────────────────────────────────────────────── - const [instrument, setInstrument] = useState('guitar') // 'guitar' | 'piano' + const [instrument, setInstrument] = useState('guitar') // 'guitar' | 'bass' | 'piano' const [showTuner, setShowTuner] = useState(false) const [showDebug, setShowDebug] = useState(false) const [monoColor, setMonoColor] = useState(false) @@ -394,6 +395,7 @@ export default function App() { className="appearance-none bg-surface border border-border hover:border-gray-500 focus:border-accent focus:outline-none rounded-lg pl-3 pr-7 py-1 text-sm text-gray-200 cursor-pointer transition-colors" > + @@ -517,10 +519,9 @@ export default function App() { {/* ── Instrument + progressions row ── */}
- {instrument === 'guitar' - ? - : - } + {instrument === 'guitar' && } + {instrument === 'bass' && } + {instrument === 'piano' && }
diff --git a/src/components/BassFretboard.jsx b/src/components/BassFretboard.jsx new file mode 100644 index 0000000..fa99e58 --- /dev/null +++ b/src/components/BassFretboard.jsx @@ -0,0 +1,149 @@ +import { getPentatonicScale, getFullScale, getChordTones, NOTES } from '../lib/theory' + +// Standard bass tuning (top of diagram = highest string) +const STRINGS = [ + { label: 'G', root: 7, thickness: 1.5 }, + { label: 'D', root: 2, thickness: 2 }, + { label: 'A', root: 9, thickness: 2.5 }, + { label: 'E', root: 4, thickness: 3 }, +] + +const NUM_FRETS = 13 +const FRET_MARKERS = [3, 5, 7, 9] +const DOUBLE_MARKER = 12 + +// Layout +const NUT_X = 40 +const OPEN_X = 18 +const FRET_W = 52 +const STRING_H = 36 // wider spacing than guitar — 4 strings feel more spread +const PAD_T = 28 +const PAD_B = 18 +const BOARD_W = NUT_X + (NUM_FRETS - 1) * FRET_W + 10 +const BOARD_H = PAD_T + 3 * STRING_H + PAD_B +const DOT_R = 10 + +const fretX = f => NUT_X + (f - 0.5) * FRET_W +const stringY = si => PAD_T + si * STRING_H + +function noteColor(isChordTone, isPenta, isScale, mono = false) { + if (isChordTone) return { fill: '#a855f7', text: '#fff' } + if (isPenta) return mono ? { fill: '#c084fc', text: '#1e1b4b' } : { fill: '#f59e0b', text: '#000' } + if (isScale) return mono ? { fill: '#e9d5ff', text: '#581c87' } : { fill: '#374151', text: '#d1d5db' } + return null +} + +export default function BassFretboard({ keyInfo, currentChord, monoColor = false }) { + const { root, mode } = keyInfo ?? {} + + if (!root) return null + + const pentaSet = new Set(getPentatonicScale(root, mode).map(n => NOTES.indexOf(n))) + const scaleSet = new Set(getFullScale(root, mode).map(n => NOTES.indexOf(n))) + const chordSet = currentChord + ? new Set(getChordTones(currentChord).map(n => NOTES.indexOf(n))) + : new Set() + + return ( +
+

+ Bass — {root} {mode} + {currentChord && / {currentChord}} +

+ +
+ + {/* Fretboard background */} + + + {/* Position marker dots (centred between strings 1–2) */} + {FRET_MARKERS.map(f => ( + + ))} + {/* Double dot at 12 */} + + + + {/* Fret lines */} + {Array.from({ length: NUM_FRETS - 1 }, (_, i) => i + 1).map(f => ( + + ))} + + {/* Nut */} + + + {/* Strings — thicker as pitch drops */} + {STRINGS.map((s, si) => ( + + ))} + + {/* Fret numbers */} + {[3, 5, 7, 9, 12].map(f => ( + {f} + ))} + + {/* String labels */} + {STRINGS.map((s, si) => ( + {s.label} + ))} + + {/* Note dots */} + {STRINGS.flatMap((str, si) => + Array.from({ length: NUM_FRETS }, (_, fi) => { + const pc = (str.root + fi) % 12 + const color = noteColor(chordSet.has(pc), pentaSet.has(pc), scaleSet.has(pc), monoColor) + if (!color) return null + + const cx = fi === 0 ? OPEN_X : fretX(fi) + const cy = stringY(si) + + return ( + + + + {NOTES[pc]} + + + ) + }) + )} + +
+ +
+ Chord tone + Pentatonic + Scale +
+
+ ) +}