Blur filtered canvas on canvastexture doens't display well

I think the title says it all…
Does threejs have issues when handling blur-filtered html canvases?

<canvas id="myCanvas" width="800" height="800"></canvas>
<script type="importmap">
      "imports": {
        "three": "",
        "three/addons/": ""
<script type="module">
    import * as THREE from 'three';

    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    canvas.width = 800;
    canvas.height = 800;
    let angleRed = 0;
    let angleGreen = Math.PI / 4;
    let angleBlue = Math.PI / 2;
    let redSpeed = Math.random()*0.03;
    let greenSpeed = Math.random()*0.03;
    let blueSpeed = Math.random()*0.03;
    let motionRadius = 50;
    let proximity = 0;

    let renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true} );
    renderer.setSize(800, 800);
    document.body.appendChild( renderer.domElement );
    let scene = new THREE.Scene();
    let camera = new THREE.PerspectiveCamera( 45, 1, 0.1, 100 );
    camera.position.z = 1.3;
    let texture = new THREE.CanvasTexture(canvas);
    const material = new THREE.MeshBasicMaterial({ map: texture });
    const planeGeometry = new THREE.PlaneGeometry(1, 1);
    const mesh = new THREE.Mesh(planeGeometry, material);

    function draw() {
        ctx.clearRect(0, 0, canvas.width, canvas.height);

        // Draw black background
        ctx.fillStyle = 'black';
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        // Update circle positions
        const centerX = canvas.width / 2;
        const centerY = canvas.height / 2;
        const radius = 100;

        const redX = centerX + Math.cos(-angleRed) * motionRadius;
        const redY = centerY + Math.sin(angleRed) * motionRadius;

        const greenX = centerX + Math.cos(angleGreen) * motionRadius;
        const greenY = centerY + Math.sin(-angleGreen) * motionRadius;

        const blueX = centerX + Math.cos(angleBlue) * motionRadius;
        const blueY = centerY + Math.sin(angleBlue) * motionRadius;

        // Draw red circle
        ctx.fillStyle = '#34cceb';
        ctx.arc(redX, redY, radius, 0, Math.PI * 2);

        // Draw green circle
        ctx.fillStyle = '#343aeb';
        ctx.arc(greenX, greenY, radius, 0, Math.PI * 2);

        // Draw blue circle
        ctx.fillStyle = '#347deb';
        ctx.arc(blueX, blueY, radius, 0, Math.PI * 2);
        // Apply blur effect
        ctx.filter = `blur(100px)`;
        ctx.drawImage(canvas, 0, 0);
        ctx.filter = 'none'; // Reset filter

        // Increment angles for circular motion
        angleRed += redSpeed;
        angleGreen += greenSpeed;
        angleBlue += blueSpeed;

        texture.needsUpdate = true;
        renderer.render(scene, camera);



  1. It should render just fine the last time I tested it - could you share a screenshot of how it looks in three.js vs how it looks on pure canvas?

  2. Keep in mind canvas filters together with theee.js are absolutely brutal for performance - if you’d like to be blurring things often, consider FBO/RT + a shader instead.

Even without using THREE, a 100px blur filter is gonna be heavy. It’s worth trying to recreate the same effect with a shader instead of a CanvasTexture, using circle sdfs will let you get a blur effect fairly easily without having to compute it like a blur filter will.

Also, in your animation loop, don’t increment your angles with constant values as this will give you different results based on frame rate.
Instead of angleRed += redSpeed; , do soemthing like this

// in your setup
const clock = new THREE.Clock()
angleRed += redSpeed + clock.getDelta();




Hi, thank you for you input!

Below is a screenshot @mjurczyk asked for.

Left: pure canvas
Right: THREE.CanvasTexture

The color looks kind of off i guess. I don’t think it’s a resolution problem because the image looks pretty much the same without the blur filter.

(EDIT: Here’s a screenshot without the blur filter)

Both of you (@mjurczyk and @Arthur) mentioned blur filters being heavy, but the pure canvas version seems to run pretty smoothly (>60fps)… And from my (I admit, very noob level of) understanding, all THREE has to do is move the pixel data from the canvas to the canvastexture… but I guess in reality it’s more complicated than that…? If someone can explain in easy words so I can understand, I would be thankful.

I was hoping I didn’t have to get into shaders, but I guess I may have to do some more studying. :slight_smile:

Try to add this:
texture.colorSpace = "srgb";

1 Like

@prisoner849 you are the best! I’ve been trying so many things for 2~3 days now…

1 Like