8-bit sine scroller



Remember the sine scroller effect from 8-bit demos and intros in the 90's?

They were done in hundreds of lines of machine code, with hardware interrupts and pre-calculated sine tables. Here is an example in about 30 lines of pure JavaScript.

We need a bitmap image with a fixed-width font (also called mono-spaced or non-proportional). That means that each letter has the same width in pixels, which will greatly simplify our calculations. This paragraph uses a proportional font, so 'w' is wider than 'i'. The code example in the table uses a non-proportional font.



The font above was ripped from some intro for the legendary Commodore 64 (I found it in the C-64 logo generator, but you can also just type in the alphabet in another font in an image editor and save it as a bitmap).

Now we're ready for the code:

<html>
<body style='background-color:black'>
<canvas id='myCanvaswidth='600height='800'></canvas>
<script>
const text = '           old school sine scroller    another cool javascript tutorial';
const fontWidth = 54;
const fontHeight = 71;
const letters = 12;
let x = [];
let char = [];
let wiggle = 0;
let counter = 0;
let position = letters;
let bitmap = new Image();
bitmap.src = 'font.png';
let canvas = document.getElementById('myCanvas');
let context = canvas.getContext('2d');
for (let n = 0n < lettersn++) {
  char[n] = text.charCodeAt(n) - 97;
  x[n] = n * fontWidth;
  }
window.requestAnimationFrame(scroll);
 
function scroll() {
  context.clearRect(00canvas.widthcanvas.height);
  for (let n = 0n < lettersn++) {
    let y = 100 + wiggle * Math.sin(n + counter / 6.28);
    context.drawImage(bitmapchar[n] * fontWidth0fontWidthfontHeightx[n], yfontWidthfontHeight);
    x[n]--;
    if (x[n] < -fontWidth) {
      x[n] = (letters - 1) * fontWidth;
      char[n] = text.charCodeAt(position) - 97;
      position++;
      if (position > text.lengthposition = 0;
      }
    }
  if (counter > 200 && wiggle < 30wiggle = wiggle + .1;
  counter++;
  window.requestAnimationFrame(scroll);
  }
</script>
</body></html>


The main program just initializes the variables:

Lines 1-3: The HTML5 setup
5: The text of of our scroll. A couple of spaces in front so that it starts on the right hand side of the screen.
6-7: width and height of the font, in pixels
8: the number of letters to be scrolled
9: the array of the x coordinate of the letters
10: the character at a given position
11: the wiggle (amplitude) factor - we start with a flat line
12: time counter
13: the current position in the text
14-15: source image (not visible on the screen)
16-17: target canvas
18-21: set the first set of characters and their x coordinates
22: start the scroll animation

All the real action is in the scroll function:

25: clear the previous frame
26: for each letter:
27: calculate the y coordinate using the sine function
28: copy the character from the bitmap to the canvas.

This line is a little complicated, so let's look at the arguments in detail:

bitmap - source bitmap
char[n] * fontWidth, 0 - top-left corner coordinates of the source image of the n-th character
fontWidth, fontHeight - size of the source image (one character)
x[n], y - coordinates of the target location on the canvas
fontWidth, fontHeight - size of the copy (also one character)

29: move the letter to the left
30-33: if the letter moved off of the screen to the left, put it back on the right hand side and assign the next character from the scroll text
32: convert the JavaScript (ASCII) character to the 0-23 range: a is 0, b is 1 and so on.
34: restart if end of text reached
37: the wiggle factor determines the amplitude of the sine wave. We started with zero - flat line. When the counter reaches 200, we start increasing the amplitutde, until we reach the maximum value of 30.
38: increase the counter in each animation frame
39: request another frame

There is a lot of things you can add to this code:

- non-alpha character handling (numbers, comma, period etc.)
- control the speed of the scroll with the text (eg. speed up when the next letter is '@', slow down if it's '#')
- control the amplitude with the text
- calculate individual y coordinate for each vertical line of pixels (instead of each letter)
Enjoy!


Check out these programming tutorials:

JavaScript:

Optical illusion (18 lines)

Oldschool fire effect (20 lines)

Animated fractal (32 lines)

Physics engine for beginners

Starfield (21 lines)

Yin Yang with a twist (4 circles and 20 lines)

Interactive animated sprites

Your first program in JavaScript: you need 5 minutes and a notepad


Fractals in Excel

Python in Blender 3d:

Domino effect (10 lines)


Wrecking ball effect (14 lines)

3d fractal in Blender Python