Interval Vector Chart
interval-vector-chartVisualizersThe 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-chartPulls in
First time? Add the registry to your components.json once:
{
"registries": {
"@syntho-ui": "https://engine.syntho.app/r/{name}.json"
}
}Usage
Controlled or uncontrolled, whichever suits the surrounding state.
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
| Prop | Type | Default | Description |
|---|---|---|---|
| vector | number[] | — | The six interval-class counts (m2, M2, m3, M3, P4, TT). Takes precedence over notes. |
| notes | number[] | — | 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. |
| height | number | 88 | Height of the bars in pixels, excluding counts and glyphs. |
| showCounts | boolean | true | Show the count above each bar. |
| label | string | "intervals" | Caption under the chart. Pass an empty string for none. |
| barColors | string[] | — | Per-class colours, six of them, running consonant → dissonant. Ignored when color is set. |
| color | string | — | A 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
sliderandswitchroles with livearia-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.