Sinusoidal ASCII-art animated text I somewhat miss those old demos showing the capabilities of computers when they were called x86 instead of i3, i5 and i7. One of the first I watched in my 386 was the Unreal demo from Future Crew that is now celebrating its 25th anniversary. At minute 0:43 the first part of the demo starts and we see a scrolling text following a sinusoidal path. Let's try to imitate that effect in ASCII art!

The challenge

Given this path:

***                                ***
   ***                          ***
      **                      **
        *                    *
         *                  *
         *                  *
          *                *
           **            **
             ***      ***
                ******


and an input text, draw the text following that path, like this:

Thi                                Golf! 
   s i                          de       Yay
      s                       Co            !
        P                     
         r                  d
         o                  n
          g                a
           ra            s 
             mmi      zle
                ng Puz


Note that the spaces count as characters in the path, and that the path repeats itself if the text is longer than the sample.

The animation part

Once you have drawn the text, wait for 100 ms (to create an animation of around 10 fps) and draw the text again but starting from the next position of the path. So, for frame #n, calculate n modulo 40 and start drawing in the following position of the path with the text always aligned to the left of the canvas:

***                                ***
|  ***                          ***  |
|     **                      **     |
|       *                    *       |
|        *                  *        |
|        *                  *        |
|         *                *         |
|          **            **          |
|            ***      ***            |
|               ******               |
Position 0                 Position 39


So for frame 10 we will have:

                           and Co
                        es       de 
                      zl            Go
                     z                l
                    u                  f
T                   P                  !
 h                                       
  is             ng                       Ya
     is       mmi                           y!
        Progra


Notes


Input will be a single string (or char array, whatever) with the text to animate, and will always have at least 1 character.
Valid characters to animate are those in the printable ASCII set.
The path to follow will be exactly as given.
Text will always be aligned to the left of the canvas, so the effect will be the text waving like a flag, with no text displacement. And by canvas I mean screen or whatever you will use to show the text. ;-)
Frames must be clear of any characters/pixels from previous frames unless the character/pixel is the same in both frames.
The speed of the animation does not matter as long as it runs smoothly or as fast as your device can (we can set a minimum of 5 fps, but this is not a requirement). Just adjust the speed to make it fluid and do not worry if the waiting times are not exactly the same.
The animation will loop endlessly.


This is code-golf, so may the shortest program or function capable of animating the text win!
HTML + ES6, 241 244 237 bytes
Breakdown:

HTML: 16 bytes
JS function: 221 bytes



let f =

s=&gt;setInterval(_=&gt;o.innerHTML=[...s].map((c,i)=&gt;([j,y]=[...Array(40)].map((_,k)=&gt;[j=k%20,y,y+=77732&gt;&gt;j&amp;1?k&lt;20||-1:0],y=0)[(i+p)%40],a[y]=a[y]||[...s].fill` `)[x]=(x+=j!=9,c),x=0,a=[],p++)&amp;&amp;a.map(r=&gt;r.join``).join`
`,p=30)

f("This is Programming Puzzles and Code Golf! Yay!")
&lt;pre id=o&gt;&lt;/pre&gt;



How?
Building the path
The following code builds the path:
[...Array(40)].map((_, k) =&gt;
  [
    j = k % 20,
    y,
    y += 77732 &gt;&gt; j &amp; 1 ? k &lt; 20 || -1 : 0
  ],
  y = 0
)

This gives an array of arrays [j, y, z] where j is the position modulo 20, y is the y-coordinate at this position and z is not used later (it just happens to be computed here to save some bytes).
Because the path is symmetric, we only need to encode the 20 first positions. We do that by using a binary number where each 1 bit means that y must be updated (+1 for the first half, -1 for the second half).
001
   001
      01
        1
         1
         1
          1
           01
             001
                000

With the first position being mapped to the least significant bit, this gives:
00010010111110100100 as binary = 77732 as decimal

Because this binary number is itself also symmetric, we can read it in the same order for the 2nd half.
Hence the formula:
y += (77732 &gt;&gt; j) &amp; 1 ? (k &lt; 20 ? 1 : -1) : 0

which can also be written as:
y += (77732 &gt;&gt; j) &amp; 1 ? k &lt; 20 || -1 : 0

where k is the position and j is the position modulo 20.
Updating x is much easier: we just have one special case to detect by comparing the position modulo 20 with 9.
Drawing the text
In the following code, the path building code described above has been replaced by a path variable for readability.
s =&gt; setInterval(                       // set a periodic timer:
  _ =&gt;                                  //   with an anonymous callback
    o.innerHTML =                       //   which updates the content of 'o'
      [...s].map((c, i) =&gt; (            //   for each character c of the string s
          [j, y] = path[(i + p) % 40],  //     retrieve j and y from the path
          a[y] = a[y] || [...s].fill` ` //     initialize a[y] if it's undefined
        )[x] = (x += j! = 9, c),        //     update a[y][x] / update x
        x = 0,                          //     initialize x
        a = [],                         //     initialize a[]
        p++                             //     increment the phase
      ) &amp;&amp;                              //   end of map()
      a.map(r =&gt; r.join``).join`\n`,    //   join everything together
  p = 30                                //   initial phase = interval period = 30
)                                       // end of timer definition