Search code examples
javascriptmultithreadingp5.jsweb-worker

"Uncaught ReferenceError: window is not defined" p5.js web worker


I have a javascript code where I use the web worker with the p5.js library. it wouldn't allow me to use any of p5's functions so I have to use the importScripts("p5.js") function to import the p5.js library before using any of p5's functions.

onmessage = (e)=>{
    importScripts("p5.min.js")
    // other scripts
}

But even then it gives me another error that said "Uncaught ReferenceError: window is not defined". I tracked it down and it seemed that p5 is unable to use the global variable named "window". I searched around the internet for a solution but so far found none. I wonder if there is a way around this. Thank you.


Solution

  • The issue here is that web workers run in a very isolated context where many of the standard global variables that would exist for javascript running on a website (window, document, etc) don't exist, and unfortunately p5.js cannot load without these variables. You could try shimming them with fake versions. Here's a basic example:

    let loadHandlers = [];
    
    window = {
      performance: performance,
      document: {
        hasFocus: () => true,
        createElementNS: (ns, elem) => {
          console.warn(`p5.js tryied to created a DOM element '${ns}:${elem}`);
          // Web Workers don't have a DOM
          return {};
        }
      },
      screen: {},
      addEventListener: (e, handler) => {
        if (e === "load") {
          loadHandlers.push(handler);
        } else {
          console.warn(`p5.js tried to added an event listener for '${e}'`);
        }
      },
      removeEventListener: () => {},
      location: {
        href: "about:blank",
        origin: "null",
        protocol: "about:",
        host: "",
        hostname: "",
        port: "",
        pathname: "blank",
        search: "",
        hash: ""
      }
    };
    
    document = window.document;
    screen = window.screen;
    
    // Without a setup function p5.js will not declare global functions
    window.setup = () => {
      window.noCanvas();
      window.noLoop();
    };
    
    importScripts("/p5.js");
    
    // Initialize p5.js
    for (const handler of loadHandlers) {
      handler();
    }
    
    postMessage({ color: "green" });
    
    onmessage = msg => {
      if (msg.data === "getRandomColor") {
        // p5.js places all of its global declarations on window
        postMessage({
          color: window.random([
            "red",
            "limegreen",
            "blue",
            "magenta",
            "yellow",
            "cyan"
          ])
        });
      }
    };
    

    This is only going to work for a limited subset of p5.js functions. Any functions that draw to the canvas are definitely not going to work. And I would be cautious about trying to pass objects back and forth (i.e. p5.Vector, p5.Color, etc) because everything sent via postMessage gets serialized and deserialized.

    I've posted a working version of this example on Glitch.