Repeated alpha not reaching full opacity

Hello,

I’m using a rectangle filled with white and a certain alpha value at the beginning of draw, and I would think that if done enough times that eventually the first thing drawn in setup would eventually be totally white. I’ve tested and it only becomes totally white if the alpha value is 128 or above.

For example, see if with this code the red circle ever becomes fully white:


float x0, y0;
float angle0, angleC;
float radius = 5; 
int bg = 255;
int alph = 10; // try up to 127

void setup() {
  size(500, 500);
  background(bg);
  x0 = random(width);
  y0 = random(height);
  angle0 = random(360);
  fill(255, 0, 0, 255);
  ellipse(x0, y0, 50, 50);
}

void draw() {
  
  fill (bg, bg, bg, alph);
  rect(0, 0, width, height);
  
  float x1 =  x0 + cos(radians(angle0)) * radius;
  float y1 =  y0 + sin(radians(angle0)) * radius;
  stroke(0);
  line(x0, y0, x1, y1);
   
  x0 = x1;
  y0 = y1;
   
  if ((x0 > width) || (y0 > height) || (x0 < 0) || (y0 < 0)) {
    x0 = random(width);
    y0 = random(height);
    angle0 = random(360);
  }
}

And my main question would be since the lower alpha levels don’t add up enough to make full opacity how could I slow down the fade out without reducing frame rate? Using an alpha of over 127 is too fast.

1 Like

working the other way with real fading lines?

float x0, y0, x1, y1, angle0, radius; 
int alpha;

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

void draw() { 
  background(255);
  oneLine();
  animate();
}

void oneLine() {
  x1 =  x0 + cos(angle0) * radius;
  y1 =  y0 + sin(angle0) * radius;
  stroke(0, 0, 0, alpha);
  line(x0, y0, x1, y1);
}

void animate() {
  radius++;
  alpha--;
  if ((x1 > width) || (y1 > height) || (x1 < 0) || (y1 < 0) || alpha < 0 ) reset();
}

void reset() {
  x0 = random(width);
  y0 = random(height);
  angle0 = random(TAU);
  radius=5;
  alpha=255;
}

3 Likes

Yeah, that is pretty good. It does switch it to thinking more in terms of animation instead of painting on the canvas. I need to think more about the consequences of that. (For example, if I use a lines alpha value to fade it I won’t be able to use it for how it relates to other lines.)

yes, only one line for now,

for many use ? arraylist of class ?
as need to remember

  • x0,y0,angle0, radius, ? radius speed? , ?color?, alpha, ? alpha speed?

first just change the above code for one line to a class

good exercise

ArrayList<OneLine> mylines = new ArrayList<OneLine>();
int many = 5;

void setup() {
  size(500, 500);
  for ( int i =0; i < many; i++) mylines.add( new OneLine() );
}

void draw() { 
  background(0,0,80);
  for ( OneLine myline : mylines ) myline.draw();
}

class OneLine {
  float x0, y0, x1, y1, angle0, radius;
  float strokeR, strokeG, strokeB;
  int alpha;

  OneLine() {
    reset();
  }

  void draw() {
    animate();
    x1 =  x0 + cos(angle0) * radius;
    y1 =  y0 + sin(angle0) * radius;
    stroke(strokeR, strokeG, strokeB, alpha);
    strokeWeight(2);
    line(x0, y0, x1, y1);
  }

  void animate() {
    radius++;
    alpha--;
    if ((x1 > width) || (y1 > height) || (x1 < 0) || (y1 < 0) || alpha < 0 ) reset();
  }

  void reset() {
    x0 = random(width);
    y0 = random(height);
    angle0 = random(TAU);
    radius=5;
    strokeR= random(100, 200);
    strokeG= random(100, 200);
    strokeB= random(100, 200);
    alpha=255;
  }
} // end class

4 Likes

Here’s a version slightly modified from kll’s version above, which has randomised line fades (uses float instead of int for alpha and picks random alphaStep fade amount per line). I also removed the edge checking (as it looked better without), made 500 instead of 5 and changed the renderer to P2D as it runs faster when accelerated:

ArrayList<OneLine> mylines = new ArrayList<OneLine>();
int many = 500;

void setup() {
  size(500, 500, P2D);
  for ( int i =0; i < many; i++) mylines.add( new OneLine() );
}

void draw() { 
  background(0,0,80);
  for ( OneLine myline : mylines ) myline.draw();
}

class OneLine {
  float x0, y0, x1, y1, angle0, radius;
  float strokeR, strokeG, strokeB;
  float alpha, alphaStep;

  OneLine() {
    reset();
  }

  void draw() {
    animate();
    x1 =  x0 + cos(angle0) * radius;
    y1 =  y0 + sin(angle0) * radius;
    stroke(strokeR, strokeG, strokeB, alpha);
    strokeWeight(2);
    line(x0, y0, x1, y1);
  }

  void animate() {
    radius++;
    alpha-=alphaStep;
    if ( alpha < 0 ) reset();
  }

