Clirio Scan Share

Scan Share is a feature of the Clirio product suite, for sharing photogrammetry scans quickly, and displaying them in a webviewer.


Unity App Feature + Website



Skills Applied

  • UX / UI
  • Game Development
  • Three.js
  • Front-End
  • Back-End




The Clirio Scan Share feature lets users quickly share a photogrammetry scan captured with the Clirio Scan app. The feature is comprised of share options in the Clirio View app developed in Unity and a webviewer developed in Blazor and Three.js, through which shared scans can be viewed. Pressing the share button on any of the Clirio View apps will open a pop-up to configure the share. The user has the option to set expiry, password protection, and to require that the viewer has been invited to the workspace, in which case the recipient will need to log in with their Clirio account to view the scan.

Once the user is satisfied with their selected options, they can generate the link, and either copy it to the clipboard or use the share function to trigger the share options native to each platform. For instance, on iOS this will open the share dialogue with compatible apps such as messengers, while on Windows the mail app will open. There is also an option for users to configure and generate a formatted embed iframe element with the share.

The webviewer through which recipients can view the scan is intended to be as lightweight and accessible as possible. The scan can be viewed from different angles, some metadata and a scale legend is displayed, and there are some buttons to hide or show elements.

An interactive example of an embedded scan share. Click and drag to look at the model from different angles. The same share can also be opened on its page.


There were two stories for the development of this feature: the clientside app share UI and logic, and the webviewer.

Share UI and Logic

Developed with C# in Unity, as a pop-up accessible through the observation details view of any scan in the Clirio View app. UI was designed in Figma in collaboration with Jordan Wischmann.


Developed with Blazor and Three.js. Backend token sharing was implemented by Timothy Thibault.

Share UI
Embed UI

Share UI and Logic

The share UI has gone through multiple iterations. Initially developed for MRTK2 with few options, the feature was later expanded to include expiry, password protection, and embed functionalities. Finally, in 2023, the UI was refreshed in collaboration with Jordan Wischmann, who designed mockups in Figma, as the Clirio View app was migrated to MRTK3.

When a user opens the share panel for a scan, the client checks whether there are existing links. If there are, the existing links are provided for the user to share. For any new configuration of a share that hasn't been created yet, the user can press generate, and a request will be sent to a backend API implemented by Timothy Thibault. The returned result includes a URL for the client to display to the user, and to include in native share options for each platform.

The biggest challenge for me was designing the UX, as there were a lot of options for the user. This included the option to make shares private so that users could restrict sharing internally to teams in workspaces, which required an additional authentication flow in the webviewer as well. The range of options also made it more difficult to design a compact UI. I utilized dynamic design and layouts so that hidden options such as expiry renewal or password change could be elegantly hidden or shown. Developing the logic was a fairly easy process, thinking about how the feature should work in the first place was the bigger challenge. After every user interaction, the UI refreshes to match the share configuration and all other states. If there is a share matching the configuration, that existing link is shown, otherwise the generate share button is shown.

An interesting change to my approach to writing controllers was first tested in this feature, after a discussion I had with my teammate Tonio Stillman. In the past, I would write public functions for button clicks, and hook them up in the inspector via the button's OnClick() event. Here, I instead set all the event listeners in the controller itself, kept all the corresponding listener functions private, and simplified the inspector setup so that each button only gets assigned once to the controller, and no other reference is needed. This makes setup in the editor less error-prone and maintainable, as it requires less understanding of the code.

Share UX Walkthrough


Implementing the webviewer was the more complex story, as it was standalone and most functionality needed to be created from scratch. The viewer was written in C# with Blazor, JavaScript with Three.js, and uses Tailwind CSS for styling. I had worked with Three.js in the past and enjoyed using it again, and a lot of my work revolved around setting up the viewer correctly.

When the user opens a share link, the page sends a request to our backend API to gather information about the share, including whether it's public, and a list of SAS tokens for all the files required to render the scan. If the scan is private, the user is routed to a login page, otherwise, the scan begins to load its resources right away. The viewer is lightweight, and only provides some metadata, and options to reset the view and show/hide the grid and dimensions legend.

Challenges with the setup of Three.js included adding support for loading the texture files defined in the MTL file with the correct SAS tokens, ensuring different MTL file formats and material properties are supported and displayed as desired, and making the viewer dynamic so it supports a wide range of model sizes and shapes, on a wide range of devices.

Code Sample

The code snippet below contains the code related to loading the 3D Model inside the webviewer, using Three.js. It's a small section of the actual file, but it shows some of the solutions to challenges encountered, such as the modified process for loading the textures defined in the MTL file using SAS tokens, and functions to help configure the viewer based on the model dimensions.