Thanks, glad you liked it!
Each circle has a hitTime property. I could get the current song time with LOVE using songTime = mySong:tell("seconds"). The note should be hit once hitTime is equal to songTime
Each circle also holds 2 math functions: easeFnX and easeFnY.
These math functions take in the difference between the hitTime and songTime and returns a number representing the position it should be drawn on screen. So when hitTime and songTime are the same, the difference is 0. So when easeFnX receives 0, then the x position should return where the middle line is. The further away easeFnX is from 0, the further away the note will be from the middle line.
Each beatmap is stored in a lua file. They have access to a function like assembleNote(hitTime, easeFnX, easeFnY) to generate the notes. And I just call it a bunch of times in the file for each note.
Sounds tedious (it was haha), but I made a crude level editor that helped me write it all out!