heading

p5 global (default) and instance mode

p5 is a javascript sketching library based on processing, check it out here. It is unfortunate that when you include p5 in your project (via cdn resource), it will create bunch of globals. There is a reason globals shall not be used when creating a library, we do not want name clashing (p5 has a globals like width and height, so you can’t use it for anything else). Fortunately there is an instance mode with which you can create a p5 sketch from a function containing a sketch instance that contains all the functions and variables that would be otherwise globals in the p5 documentation.

// ----------------------------------------------------
// default
function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(220);
}

// ----------------------------------------------------
// instance mode
const s = (sketch) => {
  sketch.setup = () => {
    sketch.createCanvas(400, 400);
  };

  sketch.draw = () => {
    sketch.background(220);
  };
};

let myp5 = new p5(s);

While following the p5 tutorials, I will be have to include sketch. before each function/variable/event handler mentioned. Example from https://p5js.org/get-started/ :

// ----------------------------------------------------
// example from https://p5js.org/get-started/
function setup() {
  createCanvas(400, 400);
}

function draw() {
  if (mouseIsPressed) {
    fill(0);
  } else {
    fill(255);
  }
  ellipse(mouseX, mouseY, 80, 80);
}

// ----------------------------------------------------
// converted to instance mode
const s = (sketch) => {
  sketch.setup = () => {
    sketch.createCanvas(400, 400);
  };

  sketch.draw = () => {
    if (sketch.mouseIsPressed) {
      sketch.fill(0);
    } else {
      sketch.fill(255);
    }
    sketch.ellipse(sketch.mouseX, sketch.mouseY, 80, 80);
  };
};

let myp5 = new p5(s);

Integration in React

If I want to include p5 in react app, I need to manage the instance and the canvas, which is not straightforward. This is what I came up with, I admit I had help from Svarog that works at Koji, see his work here https://withkoji.com/~Svarog1389/.

import { useEffect, useRef } from "react";
import p5 from "p5";

const s = (sketch) => {
  sketch.setup = () => {
    sketch.createCanvas(400, 400);
  };

  sketch.draw = () => {
    sketch.background(220);
  };
};

export default function Game() {
  let canvasContainerRef = useRef();
  let sketchRef = useRef();

  //mount
  useEffect(() => {
    sketchRef.current = new p5(s, canvasContainerRef.current);

    //unmount
    return () => {
      if (sketchRef.current) {
        sketchRef.current.remove();
        sketchRef.current = null;
      }
    };
  }, []);

  return <div ref={canvasContainerRef}></div>;
}

This react effect works with creating and cleaning up the canvas.

Breaking this down, we can see that outside the Game component we have a p5 sketch function for instance mode. Inside the Game component, we have two references - one for the p5 instance and the other for a DOM container for a canvas where the p5 sketch will draw. Unmounting the component will remove the sketch instance.

The result of running this component is a light gray 400x400 rectangle:

screenshot