Stroke Gradient for Lines

Hi there,

I am trying to make lines with gradient strokes. I have done something, but It does not work, who can help me with this? Thanks!

size (900, 900);
background (255);
colorMode(HSB, 360, 100, 100);

int hueStart = 60;
int hueEnd = 180;
for (int y = hueStart; y < hueEnd; y++) {
  stroke(y, 100, 100);
  strokeWeight(30);
  line(0,0,1000,0);
}
1 Like

You draw 120 lines that are different colors but are all the same line! That is, you cover up 119 lines when you draw the last one.

What you need to do is draw 120 PARTS of the one line. Each part will be 1/120 of the line long, and be in a different position depending on which part it is.

Consider:

size (900, 900);
background (255);
colorMode(HSB, 360, 100, 100);

int hueStart = 60;
int hueEnd = 180;
for (int y = hueStart; y < hueEnd; y++) {
  stroke(y, 100, 100);
  strokeWeight(30);
  line(100,y,100,y+1); // Draw only a small part of the line.
}
3 Likes

:sweat_smile:

Oh my god, now I see! Thank you so much four your help. But how could I do an horizontal gradient? Definitely, not like this

size (900, 900);
background (255);
colorMode(HSB, 360, 100, 100);

int hueStart = 60;
int hueEnd = 180;
for (int y = hueStart; y < hueEnd; y++) {
  stroke(y, 100, 100);
  strokeWeight(30);
  line(0, 0,900,y+1); // Draw only a small part of the line.
}

Hello,

Take a look at the tutorials and references:

:)

If you use P2D or P3D I believe you can use beginShape() / vertex() and change the stroke colour per vertex, which would be a slightly more efficient way of doing this?

I have done it recycling another code:

size (1000, 1000);
background (255);
colorMode(HSB, 360, 100, 100);
int start = 0;
int end = 1000;
int hueStart = 60;
int hueEnd = 180;
for (int y = start; y < end; y++) {
  stroke(map(y, start, end, hueStart, hueEnd), 100, 100);
  line(y, 0, y, 10);
}
1 Like

Nice work! Now see if you can turn it into a generic function?

void drawGradientLine(float x1, float y1, float x2, float y2, color color1, color color2) {
  // good luck!
}

That would be nice but I have never been able to do a generic function following tutorials and references.l :exploding_head:

Now I have made a loop with horizontal lines and would like to give them some different gradients as strokes, but I really don’t know how to join this two codes. Could you give me a helping hand?

float start = 0;
float end = 1000;
float huePink = 330;
float huePurp = 270;
float hueTurq = 180;
float hueLime = 90;

size (1000, 1000);
background (255);
colorMode(HSB, 360, 100, 100);

for(int y = 0; y < 1000; y=y+1) {
    line(0,y,1000,y);
}
  
//Gradient Pink Purple
for (float i = start; i < end; i++) {
  stroke(map(i, start, end, huePink, huePurp), 100, 100);
  line(i, 0, i, 10);
}

//Gradient Turq Lime
for (float i = start; i < end; i++) {
  stroke(map(i,start, end, hueTurq, hueLime),100,100);
  line(i,10,i,20);
}

//Gradient Purp Turq
for (float i = start; i < end; i++) {
  stroke(map(i,start, end, huePurp, hueTurq),100,100);
  line(i,20,i,30);
}

1 Like

Fair enough. Here are two examples of how you could do it using the lerp functions:

void setup()
{
  size(600, 600, P3D);
  frameRate(60);
}

void draw()
{
  background(0);

  color red = color(255, 0, 0);
  color blue = color(0, 0, 255);
  drawGradientLine(30, 30, 400, 300, red, blue);
  drawGradientLineP2D(130, 30, 500, 300, red, blue);
  
}

void drawGradientLineP2D(float x1, float y1, float x2, float y2, color color1, color color2) 
{
  int numSegs = 20;                         // how many segment shall we divide our line up into?
  
  beginShape(LINES);
  for(int i = 0; i < numSegs; i++)
  {
    float tA = i/float(numSegs);            // how far along the line are we? 0-1
    float tB = (i+1)/float(numSegs);        // how far along the line is the next one?
    
    // beginning of the segment
    float xA = lerp(x1, x2, tA);            // same as float xA = x1 + (x2 - x1) * tA;
    float yA = lerp(y1, y2, tA);            // same as float yA = y1 + (y2 - y1) * tA;
    
    // end of the segment
    float xB = lerp(x1, x2, tB);            // same as float xB = x1 + (x2 - x1) * tB;
    float yB = lerp(y1, y2, tB);            // same as float yB = y1 + (y2 - y1) * tB;
    
    stroke(lerpColor(color1, color2, tA));
    vertex(xA, yA);
    vertex(xB, yB);
  }
  endShape();
}

