# Loading, rendering then saving the canvas for GLTF models frequently

**URL:** <https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867>\
**Category:** Questions\
**Tags:** javascript, gltf\
**Created:** [February 23, 2024, 6:49pm UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867 "2024-02-23T18:49:32Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![callum](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/callum/32/2446_2.png) [@callum](https://discourse.threejs.org/u/callum)\
**Post date:** [February 23, 2024, 6:49pm UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/1 "2024-02-23T18:49:32Z")

</div>

I am trying to create thumbnail images for my application by rendering component GLTF files then saving off the canvas for later processing, eventually ending up as a small, linked thumbnail. Ultimately there will be a lot of these - many hundreds so I am hoping to find an automated solution.

_ **Note: this will try to save many images to the download folder of your browser although Chrome at least, will ask you first if you want to allow it.** _

> **[Edit fiddle - JSFiddle - Code Playground](https://jsfiddle.net/6kjut5y9/1/)**
>
> Test your JavaScript, CSS, HTML or CoffeeScript online with JSFiddle code editor.

Example code there of the sort of thing I am trying to do - even though I am try to save 50 different images, there are always less than that present in the download folder upon completion.

I imagine it’s because the browser cannot keep up, or perhaps one download starts before the previous one completes, I tried various ways of adding short delay into the loop with some success but it feels like the wrong approach.

Ideally, I would like to observe the download event and wait for it to complete before starting the next but I don’t think that’s possible.

Has anyone worked on something like this before?

---

<div class="post-metadata">

**Author:** ![donmccurdy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/donmccurdy/32/33209_2.png) [@donmccurdy](https://discourse.threejs.org/u/donmccurdy)\
**Post date:** [February 23, 2024, 6:59pm UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/2 "2024-02-23T18:59:05Z")

</div>

Putting an async callback inside a synchronous loop, with each callback sharing access to the scene and canvas, will cause trouble.

My suggestion would be to start by getting this to work with just a solid scene background color (not a model!) rendered each time, and confirm the browser is giving you all the downloads correctly and that the general approach works.

Once that’s working, use something like this to block the loop while waiting for the next model to load:

```js
for (let i = 0; i < count; i++) {
  const gltf = await loader.loadAsync('scene.glb');
  // ...
}

```

And finally once that’s working, you _might_ consider trying to do all of the models in parallel, or in small parallel batches, depending on the number and their complexity.

---

<div class="post-metadata">

**Author:** ![callum](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/callum/32/2446_2.png) [@callum](https://discourse.threejs.org/u/callum)\
**Post date:** [February 23, 2024, 8:52pm UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/3 "2024-02-23T20:52:56Z")

</div>

Interesting and much appreciated.

I posted here because everyone is helpful but I was worried it was unrelated to Three.js and just a fundamental misunderstanding of JavaScript / browsers on my part. Sounds like that’s not the case.

Lots to try there this evening - thank you!

---

<div class="post-metadata">

**Author:** ![donmccurdy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/donmccurdy/32/33209_2.png) [@donmccurdy](https://discourse.threejs.org/u/donmccurdy)\
**Post date:** [February 23, 2024, 9:35pm UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/4 "2024-02-23T21:35:44Z")

</div>

One part of the problem is a general JavaScript thing. This code …

```js
  const total = 50;
  for (let count = 0; count < total; ++count) {
    setTimeout(function () {
      // ...
    }, 10);
  }

```

… is just a bit dangerous because anything inside that callback could be called in any order, and if code inside the callback is async, then two callbacks’ execution could overlap. Mostly just easier to reason about async/await if possible.

How well the browser will handle high numbers of downloads I’m not sure, and would be curious how that part works out! Worst case I suppose you might need to use JSZip or something like that to put all the images into a ZIP and trigger one download at the end.

---

<div class="post-metadata">

**Author:** ![callum](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/callum/32/2446_2.png) [@callum](https://discourse.threejs.org/u/callum)\
**Post date:** [February 23, 2024, 10:05pm UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/5 "2024-02-23T22:05:59Z")

</div>

The ZIP approach is a good idea and may be necessary. I changed the code to load everything via an array of loader promises and then, used `Promise.all(...)`. When everything was resolved, I ran the loop to render and save.

Same issue was present sadly and even when the loop did nothing but call `render` then `save`, there were lots of missed downloaded.

This worked a couple of years ago on a much older version of Chrome so perhaps some browser internals have changed since then.

I’m not familiar with JSZip but I expect there are plenty of examples out there.

Thank you for your insight.

---

<div class="post-metadata">

**Author:** ![didi\_softwares](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/didi_softwares/32/22686_2.png) [@didi\_softwares](https://discourse.threejs.org/u/didi_softwares)\
**Post date:** [February 23, 2024, 11:21pm UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/6 "2024-02-23T23:21:32Z")

</div>

if it is because it is not in sync with the loading of the models.

```javascript
createScene(value, execute) {
        var loadingdObjt = 0;        
        function icreateObject(name, iobject) {
            loadingdObjt += 1;                        
            LoadGLBPbject(name, (object) => {                
                iobject(object);
                loadingdObjt -= 1;
                console.log('Wainting for',loadingdObjt);
                if (loadingdObjt <= 0) execute();
            });
        }	
	if(value==1){
	icreateObject('xxx1.glb');
	icreateObject('xxx2.glb');
	icreateObject('xxx3.glb');
	...
	}
}

createScene(1,()=>{
 //All Objects of scene 1Loaded
});

```

---

<div class="post-metadata">

**Author:** ![callum](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/callum/32/2446_2.png) [@callum](https://discourse.threejs.org/u/callum)\
**Post date:** [February 24, 2024, 12:33am UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/7 "2024-02-24T00:33:48Z")

</div>

I’m sorry but I don’t understand any of that.

The canvas is saved once the model is loaded so I think they are in sync?

---

<div class="post-metadata">

**Author:** ![callum](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/callum/32/2446_2.png) [@callum](https://discourse.threejs.org/u/callum)\
**Post date:** [February 24, 2024, 12:37am UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/8 "2024-02-24T00:37:19Z")

</div>

> Worst case I suppose you might need to use JSZip or something

> **[Edit fiddle - JSFiddle - Code Playground](https://jsfiddle.net/s18k4uhz/)**
>
> Test your JavaScript, CSS, HTML or CoffeeScript online with JSFiddle code editor.

I made a version that adds each canvas image to a zip and then saves the zip at the end but the zip is empty. Strangely, it’s empty locally the first time I run it too but if I run it again without reloading the page, the next zip to be created does indeed contain the images.

Probably something silly…

---

<div class="post-metadata">

**Author:** ![manthrax](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/manthrax/32/2596_2.png) [@manthrax](https://discourse.threejs.org/u/manthrax)\
**Post date:** [February 24, 2024, 1:54am UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/9 "2024-02-24T01:54:58Z")

</div>

You can use the media API to record the canvas as a video.

---

<div class="post-metadata">

**Author:** ![callum](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/callum/32/2446_2.png) [@callum](https://discourse.threejs.org/u/callum)\
**Post date:** [February 24, 2024, 1:58am UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/10 "2024-02-24T01:58:04Z")

</div>

I thought about that but I need the files as images - always possible to convert the video to frames I guess - worst case.

---

<div class="post-metadata">

**Author:** ![callum](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/callum/32/2446_2.png) [@callum](https://discourse.threejs.org/u/callum)\
**Post date:** [February 24, 2024, 2:03am UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/11 "2024-02-24T02:03:51Z")

</div>

> **[Edit fiddle - JSFiddle - Code Playground](https://jsfiddle.net/b9apjzh5/)**
>
> Test your JavaScript, CSS, HTML or CoffeeScript online with JSFiddle code editor.

I got it working - the `canvas.toBlob()` call that then pushed the file to the zip wasn’t completing for each until after the zip file was being created and saved.

Adding another array of promises for the `canvas.toBlob()` operations and then waiting for them all to resolve before adding each file to the zip and saving it seemed to work but is deeply unsatisfying…

(Afterthought: Not least of which because I need to carry along the filename to save in the real application (I use an index in this Fiddle) and I don’t know of a way to compose an array of promises that also contain additional data like filenames etc. with each)

I know this (newer) Promise/resolve paradigm is supposed to be supposed to be more straightforward but I can’t get my little brain around it…

---

<div class="post-metadata">

**Author:** ![manthrax](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/manthrax/32/2596_2.png) [@manthrax](https://discourse.threejs.org/u/manthrax)\
**Post date:** [February 24, 2024, 3:59am UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/12 "2024-02-24T03:59:58Z")

</div>

you can use the loadAsync method on the loader to make it look more imperative.

```javascript
async function generateFrames(){

for(let i=0;i<100;i++){

   let glb = await gltfLoader.loadAsync("mygltf.glb");
   //glb is fully loaded here.. do something with it...
}

}

```

---

<div class="post-metadata">

**Author:** ![manthrax](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/manthrax/32/2596_2.png) [@manthrax](https://discourse.threejs.org/u/manthrax)\
**Post date:** [February 24, 2024, 4:04am UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/13 "2024-02-24T04:04:37Z")

</div>

Promises aren’t more straightforward… it’s just a way to allow/take advantage of multithreading in the browser. While you’re waiting on a promise to resolve, you can do other stuff.

You can also use async / await to explicitly wait for a promisified function to resolve.

```javascript
  function test( ){
      return new Promise( (resolve,reject)=>{
              setTimeout(()=>{
                     console.log("Waited one second!");
                     resolve()
              }, 1000);
  })

await test();
console.log("Promise finally finished!");

will print:

"waited one second!"
"promise finally finished!"
```

---

<div class="post-metadata">

**Author:** ![manthrax](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/manthrax/32/2596_2.png) [@manthrax](https://discourse.threejs.org/u/manthrax)\
**Post date:** [February 24, 2024, 4:13am UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/14 "2024-02-24T04:13:25Z")

</div>

Here’s a synchronous version of your thing that “awaits” each load before downloading.

> **[Edit fiddle - JSFiddle - Code Playground](https://jsfiddle.net/manthrax/9f6eyzva/4/)**
>
> Test your JavaScript, CSS, HTML or CoffeeScript online with JSFiddle code editor.

---

<div class="post-metadata">

**Author:** ![callum](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/callum/32/2446_2.png) [@callum](https://discourse.threejs.org/u/callum)\
**Post date:** [February 24, 2024, 1:31pm UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/15 "2024-02-24T13:31:49Z")

</div>

Thank you for the insight for for me aty least, that fiddle behaves the same way - downloads somewhere between 25-30 images only each time. Perhaps it’s browser specific ? I am using the latest Chrome on macOS.

---

<div class="post-metadata">

**Author:** ![manthrax](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/manthrax/32/2596_2.png) [@manthrax](https://discourse.threejs.org/u/manthrax)\
**Post date:** [February 25, 2024, 1:43am UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/16 "2024-02-25T01:43:41Z")

</div>

Hmm ok perhaps I didn’t understand your issue.  
I see it reporting linearly:

- “Saving #”, 0
- “Saving #”, 1  
through 49. in the console.  
and I see 50 images in my downloads folder.  
I’m using latest chrome on windows. I didn’t explicitly make the download itself synchronous in that fiddle, perhaps that’s the issue you’re talking about?

To make it synchronous you might have to switch to using XMLHTTPRequest per this SO post:

> <https://stackoverflow.com/questions/6346021/how-do-i-know-when-download-from-a-url-is-completed>

Then you could wrap that in a Promise as well.

---

<div class="post-metadata">

**Author:** ![callum](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/callum/32/2446_2.png) [@callum](https://discourse.threejs.org/u/callum)\
**Post date:** [February 25, 2024, 3:17pm UTC](https://discourse.threejs.org/t/loading-rendering-then-saving-the-canvas-for-gltf-models-frequently/61867/17 "2024-02-25T15:17:25Z")

</div>

Maybe it’s a macOS thing then - I see the output in the console but number of imagers in download folder varies between 20-30 each time I run it.

I was able to make the JSZip approach work well so I think that ends up being a better solution.

Thanks for all the help.
