Skip to main content
Skip table of contents

Your First Application

This guide walks you through building a functional 3D configurator. While the Main Page provides an overview, here we focus on the implementation logic: connecting standard HTML UI elements to the ShapeDiver API.

1. The HTML Structure

To create a configurator, you need two things: a Viewport container for the 3D scene and a UI Layer for user inputs.

HTML
<div id="canvas-container" style="width: 100%; height: 500px; position: relative">
  <canvas id="canvas"></canvas>
</div>

<div id="controls" style="padding: 20px; border: 1px solid #eee; margin-top: 10px">
  <div style="margin-bottom: 15px">
    <label>Model Width: </label>
    <input type="range" id="width-slider" />
  </div>

  <button id="download-btn">Download Object</button>
  
  <div id="output-container" style="margin-top: 20px">
    <h3>Output:</h3>
    <pre
      id="output-content"
      style="
        height: 200px;
        overflow: scroll;
      "
    ></pre>
  </div>
</div>

2. The Initialization Script

We initialize the viewer and store the session object. We then pass this session to a dedicated function that handles our UI logic. Further details and options for sessions and viewports can be found on the corresponding linked pages.

To adjust this code for your own model, you first need to retrieve the ticket and adjust the embedding settings. For now you can also just try it out with the provided ticket of the example model.

TYPESCRIPT
import { createSession, createViewport, ISessionApi } from "@shapediver/viewer";

const init = async () => {
  // create the viewport and connect it to the canvas
  const viewport = await createViewport({
    canvas: document.getElementById("canvas") as HTMLCanvasElement,
    id: "myViewport",
  });

  // create a session with your ticket and modelViewUrl
  // in this case we use one of our example models
  const session = await createSession({
    ticket: "50eb2a26ddaa432ca18288b8a120ef194fa35bb813e4f43ae89d657991a865f9deaa20a1c840e47cdf6dbc019cd16ae15a9a6b3a7d91722455299d6bd29b1f26b3ff3b7adaac1df3d50f3ba4d010a560180dff8f745c946dadb41167a3431e223d69b32743f167-5b9465f92a0cf9c235b8ea315aab0cd5",
    modelViewUrl: "https://sdr7euc1.eu-central-1.shapediver.com",
    id: "mySession",
  });

  // once the session is ready, bind it to our UI elements
  bindParameterUI(session);
  bindExportUI(session);
};

init();

3. Connecting UI to API

This is the core of your application. We use event listeners to capture user input and translate those actions into API calls.

A. Updating Parameters

When the slider moves, we use update the parameter object and call session.customize to send a customization request.

TYPESCRIPT
const bindParameterUI = (session: ISessionApi) => {
  const slider = document.getElementById("width-slider") as HTMLInputElement;
  // fetch the parameter object by it's name that was given in GrassHopper
  const parameterApi = session.getParameterByName("Length")[0];

  // adjust the slider according to the parameter definition
  slider.min = parameterApi.min + "";
  slider.max = parameterApi.max + "";
  slider.value = parameterApi.value + "";
  slider.step = "1";

  slider.addEventListener("change", async () => {
    // Update the parameter and customize the session
    const newValue = parseInt(slider.value);
    parameterApi.value = newValue;
    await session.customize();
    console.log(`Updated width to: ${newValue}`);
  });
};

Note: In this example we show how a specific number parameter is used, here is an advanced example that handles all parameter types.

B. Requesting Exports

Unlike parameters, Exports are triggered by a single action (like a button click) to generate a file on the server.

TYPESCRIPT
const bindExportUI = (session: ISessionApi) => {
  const downloadBtn = document.getElementById(
    "download-btn",
  ) as HTMLButtonElement;
  // Fetch the export object by its name that was given in GrassHopper
  const exportApi = session.getExportByName("Obj Export")[0];

  downloadBtn.addEventListener("click", async () => {
    // 1. Request the computation and file generation
    const result = await exportApi.request();

    // 2. Handle the result (the download link)
    const fileUrl = result.content![0].href;
    window.open(fileUrl, "_blank");
  });
};

C. Reading Outputs

Outputs can be used to retrieve data directly after a computation. This can be additional information like prices or information about the model. In the

TYPESCRIPT

const bindOutputUI = (session: ISessionApi) => {
  // Fetch the output parameter by its name that was given in GrassHopper
  const outputApi = session.getOutputByName("Image Plane Box")[0];

  // define a callback function that will be called whenever the output data is updated
  const updateCallback = (newNode?: ITreeNode) => {
    const outputApiData = newNode?.data.find(
      (d) => d instanceof OutputApiData,
	) as OutputApiData;
	// get the new content
	const content = outputApiData!.api.content![0].data;
	// display the content as plain text
	const pre = document.getElementById("output-content") as HTMLPreElement;
	pre.textContent = JSON.stringify(content, null, 2);
  };

  // display initial content
  updateCallback(outputApi.node);
  // set callback for future updates
  outputApi.updateCallback = updateCallback;
};

🚀 Live Implementation

The following embed shows this exact code in a live environment. Adjust the slider to see the real-time parameter update, and click the button to see how the export request is processed.

Hint: Clicking Click to Run opens a standalone environment with a live preview and direct links to the CodeSandbox project and GitHub source code.


Next Steps

Now that you've built your first interactive app, dive deeper into the core components:

JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.