note-mapper
v1.0.2
Published
Map guitar notes from note-listener to scale degrees, intervals and game actions
Maintainers
Readme
note-mapper
Maps guitar notes from note-listener to scale degrees, intervals, and game actions.
Use it to turn a guitar into a game controller — the player picks a key and scale, and their fretboard positions map to actions regardless of what key they're in.
Installation
npm install note-mappernote-listener should already be installed in your project as this package works alongside it.
Quick Start
import { createPitchListener } from "note-listener"
import { createMapper, SCALES, ACTIONS_5 } from "note-mapper"
const mapper = createMapper({
scale: SCALES["pentatonic_minor"]["A"],
degreeActions: ACTIONS_5,
onAction(action, meta) {
console.log(action) // "jump", "attack" etc
console.log(meta) // { type, note, freq, degree, cents }
}
})
const listener = await createPitchListener({
onNotes(notes) {
mapper.process(notes)
}
})
listener.start()Concepts
Scale degrees
A scale degree is the position of a note within a scale — 0 is always the root. The player can switch key and the same fret positions produce the same actions.
A minor pentatonic: ["A", "C", "D", "E", "G"]
0 1 2 3 4
Play "A" → degree 0 → "jump"
Play "E" → degree 3 → "attack"Intervals
An interval is the distance in semitones between two notes played together. Used for two-note combinations like power chords triggering special moves.
Play A + E together → Perfect 5th (7 semitones) → "power_attack"API
createMapper(options)
Creates a mapper instance. Call mapper.process(notes) inside onNotes.
| Option | Type | Default | Description |
|---|---|---|---|
| scale | string[] | required | Scale array from SCALES |
| degreeActions | object | ACTIONS_7 | Maps degree → action string |
| intervalActions | object | ACTIONS_INTERVAL | Maps semitones → action string |
| centsTolerance | number | 40 | Cents off-pitch still accepted |
| intervalWindow | number | 150 | Ms window for interval detection |
| onAction | function | required | Fires when an action is detected |
Returns
{
process(notes), // call this inside onNotes every frame
setScale(newScale), // swap scale at runtime
setDegreeActions(map), // swap degree actions at runtime
setIntervalActions(map), // swap interval actions at runtime
getHeldNotes(), // returns currently held notes
}SCALES
All scales, keyed by type then root note.
import { SCALES } from "note-mapper"
SCALES["major"]["G"] // G major
SCALES["pentatonic_minor"]["E"] // E minor pentatonic
SCALES["dorian"]["A"] // A dorianAvailable scale types:
| Key | Name | Notes |
|---|---|---|
| major | Major / Ionian | 7 |
| natural_minor | Natural Minor / Aeolian | 7 |
| pentatonic_minor | Minor Pentatonic | 5 |
| pentatonic_major | Major Pentatonic | 5 |
| blues | Blues | 6 |
| dorian | Dorian | 7 |
| phrygian | Phrygian | 7 |
| lydian | Lydian | 7 |
| mixolydian | Mixolydian | 7 |
| locrian | Locrian | 7 |
| harmonic_minor | Harmonic Minor | 7 |
| melodic_minor | Melodic Minor | 7 |
| phrygian_dominant | Phrygian Dominant | 7 |
| whole_tone | Whole Tone | 6 |
| diminished | Diminished | 8 |
All scale types are available in all 12 keys: C C# D Eb E F F# G Ab A Bb B
KEYS
Array of all 12 root keys — useful for building a key selector UI.
import { KEYS } from "note-mapper"
// ["C", "C#", "D", "Eb", "E", "F", "F#", "G", "Ab", "A", "Bb", "B"]Default action maps
import { ACTIONS_5, ACTIONS_6, ACTIONS_7, ACTIONS_INTERVAL } from "note-mapper"ACTIONS_5 — for pentatonic scales (5 notes)
| Degree | Action | |---|---| | 0 | jump | | 1 | move_left | | 2 | move_right | | 3 | attack | | 4 | shield |
ACTIONS_6 — for blues scale (6 notes)
Adds special at degree 5.
ACTIONS_7 — for diatonic scales (7 notes)
Adds special at degree 5 and ultimate at degree 6.
ACTIONS_INTERVAL — for two-note combinations
| Semitones | Interval | Action | |---|---|---| | 5 | Perfect 4th | special | | 7 | Perfect 5th | power_attack | | 3 | Minor 3rd | block | | 4 | Major 3rd | dodge | | 6 | Tritone | taunt | | 9 | Major 6th | heal | | 10 | Minor 7th | roll | | 12 | Octave | ultimate |
Lower level utilities
If you want more control than createMapper gives you, use these directly.
import {
getDegree,
getDegreeWithTolerance,
degreeToNote,
isInScale,
getInterval,
getIntervalInfo,
detectInterval,
isInterval,
} from "note-mapper"getDegree(note, scale) — returns 0-based degree or null if not in scale
getDegree("A2", ["A","C","D","E","G"]) // → 0
getDegree("F2", ["A","C","D","E","G"]) // → nullgetDegreeWithTolerance(note, freq, scale, centsTolerance?) — same but accepts slightly out of tune notes
degreeToNote(degree, scale) — reverse lookup, degree → note name
degreeToNote(3, ["A","C","D","E","G"]) // → "E"isInScale(note, scale) — returns boolean
getInterval(freqA, freqB) — returns semitone count 0–12
getInterval(440, 660) // → 7 (perfect 5th)getIntervalInfo(freqA, freqB) — returns full interval object
getIntervalInfo(440, 660)
// → { semitones: 7, name: "Perfect 5th", short: "P5", character: "power chord, strong" }detectInterval(notes, history, windowMs?) — detects interval from onNotes array with timing window
isInterval(semitones, short) — readable interval check
isInterval(7, "P5") // → trueCustom action maps
You can define your own action maps and swap them at runtime:
const myActions = {
0: "fly",
1: "run_left",
2: "run_right",
3: "fireball",
4: "teleport",
}
mapper.setDegreeActions(myActions)
// swap back later
mapper.setDegreeActions(ACTIONS_5)Switching scale at runtime
// player changes key in settings
mapper.setScale(SCALES["major"]["G"])
// player changes scale type
mapper.setScale(SCALES["dorian"]["A"])Held notes and interval history are cleared automatically on scale change.
License
MIT © Donald Edwin
