Here are some steps you could do:
Prerender each char to an off-screen canvas in a solid color on transparent background. This canvas will be used as a sprite-sheet later. Drawing a rounded rectangle is a relative expensive operation especially when you need x number of it per char.
Set up a gradient for the colors and store that too in an off-screen canvas (by now we can see memory-caching is coming to the rescue..).
Every time you update use
requestAnimationFrame
instead of setInterval. The latter is not able to synchronize to monitor update. Scroll delay can be calculated using the number of frames elapsed as well as the time since last update (see rAF doc for details).For each update clear, then "blit" the letter from the sprite-sheet canvas to main canvas. When all are blitted change composite mode to
source-atop
and blit the gradient canvas on top, and reset composite mode tosource-over
(don't use save/restore - they are expensive).
In the last step you could also use composite mode copy
instead of clearing canvas and using source-over, as that will remove any previous existing pixels for the area you draw to.
Hope this gives you some inputs to improve the performance.