How do I avoid "blending" or "bleeding" of edges of solidly-colored shapes?

Hi all

Whenever I draw a solid shape of one color, wherever it has an edge meeting up with a different color, it will have a different color along the edge. Not the color of the shape, not the color that is outside the shape, but a mix, maybe?

Here’s a demo for what I’m talking about.

I thought initially that the problem was only for non-vertical and non-horizontal edges, but it seems that all edges are affected.

Initially, you’ll note the diagonal lines in adjoining triangles. These are the most obvious “undesired” lines, and can be made better (but not fixed entirely) by adding a stroke value. I think the issue here is that there is a tiny gap between the triangles that shows the background. This is odd enough in and of itself, since the triangles share coordinates.

However, even along the horizontal and vertical lines, we have odd blending going on. Here is a zoomed screenshot to show what I mean:
(I would show more, but I’m a new user and not allowed :frowning: )

The above image is from when I zoom in the browser. If I save the image to png (using built-in browser capabilities), the horizontal and vertical bleeds seem to disappear, but the diagonal remain.

Does anyone know details of why these artifacts appear, and if they’re avoidable? I also tried using noSmooth(), and it made no difference.

Hi @sleepingsun. I believe this is a matter of resolution. You can get around it by making the canvas larger or calling pixelDensity(2) or both.


Hello @sleepingsun !
Are you loading an image? Or are the shapes code based?
Can you copy and paste your code so we can see? I’m sure it’s an easy fix.

Thanks @sableRaph , I’ll experiment with pixelDensity! Are there rules of thumb as to performance issues when increasing pixelDensity? Or maybe concerns as to overruling pixelDensities of user devices?

1 Like

Hello @debxyz , thank you for chiming in!

I linked to a sketch with my code above, but here’s the code again. The shapes are made with calls to, in this sketch, triangle - but it could also be rect, square, circle or even vertice-based shapes (as it is in another sketch I’m working on).

const get_random_int = (rng_num, min, max) => {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(rng_num * (max - min) + min); //The maximum is exclusive and the minimum is inclusive
const get_random_from_arr = (rng_num, arr) => arr[get_random_int(rng_num,0,arr.length)];

function setup() {
  createCanvas(400, 400);


function draw() {
    const palette = [[241,54,86],[236,83,82],[224,76,26],[204,43,9],[178,96,77]];
    const background_color = [100,100,100];

    const offset = round( width / 10 );
    const num_cells = 5;
    const cell_side = round((width - 2 * offset) / num_cells);
    for(let i = 0; i < num_cells; i++) {
        for(let u = 0; u < num_cells; u++) {
            const left = offset + i * cell_side;
            const right = left + cell_side;
            const top = offset + u * cell_side;
            const bottom = top + cell_side;
            const color1 = get_random_from_arr(random(), palette);
            const color2 = get_random_from_arr(random(), palette);
            triangle(left, top, right, top, right, bottom);
            triangle(left, top, right, bottom, left, bottom);

1 Like

You are correct to assume that performance will be impacted by calling pixelDensity(). Like with screen resolution the number of pixel that needs to be rendered grows exponentially with higher pixel density.

If your canvas can be resized (like to be viewed full screen for example) you will need to render even more pixels.

If you are performing any per-pixel operations (via shaders or loadPixels() for example) these will also massively impact performance when increasing pixel density.

You could check the pixel density of the display with displayDensity and use that to set the appropriate density for a user’s display.

Since user devices may vary wildly in performance, there is no way to pick settings that will work for sure on all possible devices. One hacky thing you could do is have your sketch test its own framerate and lower the pixel density if it is running too slow.

1 Like

My guess is that it has to do with the inner workings of Javascript’s HTML5 Canvas API, rather than P5 itself. If you observe the source code, it seems like P5 uses this API for its rendering:

// starting at line 740
p5.Renderer2D.prototype.triangle = function(args) {

  // ... part left out for clarity

  ctx.moveTo(x1, y1);
  ctx.lineTo(x2, y2);
  ctx.lineTo(x3, y3);
  if (doFill) {
  if (doStroke) {

When zooming in on the result at Mozilla’s documentation on the Canvas API (which is a Canvas element), I see a similar ‘anti-alias’ effect at the edges of the green rectangle. So I think you have to redirect your search in the Javascript/HTML5 direction to find a solution to your issue.

Thank you for your detailed reply, @sableRaph :hugs: These are things I’ve considered myself, but I haven’t had a very firm grip on them. Your reply is inspiring me to research and implement a more solid strategy, more thoroughly. My hope is to have a small handful of techniques that I can apply easily; I find these kind of optimizations somewhat tedious in face of the more interesting work of the visual art that they enable :sweat_smile: So, thank you for the information and inspiration!

Thank you @Tiemen ! This is very interesting! This is, in a way, perhaps the answer I’m looking for; it’s an implementation detail, perhaps unavoidable. The essence is, it might be avoidable somehow, but at least now I know that I’m not doing anything silly that I shouldn’t be. Now, to research further :hugs:

1 Like