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
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++)
{
points.add(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.
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);
}
}
}
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() {
// fade to black
noStroke();
fill(1);
blendMode(SUBTRACT);
rect(0, 0, width, height);
// reset blend mode
blendMode(BLEND);
// draw rotating circle
float t = radians(frameCount);
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);
}
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