Control the color of the shadow with a Processing filter(BLUR).

Mastering blur and shadow colors in Processing

In this post, I explore how to overcome a common frustration in Processing: the default black shadow produced by the filter(BLUR) function. Can we make it vibrant? Let's find out.

[Japanese version / 日本語版はこちら]

In my previous article, I experimented with stacking randomly blurred layers. Here is the drawn image:

Processing BLUR filter black shadow problem

While the effect was interesting, the "nuance" felt different compared to p5.js's drawingContext. p5.js feels light and airy, while Processing can feel... a bit heavy.

This is the reference image drawn by the p5.js drawingContext.

 

The Mystery of the Black Halo

Why do these blurs look so dark? After a helpful tip from @emeen231, I realized the culprit was hiding in plain sight:

So, where does this black "halo" come from? Looking at my code, a suspect emerged: the background initialization.


p.background(0.0, 0.0);

Here is the whole code.

Click to view the source code

/**
 * Stacking the random blur layers.
 * 
 * @author @deconbatch
 * @version 0.1
 * @license CC0
 * Processing 3.5.3
 * created 2022.09.23
 */

void setup(){

  size(640, 640);

  int layerNum  = 12; // number of layers

  background(0.0);
  for (int i = 0; i < layerNum; i++) {
    image(
          getLayer(
                   random(10.0) // blur randomly
                   ),
          0, 0);
  }

}

/**
 * getLayer : returns the blured layer.
 */
PGraphics getLayer(float _blur) {

  int cNum = 24;

  PGraphics p = createGraphics(width, height);
  p.beginDraw();
  p.background(0.0, 0.0);
  p.noStroke();
  p.fill(240.0);
  for (int i = 0; i < cNum; i++) {
    p.circle(
             random(width),
             random(height),
             random(60.0)
             );
  }
  p.filter(BLUR, _blur);
  p.endDraw();
  return p;

}


 

I used transparent background for stacking the layers. And the brightness of the background was zero which is black. Even with alpha set to zero, the RGB channels are still 0,0,0. When the blur filter spreads these pixels, that underlying black bleeds into the edges of your shapes.

Let's increase the brightness value on trial.


p.background(240.0, 0.0);

white shadow in filter(BLUR) effect

The white shadow! This shows that transparent doesn't mean empty.

 

Tinting the Atmosphere: Background Color as a Glow

What happens if we change the "transparent" color? I tried setting a blue but transparent background: p.background(64.0, 128.0, 255.0, 0.0);. The result? A clean, ethereal blue glow.


p.background(64.0, 128.0, 255.0, 0.0);

Blue shadow in filter(BLUR) effect

How about the pink background?


p.background(255.0, 128.0, 222.0, 0.0);

ping shadow in filter(BLUR) effect

It's clear that the background's RGB values dictate the tint of the blur.

Random background color on each layer produces the image like this.


  p.colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
  p.background(random(210.0, 360.0), 80.0, 90.0, 0.0);

Random color shadow in filter(BLUR) effect

 

Can I Change the Color of the Part of a Shadow?

Now I can control the shadow color as one in the whole canvas. Then, can I control the color in each part of the canvas?

Encountering Unexpected Artifacts

For example, I wrote the code below instead of p.background(). It separates the canvas into four parts that have a different colors.


  int div = 2;
  p.blendMode(REPLACE);
  p.noStroke();
  for (int x = 0; x < div; x++) {
    for (int y = 0; y < div; y++) {
      float rw = width / div;
      float rh = height / div;
      p.fill(
             (x * 60.0 + y * 150.0) % 360.0,
             80.0,
             90.0,
             0.0
             );
      p.rect(x * rw, y * rh, rw, rh);
    }
  }

p.blendMode(REPLACE); is the key. It draws a image like this.

Shadow color controlled with fill()

Yes! That's... No. Something is wrong.

You can see the left side of each rectangle has a black shadow. When I remove the transparency, the whole part of the rectangle has a color like this. So it is not the failure of painting colors.

fill() is working fine

You can see the problem well when I separate the canvas into 64 parts and set the circle color black.

fill() with no transparency
Black shadow appears left side of the rectangle

I'm afraid the color value does not affect to the left side of each rectangle.

 

The last resort.

I've not been able to resolve this problem. My last resort is to paint the rectangles with a transparent value of 1.0 instead of 0.0.

No black shadows
No black shadows

By using a nearly-invisible alpha (1.0), we "anchor" the intended color to the pixels without sacrificing perceived transparency.

 

Embracing the "Handmade" Glitches of Processing

I may disappoint a little if the program does not work as expected. But I love problems like this also. That's because I feel Processing's handmade taste, not the ready-made practical product.

In conclusion, I'll show you the code that draws colorful shadows.

Colorful glow effect using transparent background in Processing


/**
 * Random colorful shadows
 *
 * @author @deconbatch
 * @version 0.1
 * @license CC0
 * Processing 3.5.3
 * created 2022.10.08
 */

void setup(){

  size(640, 640);
  smooth();
 
  int layerNum  = 12; // number of the layers

  background(255.0);
  blendMode(SUBTRACT);
  for (int i = 0; i < layerNum; i++) {
    image(
          getLayer(
                   random(10.0) // random blur
                   ),
          0, 0);
  }
 
}

/**
 * getLayer : return _blur value blured layer
 */
PGraphics getLayer(float _blur) {

  PGraphics p = createGraphics(width, height);
  p.beginDraw();
  p.colorMode(HSB, 360.0, 100.0, 100.0, 100.0);

  // random color matrix
  int div = 8;
  p.blendMode(REPLACE);
  p.noStroke();
  for (int x = 0; x < div; x++) {
    for (int y = 0; y < div; y++) {
      int rw = round(width / div);
      int rh = round(height / div);
      int rx = x * rw;
      int ry = y * rh;
      p.fill(
             random(360.0),
             90.0,
             80.0,
             1.0
             );
      p.rect(rx, ry, rw, rh);
    }
  }

  // random located circles
  int cNum = 12;
  p.blendMode(BLEND);
  p.fill(0.0, 0.0, 0.0, 100.0);
  for (int i = 0; i < cNum; i++) {
    p.circle(
             random(width),
             random(height),
             pow(random(9.0), 2)
             );
  }
  p.filter(BLUR, _blur);
  p.endDraw();
  return p;

}

 

For reference.

My Processing version was 'Processing 3.5.3 on Linux'.

When I tried with the 'Processing 4.0.1 on Linux' the right side and the bottom of the rectangles had a black shadow.

Software isn't always perfect. Between Processing 3.5.3 and 4.0.1, the way filters handle edges seems to have shifted, creating new "glitches" or artifacts. While some might find this frustrating, I love it. It gives Processing a "handmade" feel—like working with physical materials that have their own quirks and personality.

 

Next Post Previous Post
No Comment
Add Comment
comment url