Making a Sinus Scroller

Hi Guys,

Wondering if anyone can help me. Just started with Processing and tryign to make a Demoscene style app. I am in the process of tryign to add in a sinus scroller like was often seen on C64 Demos (example here) However my attempt renders the correct shape of the text but the characters all remain on the screen like some sort of hyperactive helix. Anyone able to to take a look at my sinus scrolling code and work out what I need to do to get the desired look?

Here is my draw function:-

void draw() {
  clear();
 // Draw Something.
 // Get values from Rocket using 
  // moonlander.getValue("track_name") or
  // moonlander.getIntValue("track_name")
  
  // Handles communication with Rocket.
  moonlander.update();
  
  // Create tracks in Rocket
  int bg_red = moonlander.getIntValue("background_red");
  int bg_green = moonlander.getIntValue("background_green");
  int bg_blue = moonlander.getIntValue("background_blue");
  
  // Use these track values to control the background colour
  background(bg_red, bg_blue, bg_green);
  
  // Show text
  PFont font = createFont("Joystix", 24, true); // Create the font
  textFont(font);
  String introText = "Welcome to The Retro Scene";
  text(introText,width/2 - (introText.length()/2)*12,height/2);
  sinusScroll();
 }
 
 void sinusScroll() {
     // Sinus Scroller tracks from Rocket
     int sinus_r = moonlander.getIntValue("sinus_red");
     int sinus_g = moonlander.getIntValue("sinus_green");
     int sinus_b = moonlander.getIntValue("sinus_blue");
     
     fill(sinus_r,sinus_g,sinus_b);
     for(charX = 0; charX < sinus.length(); charX++) {
       charWidth = textWidth(sinus.charAt(charX));
       float y = baseline + (sin(sinusX)*freq);
       text(sinus.charAt(charX),sinusX, y);
       if (sinusX > 16) {
         sinusX -= 16; 
       } else {
         sinusX = 800;
       }
       text("Character at X: " + sinusX + " Y: " + y,10,30);
     }
   }
1 Like

I can’t run your code. Can you get rid of all the extra stuff and keep the parts that you’re stuck on? Looks like you have the correct idea to use text() in combination with charAt and a custom x and y position.

I don’t really get what you’re asking, but I noticed one problem: you’re calling createFont() in draw(), which is not a good idea. A font is a collection of images and shapes that get loaded into memory, so if you do that 60 times a second, the JVM memory might fill up and slow everything down.

I sketched out a code that demonstrates how to achieve that sinusoidal effect that you’re after.
output

int numOfCharacters = 15;
float a, lengthOfChar;
float mult1,mult2;

void setup() {
  size(400, 400);
  background(0);
  lengthOfChar = 1.0*width/numOfCharacters;
  mult1 = 2.0;
  mult2 = 100;
}

void draw() {
  background(0);
  translate(0,height/2);
  translate(a*-20,0);
  for (int i = 0; i < numOfCharacters; i++) {
    float param = mult1*i/numOfCharacters;
    rect(i*lengthOfChar, mult2*sin(a+param), lengthOfChar, 40);
  }
  a += 0.1;

}

You can play with mult1 and mult2 to fine tune your movements to your desire. However, I’m not sure how to access the individual character height position (not the entire string) right now, and I don’t know if making separate font strings would be wise since each character might have a different width. I hope others can chime in and help you build on top of this.

2 Likes

Actually he already got that figured out:

for(charX = 0; charX < sinus.length(); charX++) {
       charWidth = textWidth(sinus.charAt(charX));
       float y = baseline + (sin(sinusX)*freq);
       text(sinus.charAt(charX),sinusX, y);
       if (sinusX > 16) {
         sinusX -= 16; 
       } else {
         sinusX = 800;
       }

He’s using text(string.charAt(), x, y) to place each letter individually and textWidth(string.charAt()) to figure out the next X co-… wait no he isn’t. I guess there should be some sinusX += charWidth; line in there and I have no clue what the else{ sinusX = 800; } is supposed to do. @RevDave31337, is that your issue?

Aha, spared me the trip to the processing reference. :slight_smile:

output

PFont font;
String someWord = "Supercalifragilisticexpialidocious";
float mult1, mult2, a;

void setup() {
  size(400, 400);
  background(0);
  
  font = createFont("LetterGothicStd.otf", 75);
  textFont(font);
  
  //Parameters to play with for sinusoidal pattern
  mult1 = 2.0;
  mult2 = 150;
}

void draw() {
  background(0);
  //Move origin to center
  translate(0, height/2);
  
  //Scroll to the left
  translate(a*-30, 0);
  
  //Move individual letters in a sinusoidal pattern
  for (int i = 0; i < someWord.length(); i++) {
    float param = mult1*i/someWord.length();
    float charWidth = textWidth(someWord.charAt(i));
    text(someWord.charAt(i), i*charWidth, mult2*sin(a+param));
  }
  
  //Animate
  a += 0.1;
}

3 Likes

Better like this:

PFont font;
String someWord = "Supercalifragilisticexpialidocious";
float mult1, mult2, a;

void setup() {
  size(400, 400);
  background(0);
  
  font = createFont("Arial", 75);
  textFont(font);
  
  //Parameters to play with for sinusoidal pattern
  mult1 = 2.0;
  mult2 = 150;
}

void draw() {
  background(0);
  //Move origin to center
  translate(0, height/2);
  
  //Scroll to the left
  translate(a*-30, 0);
  float x = 0;
  //Move individual letters in a sinusoidal pattern
  for (int i = 0; i < someWord.length(); i++) {
    float param = mult1*i/someWord.length();
    float charWidth = textWidth(someWord.charAt(i));
    text(someWord.charAt(i), x, mult2*sin(a+param));
    x+= charWidth;
  }
  
  //Animate
  a += 0.1;
}

Then it actually works with non-monospaced fonts :wink:

3 Likes

Good catch, thanks! :smiley:

Building letter or word sequences by measuring offsets with textWidth is a core method for a lot of neat tricks with variable-width fonts in Processing - highlighting, mixed fonts, custom justification, et cetera.

See for example on the old forums – multiple text colors:

2 Likes