void drawGradientLine(float x1, float y1, float x2, float y2, color color1, color color2) 
{
  int numSegs = 20;                         // how many segment shall we divide our line up into?

  for(int i = 0; i < numSegs; i++)
  {
    float tA = i/float(numSegs);            // how far along the line are we? 0-1
    float tB = (i+1)/float(numSegs);        // how far along the line is the next one?
    
    // beginning of the segment
    float xA = lerp(x1, x2, tA);            // same as float xA = x1 + (x2 - x1) * tA;
    float yA = lerp(y1, y2, tA);            // same as float yA = y1 + (y2 - y1) * tA;
    
    // end of the segment
    float xB = lerp(x1, x2, tB);            // same as float xB = x1 + (x2 - x1) * tB;
    float yB = lerp(y1, y2, tB);            // same as float yB = y1 + (y2 - y1) * tB;
    
    stroke(lerpColor(color1, color2, tA));
    line(xA, yA, xB, yB);
  }
}
2 Likes

Oh, thank you so much! Turn it into a generic function made possible work with loops, but I would like to repeat this three different lines randomly and I don’t know how to do it. I have tried with if and else without success. Could you please help me?

void setup()
{
  size(1000, 1000, P3D);
  frameRate(60);
  background (255);
}

void draw()
{
  
  strokeWeight(10);
  color red = color(255, 0, 0);
  color blue = color(0, 0, 255);
  drawGradientLine1(0, 25, 1000, 25, red, blue);
  color yellow = color(255,255,0);
  color lime = color(0,255,0);
  drawGradientLine2(0,75,1000,75,yellow,lime);
  color pink = color(255,0,255);
  color cyan = color(0,255,255);
  drawGradientLine3(0,125,1000,125,pink,cyan);
   
  for(int y = 0; y < 300; y=y+1) {
   drawGradientLine1(0,y,1000,y,red, blue);
   }
  for(int y = 300; y < 600; y=y+1) {
   drawGradientLine2(0,y,1000,y,yellow, lime);
   }
  for(int y = 600; y < 1000; y=y+1) {
   drawGradientLine3(0,y,1000,y,pink,cyan);
   }

  
}


void drawGradientLine1(float x1, float y1, float x2, float y2, color color1, color color2) 
{
  int numSegs = 300;                         // how many segment shall we divide our line up into?

  for(int i = 0; i < numSegs; i++)
  {
    float tA = i/float(numSegs);            // how far along the line are we? 0-1
    float tB = (i+1)/float(numSegs);        // how far along the line is the next one?
    
    // beginning of the segment
    float xA = lerp(x1, x2, tA);            // same as float xA = x1 + (x2 - x1) * tA;
    float yA = lerp(y1, y2, tA);            // same as float yA = y1 + (y2 - y1) * tA;
    
    // end of the segment
    float xB = lerp(x1, x2, tB);            // same as float xB = x1 + (x2 - x1) * tB;
    float yB = lerp(y1, y2, tB);            // same as float yB = y1 + (y2 - y1) * tB;
    
    stroke(lerpColor(color1, color2, tA));
    line(xA, yA, xB, yB);
  }
}

void drawGradientLine2(float x1, float y1, float x2, float y2, color color1, color color2) 
{
  int numSegs = 300;                         // how many segment shall we divide our line up into?

  for(int i = 0; i < numSegs; i++)
  {
    float tA = i/float(numSegs);            // how far along the line are we? 0-1
    float tB = (i+1)/float(numSegs);        // how far along the line is the next one?
    
    // beginning of the segment
    float xA = lerp(x1, x2, tA);            // same as float xA = x1 + (x2 - x1) * tA;
    float yA = lerp(y1, y2, tA);            // same as float yA = y1 + (y2 - y1) * tA;
    
    // end of the segment
    float xB = lerp(x1, x2, tB);            // same as float xB = x1 + (x2 - x1) * tB;
    float yB = lerp(y1, y2, tB);            // same as float yB = y1 + (y2 - y1) * tB;
    
    stroke(lerpColor(color1, color2, tA));
    line(xA, yA, xB, yB);
  }
}

void drawGradientLine3(float x1, float y1, float x2, float y2, color color1, color color2) 
{
  int numSegs = 300;                         // how many segment shall we divide our line up into?

  for(int i = 0; i < numSegs; i++)
  {
    float tA = i/float(numSegs);            // how far along the line are we? 0-1
    float tB = (i+1)/float(numSegs);        // how far along the line is the next one?
    
    // beginning of the segment
    float xA = lerp(x1, x2, tA);            // same as float xA = x1 + (x2 - x1) * tA;
    float yA = lerp(y1, y2, tA);            // same as float yA = y1 + (y2 - y1) * tA;
    
    // end of the segment
    float xB = lerp(x1, x2, tB);            // same as float xB = x1 + (x2 - x1) * tB;
    float yB = lerp(y1, y2, tB);            // same as float yB = y1 + (y2 - y1) * tB;
    
    stroke(lerpColor(color1, color2, tA));
    line(xA, yA, xB, yB);
  }
}

