How can I re-load images in p5.js once they've changed since runtime?

I have a python file outputting a .png file to a local directory once a second, and this p5.js Javascript file is being hosted from this directory on a server. My issue is in trying to use the loadImage() function to load this local .png -- if I do so, it'll only refer to the initial value of it, and not its new changed value. Even when using a callback function inside draw(), it is very buggy, and does not change. Here's the file:

let splot;

//...
//irrelevant classes
//...

function draw() {
  background(245);
  solOnePane.display();
  s1plot = loadImage("plot.png", imageLoaded);
}

function imageLoaded()
{
  console.log("Debug");
    image(s1plot,200,200);
}

The result is a super buggy and unchanging image file that only reflects the image file at the state of loading the site. Upon a refresh, the file is updated. So, how can I write code with loadImage() to load an image at the current moment?

Thanks so much.


Solution 1:

If the image is saved at a set internal externally, let's say every second, you could use setInterval() every 1100ms for example to reload the image:

var s1plot;

function setup(){
  //...init your things here
  //reload the image every 1.1s
  setInterval(loadImage, 1100, "plot.png", imageLoaded, imageLoadFailed);
}

function imageLoaded(loadedImage)
{
  s1plot = loadedImage;
  console.log("image loaded", s1plot);
}

function imageLoadFailed(event){
  console.warn("image load failed", event);
}

function draw() {
  background(245);
  if(s1plot){// try to display the image only when it's available
    image(s1plot, 200, 200);
  }
}

You could use draw() as well and change the frameRate() to so a new frame is rendered every second or so. Here's a modified version of the above using frameRate():

var s1plot;

function setup(){
  //...init your things here
  //set frameRate to 1 frame every 1.1s
  frameRate(1000 / 1100);
}

function imageLoaded(loadedImage)
{
  s1plot = loadedImage;
  console.log("image loaded", s1plot);
}

function imageLoadFailed(event){
  console.warn("image load failed", event);
}

function draw() {
  background(245);
  if(s1plot){// try to display the image only when it's available
    image(s1plot, 200, 200);
  }
  // reload the image
  loadImage("plot.png", imageLoaded, imageLoadFailed);
}

This may be simpler (sometimes setInterval's scope can get hairy), but it also means you're limited to slow render updates. You may want this if the p5 sketch if simply displaying an image, but not if for example you want to handle reactive user interaction as well.

The code above isn't tested, but hopefully illustrates the idea. It's worth double checking loadImage() reference when in doubt.

For example, these details are important:

The image may not be immediately available for rendering. If you want to ensure that the image is ready before doing anything with it, place the loadImage() call in preload(). You may also supply a callback function to handle the image when it's ready.

In your case the second option (supplying a callback function) applies, however you've forgotten to add the image argument to the image load callback.

This assumes there are no issues writing plot.png to disk with no errors and the timing is a bit of a hack.

In an ideal world you would have the first system which writes plot.png send a message to p5 when the image has been successuflly been written and is ready to be loaded. (If plot.png is for example generated by a python script using matplotlib or something similar you could use a server sent event or websocket library to which p5 can connect to and listen for the message to load the image)