Zoom To Fit? (Embedded SD)

I’m using the ShapeDiver Web UI to embed a model on a web page.
I would like the model to show up as large as possible when it is loaded.

Can someone show me how to zoom to fit the model within the current window?


You just call api.scene.camera.zoomAsync(); to zoom to the extends. You can do it as a callback after updating a parameter like so:

api.parameters.updateAsync({name: "Couch Length (cm)", value: 210}).then(

It’s taken from the example here: https://support.shapediver.com/hc/en-us/articles/360014884512-First-steps-with-the-API

Unfortunately its still super hard to find out about specific things the API can do. Everything is spread around in different articles and across 2 different documentations. I really wish they had many more smaller examples for everything.

1 Like

Thank you. That is just what I needed.

I just googled how to do this in the new viewer API v3 and came across my own post. The documentation for the viewer API is not the best, especially in terms of examples, so in case anyone else comes across this, in the v3 API you do:

await camera.zoomTo();

Viewer 3 documentation overview:

Thanks for those resources. I saw those as well.

Is there any example how zoomTo works with zooming to a specific object in the scene using zoomTarget. It’s what I want to do, but I can’t see any examples for that.

What is an IBox? I guess its the bounding box of a geometry, but I can’t find anywhere how to get it and what format it is in.


@mathieu1 Do you have any small example code how zoomTo works if I want to zoom to a specific object using zoomTarget? In the documentation it just say it wants an IBox as input, which I assume is the bounding box of an object. But how do I get this and pass it to zoomTo?

Thanks for any hints!

Hello @seltzdesign,

in our viewer, the scene tree is built with nodes and every node has a bounding box as a property.

I created a new example for you, in which I show how to get to the bounding box of an output node and use it in the zoomTo function. Note that all nodes have a bounding box assigned, not only output nodes.

In the first part of the example, I use the zoomTo function.
Then I reset the position and in the second part I calculate a manual camera movement. As the zoomTo function always takes into account the current position when zooming to the object, you might zoom to the object with a certain angle (as in the example). With a manual camera movement you can be completely free in how your end position and target will be so that you can look at the object from a certain direction.

All the best,

Hi @MajorMeerkatThe3rd

Thanks for providing the example! I am trying the same as you, but it just zooms into 0 like the bounding box has size 0. The object itself does have the correct size though:

See here: https://recordit.co/k7RKSlAyyc

Not sure what is wrong in the code.

Hello @seltzdesign,

An easy explanation for this could be that you have two outputs with the same name. You can find the right output by looking at the format property. Was this the case here?

Cheers, Michael

Hi Michael.

There should always be just one geometry in the output “textObject”. In fact in the GH script I specifically check that it is always just a single geometry.

The 2 other outputs have different names.

Even though, since I specify getOutputByName("textObject")[0] shouldn’t it only take the first one regardless if it was an array?

Which format property do you mean? This one?

Hello @seltzdesign

when you use the ShapeDiver Display component in GH, two outputs are created, one for the material (format material) and one for the geometry (format glb). They both have the same name and when using the getOutputByName the first output could be the material output. Therefore my comment was just a suggestion if that is the case.

Have you debugged how the values that are sent into the zoomTo function? Is a correct bounding box sent as input there?

Cheers, Michael

Coming back to this I still cannot get it to work. I have named my 2 geometry outputs in Grasshopper. One is called “Object” the other is called “Background”.

I want to always auto-zoom to the object. In my code I have, as in the example by @MajorMeerkatThe3rd :

As you can see in the console output the output has a valid bounding box by the looks of it. Yet the camera still just zooms into 0 and not the object.

Once again the content format is “glb”, so that part should be correct.

Not sure how to proceed. I think it would be best if Shapediver could show a working example of that with the .gh file.

Hello @seltzdesign,

in the process of creating an example that mimics the behaviour of your model, I was able to reproduce your described issue for the first time. This seems to be an issue that only happens in a specific scenario and was therefore hard to track down. It will of course be fixed in the next version, but until then, the workaround for it is to add this line
await new Promise((resolve) => setTimeout(resolve, 0));
just before you call the camera.zoomTo function. With that, it should work as expected.

All the best,

Thanks for the reply @MajorMeerkatThe3rd

I added the line and now it works sometimes (?). I have a dropdown to switch the material of the object. If I cycle through the materials than it’s usually correct for around 2 out of the 7, for the other’s it still zooms into 0. But it’s not always the same ones. So it seems to me like there is still a chance this can fail.

Maybe let’s continue this debugging in private messages, so we don’t spam this thread too much. Once the solution is there we can update it here if necessary.

There will be a patch release for the viewer very soon, we will let you know.

Hello @seltzdesign

A patch release with that fix was released to 2.9.3. Please try it out and let us know if this now solves the issue in all of your cases.

Cheers, Michael

Hi @MajorMeerkatThe3rd

I gave it a go, but it does not solve it. I still have many cases, where it just zooms to 0.

Here is my code for the auto zoom:

    // Get all outputs by the name "Object"
    const textObjectOutputs = session.getOutputByName("Object");

    // Filter out outputs that don't have a 'Format' property or a 'node' with a 'boundingBox'
    const validOutputs = textObjectOutputs.filter((output: any) => 
      output.format && 
      output.format[0] === "glb"

    // Get the first valid output
    const boundingBoxOutput = validOutputs[0];

    if (boundingBoxOutput) {
      // Auto zoom camera to text object
      await new Promise((resolve) => setTimeout(resolve, 0));
      await camera.zoomTo(boundingBoxOutput.node!.boundingBox);
    } else {
      console.log("No valid bounding box found");

Checking the console I can see that now it is actually getting the geometry object every time, but still zooms to 0 in around 9/10 cases.

I really have no idea how to fix it at this point, since it works sometimes, but most of the time doesn’t.

We really need to have this fixed, because not having autoZoom is not really an option and zooming to the whole scene is also not an option.

Hello @seltzdesign,

as we cannot reproduce this issue on our side, please provide a minimal example in a CodeSandBox for us to be able to debug this. You can enable the domain for all CodeSandBoxes by adding *.csb.app to your domains on the platform. You can either send me a link to that CodeSandBox here or as a private message.

Cheers Michael

I will try to do that, but it sounds like a lot of work, since my HTML code is built in Webflow.

In the meantime can you create a Sandbox where you show that it IS working as expected for you?

In the older viewer API, the function would automatically zoom to extends if it didn’t find a valid bbox: ShapeDiver 3D Viewer Class: CameraApi

The paths of the scene which the camera should capture or a 3D box which should be encapsulated afterwards. If none are provided, the camera will zoom to the scene extents.

But here it clearly thinks there is a bbox, but the size of it is 0 at the time. But the output states that it is quite clearly not 0.

I don’t know why you made it so difficult with the 2 outputs. Shouldn’t it be something like:

await camera.zoomTo("Object", 500);

And it just works. Why make it so difficult and have 2 outputs in no certain order and we have to filter it ourselves?