Skip to content
On this page

Phone Showcase

Let's create a product showcase for a phone. A 3D product showcase should show the product in a compelling way. Because we're using 3D models, we can add interactivity. But let's not overdo it. We want to show the product, not show off 3D tech. The interactivity should be subtle and enhance the experience.

I will point to the relevant sections in the guide to explain the principles behind the code. If this is your first time working with the Sketchfab API, I recommend you start with the Getting started tutorial.

Features

This phone showcase has the following features:

  • A 3D model of a phone
  • Size options
  • Move model
  • Color options

The 3D model is based on a model from the Sketchfab store. It's been modified to make the materials easy to update. I also added a simple hierarchy to make showing and hiding the model easier.

Size options

The model contains two phones. A 6.1" model and a 6.7" model. We can switch between them by hiding and showing the right parts of the model.

Check out the Showing and hiding tutorial for the basiscs of this technique.

This part of the code searches the nodemap for the two objects I want to target: iPhone_15_Plus_v1_002 and iPhone_15_v1_002. I store the instanceID's in an object called scenenodes. Once I have the instanceID's, I can use them to show and hide the objects.

js
const scenenodes = {
  iPhone_15_Plus_v1_002: null,
  iPhone_15_v1_002: null,
};

api.addEventListener("viewerready", () => {
  api.getNodeMap(function (err, nodeMap) {
    Object.keys(scenenodes).forEach((key) => {
      const node = findNode(nodeMap, key);
      if (node) scenenodes[key] = node.instanceID;
    });
  });
});

api.show(scenenodes["iPhone_15_v1_002"]);
api.hide(scenenodes["iPhone_15_Plus_v1_002"]);

Moving the phones

Let's add some movement to the phones. When toggling between the phone sizes, we should put the active phone in the center of the screen. We can do this by moving the model. We haven't covered moving models yet in the guide. The process is quite straightforward. First we need to find the instanceID of the object we want to move, then we can move it. In our example, we want to move the root of the scene.

Look at Get the rootnode to learn how to get the instanceID of the root node. Read the Sketchfab docs about translating to learn how to move the model.

js
api.addEventListener("viewerready", () => {
  let rootID = null;
  api.getRootMatrixNode((err, instanceID) => {
    rootID = instanceID;
  });
  api.translate(rootID, [0.08, 0, 0], {
    duration: 1.0,
    easing: "easeInOutQuad",
  });
});

api.translate takes three arguments. The first is the instanceID of the object we want to move. The second is the position we want to move to. The third is an object with options. We can set the duration and easing of the movement. Easing is a way to control the speed and accelleration of the movement.

Camera constraints

To get an easy-to-understand experience, we should reduce the freedom users have navigating the scene. Only orbit the camera, don't allow zooming or panning. That helps to focus on the product. It also makes it easy to embed the product showcase on a website. The user can't get lost in the scene.

Here we disable panning, and set the camera target to the center of the phone. We also se the initial camera position to look at the center of the phone from the front. Read about camera constraints here: Camera constraints

js
const constraints = {
  usePanConstraints: true,
  target: [0, 0, 0.08],
};
api.setCameraConstraints(constraints, function (err) {
  api.setEnableCameraConstraints(true, {});
  api.setCameraLookAt([0, 0.5, 0.08], [0, 0, 0.08], 1, function (err) {});
});

We can block the scrollwheel, and effectively disable zooming, with the init parameter scrollwheel: 0. Read more about init parameters here: Initialization options

Colors

Setting the colors takes the most effort for two reasons: first we need to determine the right colors. This is not always straightforward with the metallic, plastic and glass materials. Secondly there are many materials that change color at the same time. We need to properly manage this.

Let's get the materials that we want to manipulate and store them in an object for easy access. The scenematerials object contains the names of the materials in the Sketchfab scene. Once the scene has loaded, we use getMaterialList to get the materials that we need. Read more about getting materials in Material List

js
const scenematerials = {
  "aluminium-color": null,
  "glass-clear-color": null,
  "glass-frosted-color": null,
  logo: null,
  screen: null,
  "metal-screws": null,
  "plastic-color": null,
  "plastic-color-edges": null,
  "body-back": null,
};
api.getMaterialList(function (err, materials) {
  materials.forEach((item) => {
    if (scenematerials[item.name] === null) scenematerials[item.name] = item;
  });
});

Now that we have the materials, we can change the colors. In most of the materials, we can change the AlbedoPBR color. On the screen we want to exchange the texture. I use several topics from the guide to make this happen:

To easily manage the colors, I've created an object containing the color values and texture URL's

js
const materialsettings = {
  pink: {
    main: [227, 200, 202],
    screen: {
      url: "https://assets.codepen.io/2407400/screen-pink.png",
    },
  },
  yellow: {
    main: [229, 224, 193],
    screen: {
      url: "https://assets.codepen.io/2407400/screen-yellow.png",
    },
  },
  green: {
    main: [202, 212, 197],
    screen: {
      url: "https://assets.codepen.io/2407400/screen-green.png",
    },
  },
  blue: {
    main: [206, 213, 217],
    screen: {
      url: "https://assets.codepen.io/2407400/screen-blue.png",
    },
  },
  black: {
    main: [53, 57, 59],
    screen: {
      url: "https://assets.codepen.io/2407400/screen-black.png",
    },
  },
};

Applying the colors is a matter of looping over the materials and setting the right color.

  • Gamma correct the color
  • Loop over the materials and set the color
  • Apply the materials to the scene
js
const colorname = "pink";
const maincolor = gammaCorrectRgb(materialsettings[colorname].main);

Object.keys(scenematerials).forEach((key) => {
  if (key !== "screen")
    scenematerials[key].channels.AlbedoPBR.color = maincolor;
});

Object.keys(scenematerials).forEach((key) => {
  api.setMaterial(scenematerials[key]);
});

Setting the texture is similar. We use a function to make sure we can reuse the same texture in case a user picks the same color again. This loadTexture uses a Promise instead of a callback for convenience. Learn more about promises on the MDN Web Docs.

js
const loadTexture = (api, textureData) => {
  return new Promise((resolve) => {
    if (textureData.uid === undefined) {
      api.addTexture(textureData.url, (err, uid) => {
        textureData.uid = uid;
        resolve(uid);
      });
    } else {
      resolve(textureData.uid);
    }
  });
};
const colorname = "pink";
const uid = await loadTexture(api, materialsettings[colorname].screen);

scenematerials["screen"].channels.EmitColor.texture = { uid };

Object.keys(scenematerials).forEach((key) => {
  api.setMaterial(scenematerials[key]);
});

Assembling the code

Now that we have all the elements, we can assemble it into a working example. The example at the top of the page contains nice styling with CSS. In the following CodePen example, I've left out the fancy CSS. This helps to focus on the code, and makes it a bit easier to reuse.

Conclusion

Creating a compelling product showcase does not need an awful lot of features. However, it does need a lot of attention to detail. It's important to make it easy to use, but also easy to scroll past. The interactivity should be subtle and enhance the experience.

The example on this page can be enhanced. I see the following opportunities:

  • Add a custom loading screen.
  • Enhance the camera movement, smoothly locking to preferred camera angles.
  • Make the materials and lighting look better! I'm not the best at materials and lighting. I'm sure you can do better.
  • Instead of showing and hiding a model, create a fancy fade-in and fade-out animation.

I'd love to see what you can do with this project. Feel free to share your results with me, or reach out if you have any questions.

Form