Music
Guitar Chords
Interactive chord player with Web Audio synthesis and famous progressions. Press Space to strum.
Root Note
Chord Type
Available Chords
—
Select a chord to begin
Space / Enter = strum down · Shift+Space = strum up · ← → = prev/next chord
Keyboard Shortcuts
C D E F G A B — select & strum major chord
Shift + C/D/E… — select & strum minor chord (Cm, Dm…)
1 2 3 4 5 6 — pluck string 1–6 (low E → high e)
Space / Enter — strum current chord (down)
Shift + Space — strum current chord (up)
← → — navigate to prev/next chord
Developer Reference
Core Algorithm & Standalone Script
Standalone, zero-dependency JavaScript implementation powering this tool. Free to inspect, copy, and build upon.
// Web Audio Context & Karplus-Strong Synthesis
let audioCtx = null;
function getCtx() {
if (!audioCtx) {
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
}
if (audioCtx.state === 'suspended') {
audioCtx.resume();
}
return audioCtx;
}
function pluckString(freq, vol, startTime) {
const ctx = getCtx();
const t = startTime != null ? startTime : ctx.currentTime;
const dur = 2.0;
const N = Math.round(ctx.sampleRate / freq);
const buf = ctx.createBuffer(1, N, ctx.sampleRate);
const data = buf.getChannelData(0);
for (let i = 0; i < N; i++) {
data[i] = Math.random() * 2 - 1;
}
const src = ctx.createBufferSource();
src.buffer = buf;
src.loop = true;
const filt = ctx.createBiquadFilter();
filt.type = 'lowpass';
filt.frequency.value = freq * 4;
filt.Q.value = 15;
const gain = ctx.createGain();
gain.gain.setValueAtTime(vol, t);
gain.gain.exponentialRampToValueAtTime(0.001, t + dur);
src.connect(filt);
filt.connect(gain);
gain.connect(ctx.destination);
src.start(t);
src.stop(t + dur);
}
function strumChord(frets, direction) {
const baseFreqs = [82.41, 110.0, 146.83, 196.0, 246.94, 329.63];
const S = Math.pow(2, 1 / 12);
const order = direction === 'up' ? [5, 4, 3, 2, 1, 0] : [0, 1, 2, 3, 4, 5];
const ctx = getCtx();
order.forEach((si, i) => {
if (frets[si] < 0) return;
const freq = baseFreqs[si] * Math.pow(S, frets[si]);
pluckString(freq, 0.4, ctx.currentTime + i * 0.022);
});
}
// Chord Data
const CHORDS = [
{ id: 'C', root: 'C', type: 'major', name: 'C Major', frets: [-1, 3, 2, 0, 1, 0] },
{ id: 'D', root: 'D', type: 'major', name: 'D Major', frets: [-1, -1, 0, 2, 3, 2] },
{ id: 'E', root: 'E', type: 'major', name: 'E Major', frets: [0, 2, 2, 1, 0, 0] },
{ id: 'F', root: 'F', type: 'major', name: 'F Major', frets: [1, 1, 2, 3, 3, 1] },
{ id: 'G', root: 'G', type: 'major', name: 'G Major', frets: [3, 2, 0, 0, 0, 3] },
{ id: 'A', root: 'A', type: 'major', name: 'A Major', frets: [-1, 0, 2, 2, 2, 0] },
{ id: 'B', root: 'B', type: 'major', name: 'B Major', frets: [-1, 2, 4, 4, 4, 2] },
{ id: 'Am', root: 'A', type: 'minor', name: 'A Minor', frets: [-1, 0, 2, 2, 1, 0] },
{ id: 'Bm', root: 'B', type: 'minor', name: 'B Minor', frets: [-1, 2, 4, 4, 3, 2] },
{ id: 'Cm', root: 'C', type: 'minor', name: 'C Minor', frets: [-1, 3, 5, 5, 4, 3] },
{ id: 'Dm', root: 'D', type: 'minor', name: 'D Minor', frets: [-1, -1, 0, 2, 3, 1] },
{ id: 'Em', root: 'E', type: 'minor', name: 'E Minor', frets: [0, 2, 2, 0, 0, 0] },
{ id: 'Fm', root: 'F', type: 'minor', name: 'F Minor', frets: [1, 1, 3, 3, 2, 1] },
{ id: 'Gm', root: 'G', type: 'minor', name: 'G Minor', frets: [3, 5, 5, 3, 3, 3] },
{ id: 'A7', root: 'A', type: '7th', name: 'A7', frets: [-1, 0, 2, 0, 2, 0] },
{ id: 'B7', root: 'B', type: '7th', name: 'B7', frets: [-1, 2, 1, 2, 0, 2] },
{ id: 'C7', root: 'C', type: '7th', name: 'C7', frets: [-1, 3, 2, 3, 1, 0] },
{ id: 'D7', root: 'D', type: '7th', name: 'D7', frets: [-1, -1, 0, 2, 1, 2] },
{ id: 'E7', root: 'E', type: '7th', name: 'E7', frets: [0, 2, 0, 1, 0, 0] },
{ id: 'G7', root: 'G', type: '7th', name: 'G7', frets: [3, 2, 0, 0, 0, 1] },
{ id: 'Cmaj7', root: 'C', type: 'maj7', name: 'C Maj7', frets: [-1, 3, 2, 0, 0, 0] },
{ id: 'Emaj7', root: 'E', type: 'maj7', name: 'E Maj7', frets: [0, 2, 1, 1, 0, 0] },
{ id: 'Gmaj7', root: 'G', type: 'maj7', name: 'G Maj7', frets: [3, 2, 0, 0, 0, 2] },
{ id: 'Amaj7', root: 'A', type: 'maj7', name: 'A Maj7', frets: [-1, 0, 2, 1, 2, 0] },
{ id: 'Am7', root: 'A', type: 'min7', name: 'A Min7', frets: [-1, 0, 2, 0, 1, 0] },
{ id: 'Dm7', root: 'D', type: 'min7', name: 'D Min7', frets: [-1, -1, 0, 2, 1, 1] },
{ id: 'Em7', root: 'E', type: 'min7', name: 'E Min7', frets: [0, 2, 2, 0, 3, 0] },
{ id: 'Asus2', root: 'A', type: 'sus', name: 'A Sus2', frets: [-1, 0, 2, 2, 0, 0] },
{ id: 'Dsus4', root: 'D', type: 'sus', name: 'D Sus4', frets: [-1, -1, 0, 2, 3, 3] },
{ id: 'E5', root: 'E', type: 'power', name: 'E Power', frets: [0, 2, 2, -1, -1, -1] },
{ id: 'A5', root: 'A', type: 'power', name: 'A Power', frets: [-1, 0, 2, 2, -1, -1] },
];
// Progressions Data
const PROGRESSIONS = [
{ name: 'I–IV–V–I (C Major)', chords: ['C', 'F', 'G', 'C'], bpm: 80 },
{ name: 'I–V–vi–IV (G Major)', chords: ['G', 'D', 'Am', 'C'], bpm: 80 },
{ name: '12-Bar Blues (E)', chords: ['E', 'E', 'E', 'E', 'A', 'A', 'E', 'E', 'B7', 'A', 'E', 'B7'], bpm: 80 },
{ name: 'I–vi–IV–V (50s Pop)', chords: ['C', 'Am', 'F', 'G'], bpm: 90 },
{ name: 'ii–V–I (Jazz)', chords: ['Dm7', 'G7', 'Cmaj7', 'Cmaj7'], bpm: 100 },
{ name: 'vi–IV–I–V (Modern)', chords: ['Am', 'F', 'C', 'G'], bpm: 85 },
{ name: 'I–IV–vi–V (Country)', chords: ['G', 'C', 'Em', 'D'], bpm: 90 },
{ name: 'i–VII–VI–VII (Minor Rock)', chords: ['Am', 'G', 'F', 'G'], bpm: 85 },
{ name: 'I–bVII–IV (Power Rock)', chords: ['E', 'D', 'A', 'A'], bpm: 105 },
{ name: 'i–iv–V–i (Flamenco)', chords: ['Am', 'Dm', 'E7', 'Am'], bpm: 95 },
{ name: 'Andalusian Cadence', chords: ['Am', 'G', 'F', 'E'], bpm: 90 },
{ name: 'Folk (G–C–D)', chords: ['G', 'C', 'D', 'G'], bpm: 95 },
{ name: 'Power Chord Groove', chords: ['E5', 'A5', 'E5', 'A5'], bpm: 120 },
{ name: 'Canon Progression', chords: ['D', 'A', 'Bm', 'Fm'], bpm: 72 },
{ name: 'I–IV–I–V (Blues Basics)', chords: ['A', 'D', 'A', 'E7'], bpm: 75 },
];
// State
let selectedChordId = 'C';
let strumDirection = 'down';
let filterRoot = 'all';
let filterType = 'all';
let currentProgressionPlaying = null;
// Initialize
function init() {
renderChordGrid();
renderProgressions();
setupEventListeners();
drawChordDiagram();
updateChordInfo();
}
function renderChordGrid() {
const grid = document.getElementById('chordGrid');
grid.innerHTML = '';
const filtered = CHORDS.filter(
(c) => (filterRoot === 'all' || c.root === filterRoot) && (filterType === 'all' || c.type === filterType)
);
filtered.forEach((chord) => {
const card = document.createElement('button');
card.className = `chord-card ${chord.id === selectedChordId ? 'selected' : ''}`;
card.textContent = chord.id;
card.onclick = () => selectChord(chord.id);
grid.appendChild(card);
});
}
function selectChord(chordId) {
selectedChordId = chordId;
renderChordGrid();
drawChordDiagram();
updateChordInfo();
}
function drawChordDiagram() {
const chord = CHORDS.find((c) => c.id === selectedChordId);
if (!chord) return;
const svg = document.getElementById('chordDiagram');
svg.innerHTML = '';
const frets = chord.frets;
const stringCount = 6;
const stringSpacing = 16;
const fretSpacing = 22;
const startX = 20;
const startY = 20;
// Draw strings (vertical lines)
for (let i = 0; i < stringCount; i++) {
const x = startX + i * stringSpacing;
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', x);
line.setAttribute('y1', startY);
line.setAttribute('x2', x);
line.setAttribute('y2', startY + 4 * fretSpacing + 2);
line.setAttribute('stroke', '#555');
line.setAttribute('stroke-width', '1');
svg.appendChild(line);
}
// Draw frets (horizontal lines)
for (let i = 0; i < 5; i++) {
const y = startY + i * fretSpacing;
const line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
line.setAttribute('x1', startX);
line.setAttribute('y1', y);
line.setAttribute('x2', startX + (stringCount - 1) * stringSpacing);
line.setAttribute('y2', y);
line.setAttribute('stroke', '#333');
line.setAttribute('stroke-width', i === 0 ? '3' : '1');
svg.appendChild(line);
}
// Draw finger positions and open/muted indicators
for (let i = 0; i < stringCount; i++) {
const x = startX + i * stringSpacing;
const fret = frets[i];
if (fret === -1) {
// Muted
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
text.setAttribute('x', x);
text.setAttribute('y', startY - 5);
text.setAttribute('text-anchor', 'middle');
text.setAttribute('font-size', '12');
text.setAttribute('fill', '#999');
text.setAttribute('font-weight', 'bold');
text.textContent = '×';
svg.appendChild(text);
} else if (fret === 0) {
// Open
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
text.setAttribute('x', x);
text.setAttribute('y', startY - 5);
text.setAttribute('text-anchor', 'middle');
text.setAttribute('font-size', '12');
text.setAttribute('fill', '#999');
text.setAttribute('font-weight', 'bold');
text.textContent = 'o';
svg.appendChild(text);
} else {
// Finger position
const y = startY + (fret - 0.5) * fretSpacing;
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
circle.setAttribute('cx', x);
circle.setAttribute('cy', y);
circle.setAttribute('r', '6');
circle.setAttribute('fill', 'var(--accent)');
svg.appendChild(circle);
}
}
// Update display name
document.getElementById('chordDisplayName').textContent = chord.name;
}
function updateChordInfo() {
const chord = CHORDS.find((c) => c.id === selectedChordId);
if (!chord) return;
const fretStr = chord.frets.map((f) => (f === -1 ? '·' : f)).join('–');
document.getElementById(
'chordDetails'
).textContent = `Root: ${chord.root} · Type: ${chord.type} · Frets: ${fretStr}`;
}
function renderProgressions() {
const list = document.getElementById('progressionsList');
list.innerHTML = '';
PROGRESSIONS.forEach((prog, idx) => {
const card = document.createElement('div');
card.className = 'progression-card';
const info = document.createElement('div');
info.className = 'progression-info';
const name = document.createElement('div');
name.className = 'progression-name';
name.textContent = prog.name;
const chords = document.createElement('div');
chords.className = 'progression-chords';
chords.textContent = prog.chords.join(' → ');
info.appendChild(name);
info.appendChild(chords);
const btn = document.createElement('button');
btn.className = 'btn-play-prog';
btn.textContent = 'PLAY';
btn.onclick = () => playProgression(idx, btn);
card.appendChild(info);
card.appendChild(btn);
list.appendChild(card);
});
}
function playProgression(idx, btn) {
if (currentProgressionPlaying === idx) {
currentProgressionPlaying = null;
btn.classList.remove('playing');
return;
}
currentProgressionPlaying = idx;
btn.classList.add('playing');
const prog = PROGRESSIONS[idx];
const beatDur = (60 / prog.bpm) * 4;
let delay = 0;
prog.chords.forEach((chordId) => {
setTimeout(() => {
if (currentProgressionPlaying !== idx) return;
const chord = CHORDS.find((c) => c.id === chordId);
if (chord) strumChord(chord.frets, 'down');
}, delay * 1000);
delay += beatDur;
});
setTimeout(() => {
if (currentProgressionPlaying === idx) {
currentProgressionPlaying = null;
btn.classList.remove('playing');
}
}, delay * 1000);
}
function setupEventListeners() {
// Tab switching
document.querySelectorAll('.tab').forEach((tab) => {
tab.addEventListener('click', (e) => {
const tabName = e.target.dataset.tab;
document.querySelectorAll('.tab').forEach((t) => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach((tc) => tc.classList.remove('active'));
e.target.classList.add('active');
document.getElementById(tabName).classList.add('active');
});
});
// Root filters
document.querySelectorAll('#rootFilters .btn-filter').forEach((btn) => {
btn.addEventListener('click', (e) => {
document.querySelectorAll('#rootFilters .btn-filter').forEach((b) => b.classList.remove('active'));
e.target.classList.add('active');
filterRoot = e.target.dataset.root;
renderChordGrid();
});
});
// Type filters
document.querySelectorAll('#typeFilters .btn-filter').forEach((btn) => {
btn.addEventListener('click', (e) => {
document.querySelectorAll('#typeFilters .btn-filter').forEach((b) => b.classList.remove('active'));
e.target.classList.add('active');
filterType = e.target.dataset.type;
renderChordGrid();
});
});
// Strum direction
document.querySelectorAll('.btn-dir').forEach((btn) => {
btn.addEventListener('click', (e) => {
document.querySelectorAll('.btn-dir').forEach((b) => b.classList.remove('active'));
e.target.classList.add('active');
strumDirection = e.target.dataset.direction;
});
});
// Strum button
document.getElementById('strumBtn').addEventListener('click', () => {
const chord = CHORDS.find((c) => c.id === selectedChordId);
if (chord) strumChord(chord.frets, strumDirection);
});
// ── Keyboard controls ──────────────────────────────────────────
// Standard online guitar keyboard layout:
// A–G = select & strum that major chord instantly
// 1–6 = pluck individual strings (low E → high e)
// Space / Enter = strum current chord down
// Shift+Space = strum current chord up
// ← → = prev/next chord in filtered list
const STRING_KEYS = {'1':0,'2':1,'3':2,'4':3,'5':4,'6':5};
const BASE_FREQS = [82.41, 110.0, 146.83, 196.0, 246.94, 329.63];
const SEMITONE = Math.pow(2, 1/12);
function pluckStringN(stringIndex) {
const chord = CHORDS.find((c) => c.id === selectedChordId);
if (!chord) return;
const fret = chord.frets[stringIndex];
if (fret < 0) return; // muted
const freq = BASE_FREQS[stringIndex] * Math.pow(SEMITONE, fret);
pluckString(freq, 0.5, null);
// flash the strum button briefly as visual feedback
const btn = document.getElementById('strumBtn');
btn.style.background = 'var(--success)';
setTimeout(() => btn.style.background = '', 150);
}
document.addEventListener('keydown', (e) => {
// Ignore when typing in inputs
if (e.target.tagName === 'INPUT' || e.target.tagName === 'SELECT' || e.target.tagName === 'TEXTAREA') return;
// 1–6: pluck individual strings
if (STRING_KEYS.hasOwnProperty(e.key)) {
e.preventDefault();
pluckStringN(STRING_KEYS[e.key]);
return;
}
// A–G letter keys: jump to + strum that major chord
const LETTER_TO_CHORD = { a:'A', b:'B', c:'C', d:'D', e:'E', f:'F', g:'G' };
const lk = e.key.toLowerCase();
if (!e.ctrlKey && !e.metaKey && !e.altKey && LETTER_TO_CHORD[lk]) {
e.preventDefault();
// Shift+letter = minor (e.g. Shift+A = Am)
const targetId = e.shiftKey ? LETTER_TO_CHORD[lk] + 'm' : LETTER_TO_CHORD[lk];
const found = CHORDS.find((c) => c.id === targetId);
if (found) {
selectChord(found.id);
strumChord(found.frets, strumDirection);
}
return;
}
// Space / Enter: strum current chord
if (e.code === 'Space' || e.code === 'Enter') {
e.preventDefault();
const dir = e.shiftKey ? 'up' : strumDirection;
const chord = CHORDS.find((c) => c.id === selectedChordId);
if (chord) strumChord(chord.frets, dir);
return;
}
// Arrow keys: navigate chords
if (e.code === 'ArrowRight' || e.code === 'ArrowLeft') {
e.preventDefault();
const filtered = CHORDS.filter(
(c) =>
(filterRoot === 'all' || c.root === filterRoot) &&
(filterType === 'all' || c.type === filterType)
);
const idx = filtered.findIndex((c) => c.id === selectedChordId);
if (e.code === 'ArrowRight' && idx < filtered.length - 1) selectChord(filtered[idx + 1].id);
if (e.code === 'ArrowLeft' && idx > 0) selectChord(filtered[idx - 1].id);
}
});
}
// Start
init();