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

Viewed 1199

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.

1 Answers

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.

Related