When bounded by the width and height of a canvas, two-dimensional random walks produce a complex marbled texture that is darker in areas where the path has doubled back on itself frequently and lighter in areas less traveled.

Representational random walks stretch and shrink the length of random walk line segments to encourage the light and dark portions of the resulting image to correspond with the light and dark portions of a target image.

The example above uses a picture of my dog as the target image, along with the following source code. It is rendered using processing.js.

class RepresentationalRandomWalk {
  private float step, perturbation, alpha, x, y;
  private PImage img;

  public RepresentationalRandomWalk(PImage img, float step,
                                    float perturbation, float alpha) {
    this.img = img;
    this.alpha = alpha;
    this.step = step;
    this.perturbation = perturbation;

    this.x = img.width / 2;
    this.y = img.height / 2;
  }

  public void step() {
    color pixel = img.get(round(x), round(y));    
    float magnitude = (red(pixel) + green(pixel) + blue(pixel))/3;

    float angle = random(0, 2 * PI);
    float x2 = x + (perturbation + step * magnitude) * cos(angle);
    float y2 = y + (perturbation + step * magnitude) * sin(angle);

    x2 = constrain(x2, 0, img.width);
    y2 = constrain(y2, 0, img.height);

    stroke(0, 0, 0, alpha);
    line(x, y, x2, y2);

    x = x2;
    y = y2;
  }
}

PImage img;
RepresentationalRandomWalk w;

void setup() {
  size(1000, 800);
  pixelDensity(2);
  colorMode(RGB, 1);
  background(1);

  img = loadImage("dog.jpg");
  w = new RepresentationalRandomWalk(img, 10, 1, 0.02);
}

float k = 1.1;
boolean started = true;

void draw() {
  if (started) {
    for (int i = 0; i < k; i++) {
      w.step();
    }

    if (k < 5000) k *= 1.01;
  }
}

void keyPressed() {
  started = !started;
}

Note that because Processing does not allow dynamically sized windows, the parameters of size(1000, 800) must be adjusted for each target image. The example stops automatically after 3,000,000 steps.

Representational random walks are composed of line segments connected to each other by random angles. If the random walk lands on pixel $(i, j)$, it observes the brightness of this pixel. The pixel can range between $d(i, j) = 1$ if the pixel is perfectly dark, to $d(i, j) = 0$ if it is perfectly white.

Segment length depends on the step ($s$) and perturbation ($p$) values. Each segment’s length varies linearly between $p$ and $(p+s)$ based on the darkness of the pixel its starting point is currently sitting under, for $p > 0$. For $s = 0$, the representational random walk turns into a regular random walk. For $p$ near zero, the walk spends more time filling in dark areas and jumping over light areas. Let $(x_t, y_t)$ be the position of the random walk at time $t$. For a randomly generated angle $\theta \in [0, 2\pi)$:

$$ x_{t+1} = x_t + ((1 - d(x_t, y_t))s + p) \cos \theta $$

$$ y_{t+1} = y_t + ((1 - d(x_t, y_t))s + p) \sin \theta $$

A line segment is then drawn between $(x_t, y_t)$ and $(x_{t+1}, y_{t+1})$, where $(x_{t+1}, y_{t+1})$ is bounded by the dimensions of the image. More examples varying $s$ and $t$ (work in progress):