🎸

GuitarTheory
Challenge

A music theory quiz powered entirely by the GuitarTheory REST API

Live API · 10 Questions · No Dependencies
APIs used in this game
GET/api/v1/scales/{root}/{mode} GET/api/v1/intervals/{n1}/{n2} GET/api/v1/keys/{note}/major GET/api/v1/modes

Every question is answered in real-time using live API data. The endpoint being called is shown as you play — see the docs below to understand how it all works.

Building your questions…
Connecting to API…
Question 1 of 10 0 pts
Scale
GET /api/v1/…
0
/ 10
Nice work!
Developer Documentation

Building With the GuitarTheory API

This is a self-contained example — zero npm installs, zero build step, just a single HTML file. It demonstrates four GuitarTheory REST API endpoints to generate a real-time music theory quiz. Everything below explains exactly how each call works so you can adapt it.

Base URL: https://www.guitartheory.com/api/v1 — all endpoints below are relative to this. CORS is enabled, so requests work from any origin. Full API reference and interactive docs at guitartheory.com/docs.

Setup & Configuration

By default, BASE_URL points at https://www.guitartheory.com — the live public API. It auto-detects localhost and any hostnames/IPs listed in DEV_HOSTS, routing those requests to the local server instead. To add a dev server, just append its IP or hostname to the DEV_HOSTS array.

// Add dev server IPs or hostnames here — they will use their own API. const DEV_HOSTS = ['localhost', '127.0.0.1', '68.39.152.178']; const BASE_URL = (window.location.protocol === 'file:' || !window.location.hostname) ? 'https://guitartheory.com' // opened as a local file : DEV_HOSTS.includes(window.location.hostname) ? window.location.origin // known dev server : 'https://guitartheory.com'; // any other host → public API // All API calls go through this one helper. async function apiFetch(path) { const res = await fetch(BASE_URL + path); if (!res.ok) throw new Error(`API error: ${res.status} on ${path}`); return res.json(); }

Endpoint 1 — Scale Notes

GET /api/v1/scales/{root}/{mode}
Returns all 7 notes in a scale, the interval pattern (semitone offsets from root), display name, and a character description. Supports all 7 church modes plus harmonic and melodic minor.
↳ Used for: "What is the Nth degree of X scale?" questions
// Ask: What is the 5th degree of G Mixolydian? const root = 'G'; const mode = 'mixolydian'; const data = await apiFetch(`/api/v1/scales/${root}/${mode}`); /* Full response: { "root": "G", "mode": "mixolydian", "displayName": "G Mixolydian", "notes": ["G", "A", "B", "C", "D", "E", "F"], "intervals": [0, 2, 4, 5, 7, 9, 10], "character": "Bluesy, dominant, rock feel", "notesWithOctaves": ["G4", "A4", "B4", "C5", "D5", "E5", "F5"] } */ // notes[] is 0-indexed: notes[0] = root (1st degree), // notes[4] = 5th degree, etc. const degreeIndex = 4; // 5th degree const correctNote = data.notes[degreeIndex]; // "D" // Generate 3 wrong-answer notes from the chromatic scale const ALL_NOTES = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B']; const wrongNotes = shuffle(ALL_NOTES.filter(n => n !== correctNote)).slice(0, 3);

Endpoint 2 — Interval Calculator

GET /api/v1/intervals/{note1}/{note2}
Calculates the interval between any two chromatic pitch classes. Returns the semitone distance, the interval's full name, quality (major/minor/perfect/augmented/diminished), and interval number (2nd, 5th, etc.).
↳ Used for: "What interval is between X and Y?" questions
// Ask: What interval is between C and G? const data = await apiFetch('/api/v1/intervals/C/G'); /* Full response: { "note1": "C", "note2": "G", "semitones": 7, "intervalName": "Perfect Fifth", "quality": "perfect", "number": 5 } */ const correct = data.intervalName; // "Perfect Fifth" // Pool of all interval names for generating wrong answers const ALL_INTERVALS = [ 'Unison', 'Minor Second', 'Major Second', 'Minor Third', 'Major Third', 'Perfect Fourth', 'Tritone', 'Perfect Fifth', 'Minor Sixth', 'Major Sixth', 'Minor Seventh', 'Major Seventh', 'Octave' ]; const wrong = shuffle(ALL_INTERVALS.filter(n => n !== correct)).slice(0, 3);

Endpoint 3 — Key Analysis