Ha! You don’t have to make a new function every time! The beauty of using functions is that you have to only write the code once, and then can just call the function with different arguments. I just made two different versions of the same function - the drawGradientLineP2D version was a faster, more performant version, but requires you to run the sketch using the P2D or P3D renderer: size(1000, 1000, P2D). The other one doesn’t, so you can use the standard renderer, but is a bit slower; size(1000, 1000). P3D you only use if you want to work in 3D, which we don’t do here yet.

void setup()
{
  size(600, 600);
  frameRate(60);
  
  pixelDensity(displayDensity());
  smooth(4);
}

void draw()
{
  background(0);

  // you can do it this way, 
  for(int i = 0; i < 10; i++)
  {
    drawGradientLine(400 + i*10, 100 + i*4, 500, 100 + i*4, color(255, 0, 0), color(0, 0, 255)); 
    // but I find it a bit confusing
  }
  
  
  // instead I like to make temporary variables that make it easier to see what's going on
  int numLines = 40;
  for(int i = 0; i < numLines; i++)
  {
    color startColor = color(i/float(numLines)*255, 255, 0);
    color endColor = color(0, 0, 255);
    
    float xPos1 = 100;
    float yPos1 = 100 + i*10;
    
    float xPos2 = 100 + i*10;
    float yPos2 = 500;
    
    drawGradientLine(xPos1, yPos1, xPos2, yPos2, startColor, endColor);
  }
  
  // and random lines you'd do like this
  for(int i = 0; i < 10; i++)
  {
    color startColor = color(255);
    color endColor = color(random(255), random(255), random(255));
    
    float xPos1 = width/2;
    float yPos1 = height/2;
    
    float xPos2 = width/2 + random(-50, 50);
    float yPos2 = height/2 + random(-50, 50);
    
    drawGradientLine(xPos1, yPos1, xPos2, yPos2, startColor, endColor);
  }
  
}



void drawGradientLine(float x1, float y1, float x2, float y2, color color1, color color2) 
{
  int numSegs = 80;                         // how many segment shall we divide our line up into?

  for(int i = 0; i < numSegs; i++)
  {
    float tA = i/float(numSegs);            // how far along the line are we? 0-1
    float tB = (i+1)/float(numSegs);        // how far along the line is the next one?
    
    // beginning of the segment
    float xA = lerp(x1, x2, tA);            // same as float xA = x1 + (x2 - x1) * tA;
    float yA = lerp(y1, y2, tA);            // same as float yA = y1 + (y2 - y1) * tA;
    
    // end of the segment
    float xB = lerp(x1, x2, tB);            // same as float xB = x1 + (x2 - x1) * tB;
    float yB = lerp(y1, y2, tB);            // same as float yB = y1 + (y2 - y1) * tB;
    
    stroke(lerpColor(color1, color2, tA));
    line(xA, yA, xB, yB);
  }
}

void drawGradientLineP2D(float x1, float y1, float x2, float y2, color color1, color color2) 
{
  int numSegs = 80;                         // how many segment shall we divide our line up into?
  
  beginShape(LINES);
  for(int i = 0; i < numSegs; i++)
  {
    float tA = i/float(numSegs);            // how far along the line are we? 0-1
    float tB = (i+1)/float(numSegs);        // how far along the line is the next one?
    
    // beginning of the segment
    float xA = lerp(x1, x2, tA);            // same as float xA = x1 + (x2 - x1) * tA;
    float yA = lerp(y1, y2, tA);            // same as float yA = y1 + (y2 - y1) * tA;
    
    // end of the segment
    float xB = lerp(x1, x2, tB);            // same as float xB = x1 + (x2 - x1) * tB;
    float yB = lerp(y1, y2, tB);            // same as float yB = y1 + (y2 - y1) * tB;
    
    stroke(lerpColor(color1, color2, tA));
    vertex(xA, yA);
    vertex(xB, yB);
  }
  endShape();
}

Thank you so much for patiente with me, rapatski. As you can see, I am very beginner, so I use to do things in a really primitive way :desktop_computer: :monkey:

My first idea was this kinds of random lines across the screen, where I can control the probability of every stroke:

void setup(){
  size(1000,1000);
  background (255);
  
  
    
    strokeWeight(10);
  for(int y = 0; y < 1000; y=y+10) {
     
    line(0,y,1000,y);
      if (random(1)>0.6) {
      stroke(255,105,180);
      }
      else if (random(1)>0.8){
      stroke(255,215,0);
      }
      else if(random(1)>0.7){
      stroke(255,140,255);
      }
      else{
      stroke(135,206,250);
      }
}
}

