Skip to content

Note Mapping & Visual Alignment Reference

Core Variables

  • numNotes — count of valid scale notes in the selected range
  • position — normalized hand Y coordinate (0.0 = top, 1.0 = bottom)
  • invertedPosition = 1.0 - position (0.0 = bottom, 1.0 = top)

Fret Boundary Lines (NoteFretOverlay.swift)

Drawn at the edges of each note zone:

y_boundary(i) = 1.0 - (i / numNotes)    for i in 0...numNotes
- Boundary 0 = 1.0 (bottom of screen) - Boundary numNotes = 0.0 (top of screen) - Zone i spans from y_boundary(i) down to y_boundary(i+1)

Note Labels (NoteFretOverlay.swift)

Placed at the center of each zone:

y_label(index) = 1.0 - ((index + 0.5) / numNotes)
- Index 0 (lowest note) near bottom - Index numNotes-1 (highest note) near top

Position-to-Note Mapping (MIDIRouter.swift)

circleRadiusOffset = 14.0 / screenHeight
adjustedPosition = source.position.y - circleRadiusOffset
invertedPosition = 1.0 - adjustedPosition
floatIndex = invertedPosition * numNotes
rawIndex = Int(floatIndex)
index = max(0, min(rawIndex - 1, numNotes - 1))
The rawIndex - 1 shift is intentional — removing it makes alignment worse.

The 14pt radius offset ensures the pitch boundary aligns with the dot center, not its bottom edge.

Tracking Dot Dimensions (ContentView.swift)

  • Outer glow: 70x70pt (RadialGradient, purely visual)
  • Core circle: 28x28pt (filled + stroked)
  • Radius for offset math: 14pt

Velocity from X Position (MIDIRouter.swift)

clampedX = clamp(x, 0.0, 1.0)
curve = pow(clampedX, 1.5)
velocity = 20 + curve * 107    // range: 20-127
Left = quiet, right = loud. Minimum velocity of 20 ensures audible notes.

Modulation from Fingers (MIDIRouter.swift)

ccValue = UInt8(fingerCount / 5.0 * 127.0)
0 fingers = 0, 5 fingers = 127.