Noise generation, aka clouds, is really slow and heavy

Hello there,
I have been working on this small program that I would like to use as a fullscreen background. However, it already is really heavy. Is there a way to fix that in my code or I just have to think of another way to use it?

var density = 0.03;
var wind = 0;
var speedOfWind = 0.00002;

function setup() {
  canvas = createCanvas(500,500);
	//canvas = createCanvas(windowWidth, windowHeight);
  canvas.position(0,0);
  canvas.style('z-index', -1);
  pixelDensity(1);
  // frameRate(100);

}

function draw() {

  var yoff = 0;
  // translate(advance, advance * 0.5);
  loadPixels();
  for (var y = 0; y < height; y++) {
    var xoff = 0;
    for (var x = 0; x < width; x++) {
      var index = (x + y * width) * 4;
    
      var r = noise(xoff + wind, yoff - wind) * 255;
      pixels[index + 0] = r;
      pixels[index + 1] = r;
      pixels[index + 2] = r;
      pixels[index + 3] = 51;
      xoff += density;
    }
    yoff += density;
    wind+= speedOfWind;


  }
  updatePixels();
}

Just to clarify:
I tried increasing the frameRate() to 100 or 200, but nothing changed.
I am using a MacBook Pro 2014 Retina, so I suppose my resolution is quite high.

Any help would be appreciated. It’s my first post on the forum. I don’t know if this way of posting code is okay, let me know.

All the best!

Hi! One way to speed it up would be to:

  1. copy the image pixels one pixel left and down.
  2. generate the top row of pixels using the noise function
  3. generate the right column of pixels using the noise function

This way you avoid calling noise for every single pixel on the screen, but only a tiny fraction of them.

Another (more complex, faster) option is to use shaders to generate that background.

2 Likes

To add to @hamoid answer, the frameRate function is not working the way you think.

Calling frameRate(100) won’t make your sketch faster. It is just a limit speed that you are setting. It means that no matter how fast you can generate the next image, you won’t display it unless 10 milliseconds has passed since the last image, insuring that you won’t have more than 100 frame generated in 1 second.

1 Like

Good point @jb4x, I forgot to mention that :slight_smile:

Minor correction (typo?): for frameRate(100) it’s “10ms has passed” instead of “100ms has passed”.(1000ms/100frames=10ms/frame and the default is 1000ms/60frames=16.666ms/frame).

Thanks, it’s corrected :slight_smile:

Hello, thank you for your quick reply. I tried to figure this out but I couldn’t.
I got the copy() function, but I cannot implement it. In the example, an image is copied, how do I copy the pixels? I am very confused…

I am posting another version of the sketch here, if you would like to give me an example :slight_smile:
https://editor.p5js.org/michelangelo/sketches/Hkehv3bJE

Hi! I know it’s not very intuitive to copy an image onto itself :slight_smile:
Here something you could use to get started :slight_smile:

https://editor.p5js.org/abe/sketches/HkQBn3WJE

Hi,
something weird happened yesterday, because my post was flagged for spam!?!

Anyway, yesterday I made this https://editor.p5js.org/michelangelo/sketches/Hkehv3bJE.
Your example really helped! Thank you!

Now there are still a few problems:
– The generated image is a bit skewed, I don’t understand why, is it the speed of the draw(), is it something else.
– The way I make the initial image in the setup() and later in draw(), seems a bit redundant, but I can’t think of a better way.
– Maybe my if statement is also not proper?
– Aaaand at the end, is the sketch faster? How can I test that: is the Performance tab from the Dev tools for this kind of a test.

Thank you again :slight_smile:

– The generated image is a bit skewed, I don’t understand why, is it the speed of the draw(), is it something else.

You need to offset the noise to counteract the movement. Even if you always calculate the noise for the same positions (top and right borders), you should add an “imaginary” offset because you are actually sampling noise from a position that goes up and right.

In code, an alternative that would work is this:

var r = noise((x+frameCount)*density,  
              (y-frameCount)*density) * 255;

Notice how there is a variable that always increases (frameCount). You don’t need to use x, y and frameCount, but this was the easiest working approach I could think of.

– The way I make the initial image in the setup() and later in draw(), seems a bit redundant, but I can’t think of a better way.

To avoid redundancy, you could create a function like this:

function setPixel(image, pixelIndex, colorValue);

and you could call this function in both places (in setup and in draw), so you avoid repeating code.

– Maybe my if statement is also not proper?

It’s not efficient :slight_smile: It’s better to make two non-nested for loops. The first for loop iterates over 500 pixels. The second for loop can iterate over 499 pixels (if you do 500 pixels you repeat the corner pixel). That’s a total of 999 iterations. Instead, if you nest the two for loops you are doing 500x500 iterations (250.000) and on top of that there’s an if-statement to skip 249.001 of those iterations. If you have two for loops one after the other you don’t need the if statement, and you iterate much less.

– Aaaand at the end, is the sketch faster? How can I test that: is the Performance tab from the Dev tools for this kind of a test.

You could use the Dev tools, or a very simple approach: add this at the end of draw:

if(frameCount % 60 == 0) { console.log(floor(frameRate())); } 

which will print the frame rate every second. Of course this is only useful if you are hitting the frame rate limit (say it used to be 30 with the previous approach, and now it’s 50). If you were at 60 fps already then maybe you didn’t need to optimize it :slight_smile:

Thank you for your help! I understand my mistakes. Super nice that you gave me some examples.

Wish you all the best :slight_smile:

1 Like