Then, I wanted to do gradient strokes for lines , so I try to do gradient horizontal lines, that I could do with your help, and I tried to mix both codes. But I did not think that maybe what I really needed was gradient strokes for the lines, if that is possible. What do you think that would be the best way to do it?

I have tried to make two loops for lines and gradients, but variables depend on each others and I have no control of every part

size(1000,1000);
int strokeWeight = 3;
 for(int i = 0; i< 1000; i++) { 
   for(int c = 0; c<300; c++) { 
     stroke(c, i, c); 
     line(i, 0, i, 1000); } 
     }

I’m not quite sure what you want to achieve… is this it?

void setup() {
  size(1000, 1000);
  background (255);

  strokeWeight(10);
  for (int y = 0; y < 1000; y=y+10) {

    color c1, c2;
    if (random(1)>0.6) {
      c1 = color(255, 105, 180);
      c2 = color(255, 255, 255);
    } else if (random(1)>0.8) {
      c1 = color(255, 215, 0);
      c2 = color(0, 0, 0);
    } else if (random(1)>0.7) {
      c1 = color(255, 140, 255);
      c2 = color(255, 255, 255);
    } else {
      c1 = color(135, 206, 250);
      c2 = color(0, 0, 0);
    }
    
    drawGradientLine(0, y, 1000, y, c1, c2);
  }
}

void drawGradientLine(float x1, float y1, float x2, float y2, color color1, color color2) 
{
  int numSegs = 80;                         // how many segment shall we divide our line up into?

  for(int i = 0; i < numSegs; i++)
  {
    float tA = i/float(numSegs);            // how far along the line are we? 0-1
    float tB = (i+1)/float(numSegs);        // how far along the line is the next one?
    
    // beginning of the segment
    float xA = lerp(x1, x2, tA);            // same as float xA = x1 + (x2 - x1) * tA;
    float yA = lerp(y1, y2, tA);            // same as float yA = y1 + (y2 - y1) * tA;
    
    // end of the segment
    float xB = lerp(x1, x2, tB);            // same as float xB = x1 + (x2 - x1) * tB;
    float yB = lerp(y1, y2, tB);            // same as float yB = y1 + (y2 - y1) * tB;
    
    stroke(lerpColor(color1, color2, tA));
    line(xA, yA, xB, yB);
  }
}

That is exactly what I was looking for! Thank you so much for your help!!!

Yesterday I did something similar before seeing your message and I discovered a really interesting effect, but I have no idea of how I did it. Could you please tell me how to control something like this on your clean code?

  void setup(){
  size(1000, 1000);
  
  background(255);
  strokeWeight(1);

  color a = color (0);
  color b = color (0);
  for(int y = 0; y < 1000; y=y+1) {

      if (random(1)>0.6) {
      a = a+30;
      b = b+60;
      }
      else if (random(1)>0.8){
      a = a+90;
      b = b+120;
      }
      else if(random(1)>0.7){
      a = a+150;
      b = b+180;
      }
      else{
      a = a+210;
      b = b+240;
      }
      
      drawGradientLine(0,y,1000,y,a,b);
}
}

void drawGradientLine(float x1, float y1, float x2, float y2, color color1, color color2) 
{
  int numSegs = 300;                      // how many segment shall we divide our line up into?

  for(int i = 0; i < numSegs; i++)
  {
    float tA = i/float(numSegs);            // how far along the line are we? 0-1
    float tB = (i+1)/float(numSegs);        // how far along the line is the next one?
    
    // beginning of the segment
    float xA = lerp(x1, x2, tA);            // same as float xA = x1 + (x2 - x1) * tA;
    float yA = lerp(y1, y2, tA);            // same as float yA = y1 + (y2 - y1) * tA;
    
    // end of the segment
    float xB = lerp(x1, x2, tB);            // same as float xB = x1 + (x2 - x1) * tB;
    float yB = lerp(y1, y2, tB);            // same as float yB = y1 + (y2 - y1) * tB;
    
    stroke(lerpColor(color1, color2, tA));
    line(xA, yA, xB, yB);
  }
}

Ah I love it! You’ve accidentally dipped into some black magic!

The Processing color datatype is actually stored as an int(eger), using a particular logic to store the RGB values of the color in a single number. By adding to that number, you’re sort of ‘scanning’ the color datatype, which explains why you see these blue and green gradients as you increment through the number. Try println() on some color variables, and you’ll see some very strange numbers pop up.

Try this for a good laugh, and you’ll see how the colours are actually stored:

void setup()
{
  size(4096, 4096);
  
  color col = color(0);
  
  loadPixels();
  for(int i = 0; i < pixels.length; i++)
  {
    pixels[i] = col;
    col += 1;
  }
  updatePixels();
  
  saveFrame("color.png");
}  

So glitchy, I love dark magic!