so, messing around with bytebeat you probably noticerd that putting only "t" will generate a sawtooth wave at a fixed frequency.
you might also have noticed that multiplying t with something will change its frequency.
if we want to make a sawtooth that changes notes over time, we will need some kind of time reference. of course we can use t itself, but for me its simpler to divide it with some number, and use a "beat counter" or something.
so based on that, here's some small code:
beat = t/1000,
notes = [4,0,4,4.5,5,5,4,0,5.3,5.3,5,0,4.5,4.5,5.3,0,5,5,4,0,3,3,2.8,0,2.65,2.65,2.65,2.65,2.65,0,2.65,2.8],
t * notes[floor(beat % 32)] % 128
"beat" is the "beat counter" i mentioned above
"notes" is an array representing which number should t be multiplied with at each moment in the song
"t * notes[floor(beat % 32)]" means t multiplied by the number found in the position "floor(beat % 32)" inside the "notes" array. wtf is that position?
"beat % 32" ensures that the value loops from 0 to 31, since the "notes" array has 32 values in it
"floor(beat % 32)" ensures that the number gets rounded down; since you can access the 3rd position of an array, but not the 3.85649084rd (reminder that beat = t/1000 and t/1000 will not necessarily be a whole number)
lastly "% 128" in the end just halves the volume of the saw, but also doubles its pitch (test with t%128), this is useful because we cannot have more than one track if this one track's volume is maximum
this is the base of my track really, all it changes is that i used different instruments other than sawtooth, but the way notes are stored are pretty much that
i hope this explanation is decent enough! i just woke up so some parts may be poorly written, if you have any questions feel free to ask etc