Interval Vector Chart

interval-vector-chartVisualizers

The interval-class vector counts every interval present in a set of notes, folded into six classes — m2, M2, m3, M3, P4 and the tritone. Folded, because an interval and its inversion connect the same two pitch classes: a fourth up is a fifth down. What makes it worth drawing rather than printing is that the result is invariant under both transposition and inversion, so it describes what a sonority SOUNDS like independent of how it is spelled. Every major triad in every key draws the same three bars; so does every minor triad, because they are reflections of each other. Read the shape and you are reading character: weight on the right-hand bars is tension (the last bar is literally the tritone count), weight in the middle is sweetness, and a perfectly flat profile is the all-interval tetrachord, which contains every class exactly once and sounds like nothing in particular. Pass the six counts if you have them, or the notes to compute them from.

C and Cm draw the identical shape — they are inversions of each other, and the vector cannot tell them apart by design. The diminished seventh is all minor thirds and tritones; the whole-tone scale has no minor thirds or fourths at all.

Install

Copies the source into your project. Anything it depends on comes with it.

$ npx shadcn add @syntho-ui/interval-vector-chart

Pulls in

control-themeconsonance

First time? Add the registry to your components.json once:

components.json
{
  "registries": {
    "@syntho-ui": "https://engine.syntho.app/r/{name}.json"
  }
}

Usage

Controlled or uncontrolled, whichever suits the surrounding state.

example.tsx
import { IntervalVectorChart } from "@/components/ui/interval-vector-chart"

// Either the six counts directly…
<IntervalVectorChart vector={[0, 0, 1, 1, 1, 0]} />

// …or the notes, and it computes them.
<IntervalVectorChart notes={[60, 64, 67]} glyphStyle="musical" />

Props

PropTypeDefaultDescription
vectornumber[]The six interval-class counts (m2, M2, m3, M3, P4, TT). Takes precedence over notes.
notesnumber[]MIDI notes to compute the vector from. Reduced mod 12 and de-duplicated, so a doubled octave does not change the fingerprint.
glyphStyle"musical" | "emoji""musical"Bar labels: musical glyphs (♭2, ♭3, TT) under the bars, or emoji over them.
heightnumber88Height of the bars in pixels, excluding counts and glyphs.
showCountsbooleantrueShow the count above each bar.
labelstring"intervals"Caption under the chart. Pass an empty string for none.
barColorsstring[]Per-class colours, six of them, running consonant → dissonant. Ignored when color is set.
colorstringA single colour for every bar, overriding the per-class palette.

Interaction

  • Drag with pointer capture, so the gesture keeps tracking past the edge of the control.
  • Shift while dragging for a fine pass; release it mid-gesture and coarse tracking resumes.
  • Double-click resets to defaultValue.
  • Arrow keys step normally, Shift+arrow steps finely, PageUp/PageDown jump by ten, Home/End go to the extremes.
  • Proper slider and switch roles with live aria-valuetext, so a screen reader announces the formatted value rather than the raw number.
  • The scroll wheel adjusts the value while the pointer is over the control; stepped ranges move at least one declared step per notch by default.