skip to Main Content

I want to display a stream of images in a browser component.

So far I’ve tried something like

function fetchData() {
  fetch("data.png?")
     .then((response) => response.blob())
     .then((blob) => {
            var ctx = canvas.getContext("2d");
            var width = canvas.width;
            var height = canvas.height
            
            myimage = new Image();
            myimage.onload = function() {
                    ctx.drawImage(myimage, 0, 0,width,height);
                    }
        const imageObjectURL = URL.createObjectURL(blob);
        myimage.src = imageObjectURL;           
       }

...
setInterval(fetchData, 50);

This sort of works. But if I make the setInterval period too short I start to get ERR_INSUFFICIENT_ RESOURCES error. I guess this is because fetch requests or responses pile up somewhere.

Ideally the drawing of a fetched image would trigger the next fetch. However I’m worried that sometimes no drawing will take place or a fetch might not complete in which case the process would stop.

Further I would like to have a few fetches in-flight to maximize performance and they should be completed in order, but without excessive buffering as the fetched data is interactive controlled from the browser so it is not like I would want to buffer a ten seconds worth of data.

So I’m looking for a general strategy implement above, I’m sure many people have worked on things like this before but I’ve not found examples.

2

Answers


  1. You have load event to use

    You can even add to error, but I would give up after x amount of retries

    const ctx = canvas.getContext("2d");
    const width = canvas.width;
    const height = canvas.height;
    
    const fetchData = () => {
        fetch("data.png?")
          .then((response) => response.blob())
          .then((blob) => {
              myimage = new Image();
              myimage.onload = () => {
                ctx.drawImage(myimage, 0, 0, width, height);
                setTimeout(fetchData, 200); // 50 is silly
              }
              myimage.onerror = () => {
                setTimeout(fetchData, 200); 
              }
              const imageObjectURL = URL.createObjectURL(blob);
              myimage.src = imageObjectURL;
            }
          }
    
    Login or Signup to reply.
    • setInterval isn’t appropriate because it doesn’t count the fetch and image load time.

    • To get max FPS you should call fetchData immediately after the drawing.

    • I suggest to draw with requestAnimationFrame to avoid possible image render tearing.

    • You should have some stopped flag to be able to stop the infinite cycle.

    • Also you should avoid requesting the context for each renders, it’s expensive.

    • Also you could use more ES6+ features and avoid creating intermediate 1 line variables:

    // get ctx outside
    const ctx = canvas.getContext("2d");
    let stopped = false;
    
    const delay = (delay = 0) => new Promise(r => setTimeout(r, delay));
    
    async function fetchData() {
      
      const r = await fetch("data.png?");
      
      if(stopped) return;
         
      const [width, height] = canvas;
      const myimage = new Image();
    
      myimage.onload = () => {
        requestAnimationFrame(async () => {
          ctx.drawImage(myimage, 0, 0,width,height);
          
          await delay(50); // if you don't need a delay and need max FPS, remove that and async
          
          fetchData();
        }
      };
      myimage.src = URL.createObjectURL(await r.blob());           
    }
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search