Open-string notes appear at a seemingly random spot instead of lining up with the fretted notes around them.
Root cause
Not a parser/timing bug — lib/gp2rs_gpx.py and lib/gp2rs.py compute one timestamp per beat and apply it to every note in the beat identically, fretted or open (lib/gp2rs_gpx.py:1826, lib/gp2rs.py:837-839). Times are correct.
The mispositioning is in the renderers' X-axis placement:
- 3D highway —
plugins/highway_3d/screen.js:14337:
const xBase = n.f === 0 ? (openX !== undefined ? openX : curX) : xFretMid(n.f);
A standalone open note has no openX, so it falls back to curX — wherever the camera currently happens to be centered, which is driven entirely by the other (fretted) notes nearby. Open strings (fret 0) are explicitly excluded from the camera-anchor computation (lib/gp2rs_gpx.py:2057-2074, lib/gp2rs.py:1097-1118 — "Exclude open strings (fret 0) — they span the full highway and shouldn't cause the fret range to shift"), so an open note's on-screen position is disconnected from the neck position of the phrase around it.
- Legacy 2D highway —
static/js/highway-draw.js:100-121: the caller does compute a real nut-position x = fretX(hwState, n.f, p.scale, W) (static/js/highway-draw.js:533), but drawNote() discards it for fret === 0 && !isChord and instead centers the bar at W/2 (screen center) regardless of the surrounding notes' fret position.
This "open string = wide bar" rendering is intentional, but the current fallback (camera-center / screen-center) makes it look randomly placed relative to the rest of the phrase.
Suggested fix
Interpolate openX from neighboring fretted-note positions in time (rather than falling back to camera-center / screen-center) in both plugins/highway_3d/screen.js:14337 and static/js/highway-draw.js:100-121, so a standalone open note visually belongs with the phrase around it.
Notes
No existing test/commit treats this as a bug (git log --oneline --all | grep -iE "open.string" — no hits). Flagging as a design gap, not a regression.
Open-string notes appear at a seemingly random spot instead of lining up with the fretted notes around them.
Root cause
Not a parser/timing bug —
lib/gp2rs_gpx.pyandlib/gp2rs.pycompute one timestamp per beat and apply it to every note in the beat identically, fretted or open (lib/gp2rs_gpx.py:1826,lib/gp2rs.py:837-839). Times are correct.The mispositioning is in the renderers' X-axis placement:
plugins/highway_3d/screen.js:14337:openX, so it falls back tocurX— wherever the camera currently happens to be centered, which is driven entirely by the other (fretted) notes nearby. Open strings (fret 0) are explicitly excluded from the camera-anchor computation (lib/gp2rs_gpx.py:2057-2074,lib/gp2rs.py:1097-1118— "Exclude open strings (fret 0) — they span the full highway and shouldn't cause the fret range to shift"), so an open note's on-screen position is disconnected from the neck position of the phrase around it.static/js/highway-draw.js:100-121: the caller does compute a real nut-positionx = fretX(hwState, n.f, p.scale, W)(static/js/highway-draw.js:533), butdrawNote()discards it forfret === 0 && !isChordand instead centers the bar atW/2(screen center) regardless of the surrounding notes' fret position.This "open string = wide bar" rendering is intentional, but the current fallback (camera-center / screen-center) makes it look randomly placed relative to the rest of the phrase.
Suggested fix
Interpolate
openXfrom neighboring fretted-note positions in time (rather than falling back to camera-center / screen-center) in bothplugins/highway_3d/screen.js:14337andstatic/js/highway-draw.js:100-121, so a standalone open note visually belongs with the phrase around it.Notes
No existing test/commit treats this as a bug (
git log --oneline --all | grep -iE "open.string"— no hits). Flagging as a design gap, not a regression.