Many thanks for this. I had tried to make it without the scrolling element, but really struggled to understand how to. Your commented out line is really useful for understanding that a bit better. I had been confused when I tried to do that because all the items ended up in the to state, and so I couldn't see how that would work when I then added in scrolling.
Can I please check I'm understanding how it's working? The to for each item is doing the work of scaling it, but the clever bit that I completely didn't think of is to do with the timing, which is being set with 0.3 * i for each of the to commands. (As I understand, these values are absolute, and a similar timing could be reached by putting "< 0.3" in here.) This is causing an overlap in the animations.
Then, adding the scroll in, the default duration relates to the value of overlap that we're using. If the default duration was 0.3, we'd get no overlap and no ripple effect, and if the default duration was, e.g., 1 we'd get overlap into more items. I've tested this and this is what occurs.
The repeat: 1 is also crucial. If I take that out, the items don't fade away. And if I take out the yoyo it goes weird as well. This is because the repeat:1 and yoyo work together to mean the animation occurs once to move the item to the to state, and again (due to repeat: 1) to move it back to the original state (yoyo tells it to do a reversal, rather than repeat from the start again).
I've also managed to target the individual span inside each line by using gsap.utils.selector, and worked out how to leave the last item in its animated state at the end of the scroll, which gives a more natural effect for what I'm after (commented out in my new CodePen, but there if anyone else wants it).
Thanks for your help!
https://codepen.io/bojates/pen/GRBmpLq