  void reset() {
    x0 = random(width);
    y0 = random(height);
    angle0 = random(TAU);
    radius=5;
    strokeR= random(100, 200);
    strokeG= random(100, 200);
    strokeB= random(100, 200);
    alpha=255;
    alphaStep = random(0.5, 1.5);
  }
} // end class
4 Likes

By default this is not the case – instead, in the default blendMode(), alpha pixels get a certain percentage of the way closer to the target color… the pixels never reach 100% value because, at a certain distance, the next step is so small that it drops below the rounding error. For discussion and test code, see:

You may use blendMode(ADD) to get the behavior you were expecting.

2 Likes

That works:

float x0, y0;
float angle0;
float radius = 5; 
int bg = 255;
int alph = 3;

void setup() {
  size(500, 500);
  background(bg);
  x0 = random(width);
  y0 = random(height);
  angle0 = random(360);
  fill(255, 0, 0, 255);
  ellipse(x0, y0, 50, 50);
}

void draw() {
  //blendMode(ADD);  //uncomment this to compare
  noStroke();
  fill (bg, bg, bg, alph);
  rect(0, 0, width, height);
  
  blendMode(BLEND);
  
  float x1 =  x0 + cos(radians(angle0)) * radius;
  float y1 =  y0 + sin(radians(angle0)) * radius;
  stroke(0);
  line(x0, y0, x1, y1);
   
  x0 = x1;
  y0 = y1;
   
  if ((x0 > width) || (y0 > height) || (x0 < 0) || (y0 < 0)) {
    x0 = random(width);
    y0 = random(height);
    angle0 = random(360);
  }
}
2 Likes

Sorry if this is a bit late, but your solution only seems to work with a white background, not with a black one.

Im trying to get is to work with my p5 sketch as in this post:
https://discourse.processing.org/t/trails-with-background-transparency/17139/3?u=skranker

Here is your solution with the white background that works perfectly:

And here i am trying to do the same thing with a black background:

The background color turns to (205, 205, 205) for some reason and the transparency is not applied at all.

This will not work, because ADD is additive.

It adds 255 (*3/255) to something, so it adds 3 to the value each frame. What happens when you add 0 – or 0 (*3/255) to something? How many times do you need to add 0 to something before it changes…?

If you want to fade things to black against a black background, one method is to SUBTRACT a fraction of a white rectangle each frame.

void setup() {
  size(400, 400);
  stroke(255);
  strokeWeight(5);
  background(0);
}

void draw() {
  blendMode(SUBTRACT);
  fill(255, 3);
  rect(0, 0, width, height);
  blendMode(BLEND);
  point(mouseX, mouseY);
}
2 Likes

Ok that makes sense now that you say it. I never really worked with blendModes so far.

It works fine in processing, but i still can’t seem to get it to work in p5, where the SUBTRACT blendMode seems to be only working with WEBGL which i have not much knowledge about but it seems to be only for 3D stuff?:

https://p5js.org/reference/#/p5/blendMode

How would i go about achieving this same effect in p5?

It apparently is not as simple as this: :smiley:

function setup() {
  createCanvas(400, 400, WEBGL);
  stroke(255);
  strokeWeight(5);
  background(0);
}

function draw() {
  blendMode(SUBTRACT);
  fill(255, 3);
  rect(0, 0, width, height);
  blendMode(BLEND);
  point(mouseX, mouseY);
}
1 Like

I haven’t tried in p5 – interesting fact about WEBGL. In general, attempting to wash a fading color repeatedly over the canvas can be an inflexible strategy as soon as you have elements that fade at different times and different ways – or don’t fade. The easiest thing to do if you are drawing a series of objects / segments and want fading trails to keep an array of drawn items and redraw them each frame, decreasing the opacity of each item in the list.

This approach is covered in-depth for p5 in:

The only change would be to make the tint of each trail point / segment dependent on its posiition in the for loop. Then the trails are fading.

There is a generalization of this to all canvas “objects.” Create one or more classes for your drawables (points, lines, shapes, sprites, whatever) and make sure that each one has a timeCreated. When you create the thing, stamp it with the current frameCount or millis() (depending on how you want sketch time to work). When you draw the thing, make it fade based on how old it is – by setting tint or its fill alpha etc. based on the difference between its timeCreated and the current time.

4 Likes

Yeah, I think that is the better way to do it: making it depend on the class, not on the background.

Yeah i already had a version of my sketch with arrays for the trails, but there were some drawbacks that led me to revert it to the workflow with background transparency.

Maybe i did something wrong, but the transparent circles overlapped kind of oddly, not the way they did with background transparency. Also the performance seemed to drop quite a bit. And i would’ve had create arraylists for every single item i wanted to draw, and there were a lot of them.

In the end i used a weird band-aid fix where i have a kind of “cleaner” object that crosses the screen once and “fixes” every pixel for the background transparency. The background is not completely black but im ok with that.

Working with multiple canvases and createGraphics() i use both objects with and withouth trails, so that works for me.

Thanks for the replies, though!