- Y-drag creates ghost nodes at quantized ratios (2/1, 3/2, 5/4, etc.)
- Ghost nodes store their own frequency (fundamental * new fraction)
- Preview ghost shows frequency in real-time while dragging
- Final ghost created on release (if dragged >100 cents from original)
- Server API accepts frequency directly for ghost nodes
- Fix frequency display: fundamental * fraction (not cents-adjusted)
- Fix negative snap ratios to use correct reciprocals
- Add colored circle on click (both ramp and non-ramp modes)
- Ghost nodes: no border initially, opacity 0.7, border shows on click
- Chord label clicks exclude ghost nodes
- Remove octaveOffset, use cents or direct frequency instead
- Add siren selection (1-4) and auto (A) toggle buttons
- Keyboard: 1-4 select siren, 0/A auto, R ramp toggle
- Click plays to siren, Shift+click to SuperCollider
- Add RAMP toggle button
- Colored borders on nodes based on which siren is playing
- Fix label clicks to use auto (node's voice)
- Add toggle styling to cents/freq button
- Disable siren hotkeys when typing in input fields
- Disable Flask debug mode for background operation
- Add label nodes at top of each chord column
- Modify xforce layout to skip label nodes
- Click label to play entire chord on siren
- Use chordIndex/nodeIndex (server calculates frequency)
- Fix server to support both calling conventions
- Fix client to use proper JSON headers for kill-siren
- Add /api/play-siren endpoint for Shift+click
- Add /api/kill-siren endpoint for k/K keys
- Add set_chords method to OSCSender for dynamic voice count
- Update soft kill to 20Hz in osc_sender.py
- Refactor CLI to use send_kill method
- Fix kill to send to all voices regardless of count
- Add /api/set-fundamental and /api/play-freq endpoints to server.py
- Add send_single method to osc_sender.py for sending single frequencies
- Add fundamental frequency input and click handler to path_navigator.html
- Distinguish between click (play) and drag (move) on nodes
- Add osc_receiver.scd for SuperCollider to receive /freq messages
- Server calculates graph edges and ratios using src/pitch.py
- Frontend fetches computed graph from API
- Supports loading different output files via dropdown
- All pitch calculations now in Python, JS only for rendering