Search code examples
p5.js

how to avoid multiple p5.js sketches to run all at once


I have a web page which encloses a few sketches, all written in P5.JS

Each sketch uses its own name space, so that it runs independently from the others.

I noticed that, for each sketch, the level of performance is lower than the one I get when it runs alone in a separate web page.

My question : what can I do to prevent all the sketches to run all at once ? Is it possible, for example, to activate a sketch only when the mouse hovers its canvas ? It would probably spare ressources.

Thank you for your help.


Solution

  • You can call noLoop() and loop() to stop and restart a sketch. There aren't any built in p5.js events to help you trigger noLoop() when the mouse leaves the sketch or when the sketch is scrolled off screen, however there are a couple of ways you can do it which rely on using the underlying browser functionality:

    1. The built in mouseenter and mouseleave events
    2. Checking winMouseX and winMouseY against the sketch canvas getBoundingClientRect() in each call to draw()

    function makeSketch(...colorArgs) {
      return (p) => {
        let bgColor;
        let black;
        let c;
        p.setup = () => {
          c = p.createCanvas(p.windowWidth, p.windowHeight / 3);
          bgColor = p.color(...colorArgs);
          black = p.color(0);
          
          c.elt.addEventListener('mouseenter', () => {
            p.loop();
          });
          c.elt.addEventListener('mouseleave', () => {
            p.noLoop();
          });
          
          let bounds = c.elt.getBoundingClientRect();
          
          // Just in case the mouse is already over the canvas when it is created.
          // This is also how you would use getBoundingClientRect from the draw()
          // and mouseMoved() functions instead of the mouseenter/mouseleave events.
          if (p.winMouseX < bounds.left ||
            p.winMouseX > bounds.right ||
            p.minMouseY < bounds.top ||
            p.winMouseY > bounds.bottom) {
            
            p.noLoop();
          }
        };
        
        p.draw = () => {
          p.background(p.lerpColor(
            bgColor,
            black,
            p.abs((p.frameCount % 240 - 120) / 120)
          ));
          
          
          let bounds = c.elt.getBoundingClientRect();
          p.fill('white');
          p.noStroke();
          p.text(`${p.winMouseX}, ${p.winMouseY} :: ${bounds.left}, ${bounds.top}, ${bounds.right}, ${bounds.bottom}`, 10, 10); 
        }
      };
    }
    
    let sketch1 = new p5(makeSketch('red'));
    let sketch2 = new p5(makeSketch(0, 255, 0));
    let sketch3 = new p5(makeSketch('blue'));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

    You might also find that it is sufficient to pause sketches that are off screen:

    function makeSketch(...colorArgs) {
      return (p) => {
        let bgColor;
        let black;
        let c;
        let isLooping;
        p.setup = () => {
          c = p.createCanvas(p.windowWidth, p.windowHeight);
          bgColor = p.color(...colorArgs);
          black = p.color(0);
          
          let bounds = c.elt.getBoundingClientRect();
          
          isLooping = true;
          if (bounds.bottom < 0 ||
              bounds.top > p.windowHeight) {
            
            p.noLoop();
            isLooping = false;
          }
          
          // Might need to check this on resize as well.
          document.addEventListener('scroll', () => {
            let bounds = c.elt.getBoundingClientRect();
            // Note this only checks verticle scrolling, but you could check horizontal as well
            if (bounds.bottom > 0 &&
                bounds.top <= p.windowHeight) {
    
              if (!isLooping) {
                isLooping = true;
                console.log(`sketch ${colorArgs.join(',')}: loop`);
                p.loop();
              }
            } else if (isLooping) {
              isLooping = false;
              console.log(`sketch ${colorArgs.join(',')}: noLoop`);
              p.noLoop();
            }
          });
        };
        
        p.draw = () => {
          p.background(p.lerpColor(
            bgColor,
            black,
            p.abs((p.frameCount % 240 - 120) / 120)
          ));
          
          p.fill('white');
          p.noStroke();
          p.text(`${p.frameCount}`, 10, 10);
        }
      };
    }
    
    let sketch1 = new p5(makeSketch('red'));
    let sketch2 = new p5(makeSketch(0, 255, 0));
    let sketch3 = new p5(makeSketch('blue'));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>