I think it might even be less prone to lag issues, at least the way we did it.
- Get a list of all notes to be played (note + start time). We cheated a bit and just let our composer export a midi file for us :)
- For every note just instantiate a note-scene at position.y = start_time_in_seconds * pixels_per_second
- All notes are under one parent node
- Instead of "moving the parent note by some value" just absolutely reposition the parent note at position.y = current_playback_time_in_seconds * pixels_per_second * -1 every (physics) frame
This way, the notes move always in sync with the music, if the music skips/lags, the visuals will also skip/lag.
This approach might lead to performance issues with very large songs (lots of notes), but for us this worked. For note detection we don't use physics at all btw, we just check if the note is withing a given timeframe of their start time. i.e.:
var min_time = note.start_time - tolerance_time
var max_time = note.start_time + tolerance_time
if current_playback_time >= min_time and current_playback_time <= max_time -> then note is hit