GET /api/v1/keys/{note}/{quality}
Returns a full key analysis: all 7 scale notes, the relative minor (or major), and all 7 diatonic chord names with their Roman numeral function. Quality is major or minor.
↳ Used for: "What is the relative minor of X major?" questions
// Ask: What is the relative minor of D major? const data = await apiFetch('/api/v1/keys/D/major'); /* Full response: { "key": "D", "quality": "major", "scale": ["D", "E", "F#", "G", "A", "B", "C#"], "relativeMinor": "B", "chords": { "I": { "root": "D", "type": "major", "numeral": "I" }, "ii": { "root": "E", "type": "minor", "numeral": "ii" }, "iii": { "root": "F#", "type": "minor", "numeral": "iii" }, "IV": { "root": "G", "type": "major", "numeral": "IV" }, "V": { "root": "A", "type": "major", "numeral": "V" }, "vi": { "root": "B", "type": "minor", "numeral": "vi" }, "vii°":{ "root": "C#", "type": "diminished","numeral": "vii°"} }, "commonProgressions": [ … ] } */ const correct = data.relativeMinor + 'm'; // "Bm" // Wrong answers: other minor keys (sampled from natural notes) const NATURALS = ['C', 'D', 'E', 'F', 'G', 'A', 'B']; const wrong = shuffle( NATURALS.filter(n => n !== data.relativeMinor) ).slice(0, 3).map(n => n + 'm');

Endpoint 4 — Modes Catalog

GET /api/v1/modes
Returns all 7 church modes with interval patterns, display names, and character descriptions. This endpoint is fetched once at game start and cached — no repeated calls needed.
↳ Used for: "Which mode is described as '…'?" questions
// Fetch once, cache in a module-level variable let modesCache = null; async function getModes() { if (!modesCache) { const data = await apiFetch('/api/v1/modes'); modesCache = data.modes; } return modesCache; } /* Each mode object looks like: { "name": "lydian", "displayName": "Lydian", "intervals": [0, 2, 4, 6, 7, 9, 11], "character": "Dreamy, ethereal, floating quality", "relatedMode": "major" } */ // Quiz: show mode.character → player picks mode.displayName const modes = await getModes(); const target = modes[3]; // Lydian const correct = target.displayName; // "Lydian" const wrong = shuffle( modes.filter(m => m.name !== target.name).map(m => m.displayName) ).slice(0, 3);

How the Game Loop Works

Questions are generated sequentially during the loading screen so all API calls complete before play begins — no lag mid-game. Each generator function returns a plain object with everything needed to render that question:

// Question object shape { type: 'scale' | 'interval' | 'keys' | 'modes', question: 'What is the 5th note of …?', // shown to player answers: ['D', 'F#', 'A', 'C'], // shuffled, 4 options correct: 'D', // the right answer apiCall: '/api/v1/scales/G/mixolydian', // displayed in callout responseSnippet:'"notes": ["G","A","B","C","D","E","F"]', explanation: 'G Mixolydian: G – A – B – C – D – E – F' } // Generation sequence (shuffled mix of all four types) const generators = shuffle([ makeScaleQuestion, makeScaleQuestion, makeScaleQuestion, makeIntervalQuestion, makeIntervalQuestion, makeIntervalQuestion, makeKeysQuestion, makeKeysQuestion, makeModesQuestion, makeModesQuestion, ]); // Build all 10 questions before showing the quiz screen const questions = []; for (const gen of generators) { questions.push(await gen()); // sequential so loading bar advances }

Using This File in Your Project

This is a single self-contained HTML file — no build step, no dependencies. Download it and drop it into any project or host it anywhere. It calls the GuitarTheory API at https://www.guitartheory.com/api/v1 by default, which has CORS enabled so it works from any origin.

⬇ Download guitartheory-api-demo.html
// The file works out of the box — just open it in a browser. // All API calls go to https://www.guitartheory.com/api/v1 by default. // If self-hosting GuitarTheory on your own server, change this one line: const BASE_URL = 'https://your-server.com'; // ← only change needed

Ideas for Extending This Example

The GuitarTheory API has five more endpoints not used in this game. Each one opens up new game mechanics:

GET
/api/v1/chords/{root}/{type}
Quiz on chord spellings — "which notes are in Cmaj7?" — or render fretboard chord diagrams
GET
/api/v1/chord-types
Show the formula (1–♭3–5) and ask the player to name the chord type
GET
/api/v1/circle-of-fifths
Build an SVG circle and ask the player to click the correct key — uses the clockwise/counterClockwise arrays
GET
/api/v1/fretboard/scale/{root}/{mode}
Show a fretboard diagram with highlighted dots and ask the player to identify the scale