# How can I create a walker with a finite trail?

Given this sketch:

``````
Walker walker;

void settings() { size(640, 360); }

void setup() {
windowTitle("Perlin Noise Walker");
background(0);
walker = new Walker();
}

void draw() {
walker.step();
walker.render();
}

class Walker {
float tx, ty;
float x, y;
float prevX, prevY;

Walker() {
tx = 0;
ty = 10_000;
x = map(noise(tx), 0, 1, 0, width);
y = map(noise(ty), 0, 1, 0, height);
}

void step() {
prevX = x;
prevY = y;

x = map(noise(tx), 0, 1, 0, width);
y = map(noise(ty), 0, 1, 0, height);

tx += 0.01f;
ty += 0.01f;
}

void render() {
stroke(255);
background(0); // no trail
line(prevX, prevY, x, y);
}
}
``````

Iâ€™m wondering can I achieve this effect.

At the moment I can turn on and off the â€śtrailâ€ť by calling `background(0)` on each render frame.

But itâ€™s not a very good solution. Iâ€™d like the â€śtrailâ€ť made by the walker to disappear after a few frames or a few seconds.

1 Like

Hello @benjamin-thomas,

``````  void render() {
stroke(255);
fill(0, 5);
rect(0, 0, width, height); // no trail
line(prevX, prevY, x, y);
}
``````

A look at Examples on the Processing page for inspiration:

One of many examples:

Consider an ArrayList of objects with a â€śTime to Liveâ€ť for each object.

The Simulate examples may be of interest.

`:)`

1 Like

Thanks @glv, this feels like a nice trick

1 Like

Really hate that it never fades back to pure black in processing, I did see a work around with pixels somewhere a few years ago but turned out to be slow, I know its a float issue but would be nice to have a clean solution

2 Likes

Indeed, I didnâ€™t see this at first. Iâ€™ve managed to work around this issue like this:

``````
Walker[] walkers;

void settings() { size(640, 360); }

void setup() {
windowTitle("Perlin Noise Walker");
background(0); // black
walkers = new Walker[]{
new Walker(0f, 10_000f),
new Walker(15_000f, 25_000f),
};
}

void draw() {
for (Walker walker : walkers) {
walker.step();
walker.render();
}
}

class Walker {
float tx, ty;
float x, y;
float prevX, prevY;

Walker(float tx, float ty) {
this.tx = tx;
this.ty = ty;
x = map(noise(tx), 0, 1, 0, width);
y = map(noise(ty), 0, 1, 0, height);
}

void step() {
prevX = x;
prevY = y;

x = map(noise(tx), 0, 1, 0, width);
y = map(noise(ty), 0, 1, 0, height);

tx += 0.01f;
ty += 0.01f;
}

void render() {
stroke(255);
line(prevX, prevY, x, y);

// Make trail finite
noStroke();
fill(0, 30);
rect(0, 0, width, height);

// Really clear. A faint trail remains indefinitely otherwise.
if (frameCount % 60 == 0)
background(0);
}
}
``````

It works okay for the short trail I had in mind but Iâ€™d be interested to know if thereâ€™s a better way to handle that problem.

1 Like

Hello @benjamin-thomas,

Consider using an ArrayList().
There is a learning curve to this but that is the fun part.

Example that I cooked up:

``````ArrayList<Integer> points = new ArrayList<Integer>();
int num = 360;

float phase;

void setup() {
size(500, 500, P2D);
for (int i=0; i<num; i++)
{
}
}

void draw()
{
background(0);
translate(width/2, height/2);
phase += TAU/360;
noStroke();
for (int i=0; i<num; i++)
{
int alpha = i*255/num;
fill(255, 0, 0, alpha);
//float angle = i*TAU/num + phase;
float angle = points.get(i)*TAU/num + phase;
float x = 200*cos(angle);
float y = 200*sin(angle);
circle(x, y, 10);
}
}
``````

Start simple and build on that.
The above is just storing points for nowâ€¦

Your will have to add and remove objects/elements from an ArrayList of objects created with your class as your random walker generates these and then redraw these for each draw() cycle.

`:)`

