npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

note-mapper

v1.0.2

Published

Map guitar notes from note-listener to scale degrees, intervals and game actions

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-mapper

note-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 dorian

Available 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"]) // → null

getDegreeWithTolerance(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") // → true

Custom 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