A Noisy Walker

Note To Viewers At Home: This sketch disturbs my rabbit. Viewer discretion is advised.

This is a follow-up to the Random Walker post. In that previous post I made a walker that moved randomly in any (2D) direction. In this post I'll make a walker that moves according to values given by the p5 noise function. This is a pretty straight repeat of what's in the introduction to the Nature of Code.

The Noise Walker

The NoiseWalker keeps track of its position on the canvas as well as moving itself and plotting a circle in its current location. Its constructor starts it at the center of the canvas and creates a noise-offset vector to hold the inputs to the noise function. The noise-offset vector is initially given random x and y values so they aren't the same - since noise is a function if we passed in the same value to it for both x and y they would both get the same value out of it and we'd end up with a diagonal line instead of something that looks random-ish the way that we want.

The first call to the walk method will clobber the initial x and y positions so setting it to the center of the canvas isn't really important.

/**  a noise-based walker */
class NoiseWalker {
  /** create the Noise Walker
   * @param: p5 - a p5.js instance
   * @param: slider - widget to get the amount of noise offset change
   * @param: stroke_weight: how thick to make the circle line
   * @param: diameter of the circle
   * @param: fill_color: color to fill the circle
   * @param: stroke_color - color for the circle's line
   */
  constructor({p5,
               slider,
               stroke_weight=1,
               diameter=24,
               fill_color="cornflowerblue",
               stroke_color="black"} = {}) {
    let noise_upper_bound = 10**5

    this.p5 = p5;
    this.slider = slider;
    this.stroke_weight = stroke_weight;
    this.diameter = diameter;
    this.fill_color = fill_color;
    this.stroke_color = stroke_color;

    this.position = p5.createVector(p5.width / 2, p5.height / 2);
    this.noise_offset = p5.createVector(p5.random(noise_upper_bound),
                                        p5.random(noise_upper_bound));
  } // end constructor

Noise Value Offset

This property will return the current value of the slider. It's maybe overkill, but I'm trying to re-learn javascript along with p5 and follow along with The Nature of Code so I need things to be extra-obvious, sometimes.

/** getter for amount to add to the noise offset
 * @returns {number}: amount to add to the noise offset on walking
*/
get noise_offset_change() {
  return this.slider.value();
} // end get noise_offset_change

Walk

The walk method moves the walker by adding some noise to its position. The noise function outputs a value from 0 to 1 so I'll use it as a scale factor for the width and height of the canvas to get the new location of the walker. Note that we're not using the prior position of the walker when calculating the new one, just the noise.

/** update the position and noise offset */
walk() {
  this.position.x = this.p5.noise(this.noise_offset.x) * this.p5.width;
  this.position.y = this.p5.noise(this.noise_offset.y) * this.p5.height;
  this.noise_offset.add(this.noise_offset_change,
                        this.noise_offset_change);
} // end walk

Show Yourself

The Noise Walker's show_yourself method draws a circle wherever the walker happens to be at the moment. Nothing fancy.

show_yourself() {
  this.p5.strokeWeight(this.stroke_weight);
  this.p5.fill(this.fill_color);
  this.p5.stroke(this.stroke_color);
  this.p5.circle(this.position.x, this.position.y, this.diameter);
} // end show_yourself

The Sketch

I'll put the sketch in a function named "noise_walk".

NOISY_WALKER_DIV = "noisy-walker";

/** a sketch to make a circle-drawing walker that moves with noise */
function noise_walk(p5js) {
  const SLIDER_WIDTH = 500;
  const MEDIUM_TEAL_BLUE = p5js.color(0, 89, 179, 100);
  const MEDIUM_TEAL_BLUE_OPAQUE = p5js.color(0, 89, 179);
  let walker;
  let slider;

Set Up

The setup method will create the canvas, a slider which the user can use to change the size of the steps the walker takes along the noise offset, and the walker itself. The canvas and slider are placed based on the order in which you create them. In this case I wanted the slider underneath the canvas so I had to define it after the canvas (but before the walker since I pass it to the walker).

/** setup the canvas, walker, and slider */
p5js.setup = function() {
  p5js.createCanvas(
    document.getElementById(NOISY_WALKER_DIV).offsetWidth,
    400);

  // a slider to let the user change how much the noise is changing
  slider = p5js.createSlider(0, 1, 0.01, 0);
  slider.style("width", `${SLIDER_WIDTH}px`);

  walker = new NoiseWalker({p5: p5js,
                            slider: slider,
                            fill_color:MEDIUM_TEAL_BLUE});
  p5js.background("white");
} // end setup

Draw

The draw function moves and shows the walker.

/** move and draw the walker */
p5js.draw = function() {
  walker.walk();
  walker.show_yourself();

  p5js.textSize(32);
  p5js.textAlign(p5js.CENTER);
  p5js.fill("white");
  p5js.noStroke()
  p5js.rect(p5js.width/2 + 20, p5js.height - 35 , 250, 30);
  p5js.fill(MEDIUM_TEAL_BLUE_OPAQUE);
  p5js.text(`Noise Change: ${slider.value().toFixed(3)}`,
            p5js.width/2 , p5js.height - 10);
} // end draw

The End

And there's the code for the sketch at the top of the page.

Sources