Smoke from Perlin noise

Hi everybody,
I recently got mesmerized by this sketch found on openprocessing.org.
I wanted to tip my toe into p5 so I decided to try to adapt the code and here is my shot:

let x;
let t = 0;
let w = 255;
let m = 0;
let c;
function setup() {
  createCanvas(255, 255);
}

function draw() {
  t++;
  background(0);

  for (x = 0; x < w * w; x++) {
    X = x % w + n();
    Y = x / w + n();
    c = 50 + (get(X, Y) >> 8 & w);
    set(X, Y, color(c, c, c*2));
  }
  updatePixels();
}

function n(){
  return noise((x % w / 0.99), (x / w / 0.99), (t / 0.99 + m++ % 2)) * w - w/2;
}

Although the code is pretty identical to the exemple from openprocessing, I only could get a cloud of noise when running it, without the elegant smoke behaviour.
I’m quite familiar with Java but much less with JS, so maybe I’m forgetting something obvious here…

Anyway I think some code is missing to reproduce the exemple, so I’m asking for your wise guidance.
Thank you

1 Like

after a quick look in your noise function you are dividing by 0.99 whereas the source code is dividing by 99. there may be other things but that’s probably worth changing.

so this

  return noise((x % w / 0.99), (x / w / 0.99), (t / 0.99 + m++ % 2)) * w - w/2;

should probably be this

  return noise(x % w / 99, x / w / 99, t / 99 + m++ % 2) * w - w/2;

1 Like

set() is slow. You should instead use https://p5js.org/reference/#/p5/pixels

Processing main site has tutorial on pixels and how to access them. Commands are slightly different (Java) but it all applies to p5.js too https://processing.org/tutorials/pixels/

1 Like

Thank you both for your help.

should probably be this

 return noise(x % w / 99, x / w / 99, t / 99 + m++ % 2) * w - w/2;

Yep this is much nicer now!

set() is slow. You should instead use https://p5js.org/reference/#/p5/pixels

It’s what I tried below :

let x = 0;
let t = 0;
let w = 255;
let m = 0;

function setup() {
  createCanvas(w, w);
  pixelDensity(1);
}

function draw() {
  t++;
  background(0);
  loadPixels();
  for (x = 0; ++x < w * w;) {
    
    X = x % w + n();
    Y = x / w + n();
    
    index = (X + Y * w) * 4;
    c = 50 + pixels[index + 1] & w;

    pixels[index + 0] = c;
    pixels[index + 1] = c;
    pixels[index + 2] = c * 2;
    pixels[index + 3] = 255;
  }
  updatePixels();
}

function n() {
  return Math.round(noise((x % w / 99), (x / w / 99), (t / 99 + m++ % 2)) * w - w / 2);
}

but I still have a super slow framerate (~3 fps)…

Edit : I did try some optimization technique found here https://hacks.mozilla.org/2011/12/faster-canvas-pixel-manipulation-with-typed-arrays/ with no success whatsoever .

2 Likes

Hi @segu;

noise in p5.js seems to be very slow. Calling it twice makes the sketch even slower.
I would suggest to use an external module instead.

Here is an example with Simplex Noise (see this post for loading):

p5.disableFriendlyErrors = true;

// storing constants
const w = 255;
const s = w*w;
const k = 99;
const f = 30;
const posx =  [...Array(s).keys()].map(i => (i % w));
const posy =  [...Array(s).keys()].map(i => (i / w));
const a = [...Array(s).keys()].map(i => posx[i] / k);
const b = [...Array(s).keys()].map(i => posy[i] / k);

let x, y;
let i = 0, t = 0, m = 0;
let simplex = new SimplexNoise();


function setup() {
  createCanvas(w, w);
  pixelDensity(1);
}


function draw() {
  t++;
  background('#000');
  
  loadPixels();
  for (i =0; ++i < s;) {
        
    x = posx[i] + Math.round(simplex.noise3D(a[i], b[i], (t/k + m++ % 2)) * f);
    y = posy[i] + Math.round(simplex.noise3D(a[i], b[i], (t/k + m++ % 2)) * f);
    
    index = (x + y * w) << 2;
    c = 55 + pixels[index];
  
    pixels[index] = c;
    pixels[index + 1] = c;
    pixels[index + 2] = c << 1;
  }
  updatePixels();
}
3 Likes

Cool @solub !!
I will experiment with this instead.

One question though : can you explain the [...Array(s).keys()] syntax? You’re crating an array where each element is the mapping function?

Thanks

Glad it helped.

Here the map() method iterates over the array [...Array(s).keys()] and calls a function i => (i % w) on each of its element .

In other words:

  • [...Array(s).keys()] is similar to [0, 1, 2, ...s]

  • for each i in [0, 1, 2, ...s] the function makes the conversion (i % w)

So, to answer your question, each element in the final array is not a function but the result of that function. They are mapped (already transformed) in order to avoid redundant calculations in the draw() function.

On a side note posx and posy can be merged into one single array:

const w = 255; //width of canvas
const n = w*w; //total number of points

const pos  = [...Array(n*2).keys()].map(i => (i & 1 ? Math.floor(i/(w<<1)): (i-(i>>1))%w) )

for (let i = 0; i < n*2; i+=2) {
    x = i
    y = i+1
    ...
  }

Unlike before, pos now contains both the x and y positions (one after the other) in a single array. Same thing can be done for arrays a and b.

1 Like

JS already provides a much more simplified method for such cases:

We can replace the whole thing like this:

const W = 255, S = W * W, K = 99, F = 30,
      posX = Array.from({ length: S }, (v, i) => i % W),
      posY = Array.from({ length: S }, (v, i) => i / W),
      a = Array.from(posX, x => x / K),
      b = Array.from(posY, y => y / K);

console.log(posX);
console.log(posY);
console.log(a);
console.log(b);

The excerpt above can be pasted on any browser console btW. :wink:

P.S.: Since those 4 arrays store numerical values only, we can use typed arrays for them instead: :bulb:

const W = 255, S = W * W, K = 99, F = 30,
      posX = Uint8Array.from({ length: S }, (v, i) => i % W),
      posY = Float32Array.from({ length: S }, (v, i) => i / W),
      a = Float32Array.from(posX, x => x / K),
      b = Float32Array.from(posY, y => y / K);

console.log(posX);
console.log(posY);
console.log(a);
console.log(b);
1 Like

I know, and I recently started to use typed arrays as well (d3.js ‘sketches’ are full of them) but for some reason I find [...Array(s).keys()] to be more readable. That’s a bad habit I guess.

1 Like

It seems shorter but there are some issues to it:

  • You’re creating 2 arrays here: Array(s) & [].
  • An array iterator too: .keys().
  • Plus a destructuring via the rest operator: ....
  • And then map() creates the 3rd last array: .map(i => (i % w))

Method Array.from() saves some steps and I guess it’s slightly faster. :racing_car:

2 Likes