Render sequence of objects consecutively and save each

I generate a series of complete objects using CSG and then render each one in sequence. I want to save the result of the render to a JPEG file, before moving onto the next one. I know how to make the object, how to render it and how to save off the three.js canvas.

It seems like it must be a simple problem but I cannot understand how to structure my code so I can build → render → save each one synchronously and be certain that each has rendered before I save it.

How can this be achieved?

Likely quite a lot of ways how you could structure that, depending on the rest of your code, ex.:

  1. Create separate Three.Scene for each object. Place object in the centre of it. (No need for multiple cameras, just one should be ok.)
  2. Rendering is synchronous, so now all you really need to do is:
scenes.forEach((scene) => {
  renderer.render(scene, camera);

  // NOTE Download canvas as JPG
  1. Here’s an example of how to download canvases.

If your models are dummy thicc and can’t be loaded all at once, you can use async functions to go one-by-one, instead of creating all scenes right away:

(async () => {
  for (let i = 0; i < modelUrls.length; i++) {
    const gltf = await (new GLTFLoader().loadAsync(modelUrls[i]));


     // NOTE Rendering is synchronous, you don't have to await it
    renderer.render(scene, camera);

    // NOTE Download canvas as JPG

If models contain loadable materials, be sure to use a LoadingManager - wrap it in Promise and await until all assets are loaded before rendering.

Thank you! I am sure I tried something similar but it looked like renderer.render(…) was asynchronous so I gave up.

That almost worked perfectly - the first model in the list doesn’t get moved to it’s final position though and I can’t see why. Must be some state set in the subsequent code that makes thing work the next time through the loop but I cannot see it.

I’ll keep trying and thanks again.

I worked around the bad first load/save by inserting a dummy at the start of the list and then discarding it…

However, it appears to run so far (~300 images in a few seconds) that the browser cannot keep up and it randomly misses about 20% of items. (different 20% each time). Maybe there is a way to slow this down and give the browser a chance to keep up.

Update: Adding a delay with code like this:

const sleep = (time) => {
    return new Promise((resolve) => setTimeout(resolve, time));

seemed to do the trick - only needed 25ms delay but now I get all items, every time. Kind of annoying though - that number probably depends on the speed of my computer.