Related topic:

I did suggest using an ArrayList and provided links and a very minimal example to inspire.

`:)`

Thanks for the nudge @glv : )

I couldnâ€™t think of a good way to handle adding and removing elements to an ArrayList, so I instead tried to use a static array (acting like a buffer) and it seems to work quite well.

I then applied transparency linearly to make the trail fade as time passes.

``````private Walker[] walkers;

@Override
public void settings() {
size(640, 360);
}

@Override
public void setup() {
windowTitle("Perlin Noise Walker");
walkers = new Walker[]{
new Walker(0f, 10_000f),
new Walker(15_000f, 25_000f),
};
}

@Override
public void draw() {
background(0); // clear previous renders
stroke(255);
strokeWeight(2);

for (Walker walker : walkers) {
walker.step();
walker.render();
}
}

class Walker {
private int cursor;
private float tx, ty;
private final PVector[] points = new PVector[25]; // adjust trail size here

Walker(float tx, float ty) {
this.tx = tx;
this.ty = ty;
this.cursor = 0;
step();
}

void step() {
float x = map(noise(tx), 0, 1, 0, width);
float y = map(noise(ty), 0, 1, 0, height);

points[this.cursor] = new PVector(x, y);
this.cursor++;

// Clear trail once buffer has been filled.
if (this.cursor >= points.length) {
// shift all by one (clear first data point)
for (int i = 1; i < points.length; i++) {
points[i - 1] = points[i];
}
// backtrack
this.cursor--;
}

tx += 0.01f;
ty += 0.01f;
}

void render() {
for (int i = 1; i < points.length; i++) {
PVector v = points[i];
if (v == null) break; // buffer not filled yet

PVector prev = points[i - 1];

// Make the trail fainter and fainter
float progression = i / ((float) points.length);
float alpha = 255 * progression;
stroke(255, 255, 255, alpha);

line(prev.x, prev.y, v.x, v.y);
}
}

}
``````

1 Like

Many ways to code thingsâ€¦ something is learned with each path taken.

``````// GLV
// 2023-04-13
// Version 1.0.0

ArrayList<Float> theta = new ArrayList<Float>();

void setup()
{
size(500, 500);
}

float angle0;
int slice;
int div;

void draw()
{
background(0);
translate(width/2, height/2);
noStroke();

int sliceSize = 90; // degrees

div = 360;

// Update
angle0 = (frameCount%div)*(TAU/div); //0 to TAU in radians and repeat
// Try different values for rad
slice = theta.size();

if (slice>sliceSize)
{
theta.remove(0);
}

// Render
for (int i=0; i<theta.size(); i++)
{
render(i);
}
}

void render(int j)
{
float angle = theta.get(j);
float x = r*cos(angle);
float y = r*sin(angle);

int alpha = j*255/(slice);
fill(255, 128, 0, alpha);
circle(x, y, 5);
//println(j, r, theta.get(j), degrees(angle), x, y, alpha); //debug as required
}
``````

A 270 slice:

Have fun!

`:)`

Oh now I get it

Many thanks!

This actually has a great solution without the ugly trails using blendMode(SUBTRACT) for fading to black as seen in the code example below or using blendMode(ADD) for fading towards white. This is fast and clean. You can even use blendMode(LIGHTEST) to enforce a less extreme color like 38/255 as the minimum and similarly also blendMode(DARKEST).

``````void setup() {
size(500, 500);
colorMode(RGB, 255, 255, 255, 100);
background(0);
}

void draw() {
noStroke();
fill(1);
blendMode(SUBTRACT);
rect(0, 0, width, height);

// reset blend mode
blendMode(BLEND);

// draw rotating circle
float r = 200;
float x = r * cos(t);
float y = r * sin(t);
float s = 50;
translate(width/2, height/2);
fill(255);
ellipse(x, y, s, s);
}
``````

2 Likes

Nice! That look like a cool technique, thanks

1 Like

Legend, been working with particles and had that issue for years lol, you just solved it, sad because ive used blend mode many times for a glow effect and never thought of using it in this sense, thanks matey for adding to my knowledge

1 Like