Documentation

Overview

The Arcsine viewer represents an interactive 3D scene containing a model to view, select camera viewpoints to view the model from, and floating buttons to navigate to certain viewpoints. The viewer also handles a number of user input events to enable intuitive movement through this scene.

A key element of the viewer that distiguishes this technology from other 3D viewers is the notion of viewpoints. Each viewpoint defines a virtual camera position that perfectly aligns the camera's view of the model to a specific product photo. Selecting a viewpoint, through either interaction or code, will move the camera to view the object from the specified angle. Viewpoint data isn't loaded until it is viewed for the first time.

To use the viewer, an instance of it can be embedded directly through HTML, or through our Javascript SDK, with customization available in both methods.

Direct Embed

Include the SDK script:

<script src="https://cdn.arcsineimaging.com/scripts/2.4.0/sdk.bundle.js"></script>

A viewer can be embedded directly in HTML without any scripting.

<div arcsine-viewer="your-project-id"></div>

All elements with the arcsine-viewer attribute set will be automatically targeted by the SDK script and populated with a viewer instance. This example will load a model with the default configuration. The viewer will scale to the size of its container element so make sure this has a non-zero width and height.

Javascript Embed

Start by including the SDK script:

<script src="https://cdn.arcsineimaging.com/scripts/2.4.0/sdk.bundle.js"></script>

When the SDK loads, the Javascript API is accessible under window.arcsineImaging.

To initialize a viewer:

const viewer = window.arcsineImaging.createViewer(document.getElementById('viewer-container'), {
  id: 'your-project-id'
});

To load the model:

// Load is asynchronous and returns a Promise.
viewer.load().then(() => {
  console.log('loaded!');

  // Now you can control the viewer with API methods.
});

Alternatively, you can handle the model to load with an event listener:

viewer.addEventListener('viewer-ready', () => {
  console.log('loaded!');  
});

Configuration

Direct embeds can be customized with HTML attributes while Javascript embeds use an options hash. These two examples are equivalent.

<div
  id="viewer-container"
  arcsine-viewer="your-project-id"
  initial-viewpoint="1"
  navigation="none"
>
</div>
const viewer = window.arcsineImaging.createViewer(document.getElementById('viewer-container'), {
  id: 'your-project-id',
  initialViewpoint: 1,
  navigation: 'none'
});

This table provides an overview of viewer configuration options, their Javascript property name, corresponding HTML attribute, and default values.

Javascript HTML Description
id arcsine-viewer The ID of your project.

This configuration option is required
initialViewpoint initial-viewpoint The ID of the initial viewpoint.

Default: 0
initialAnimation initial-animation Type of animation to play when the viewer loads. Currently slideshow, turntable, scroll and none are supported. This should be used in combination with an animation configuration.

Default: none
autoLoad auto-load Whether the model should load automatically. If false the model won't load until the container element receives a click event. The placeholder image will be shown in its place.

Default: true
navigation navigation Type of viewpoint navigation bar to display. Currently thumbnails or none are supported.

Default: thumbnails
navigationOrder navigation-order Array of viewpoint IDs to include in the navigation bar in the order they appear from left to right. ex. [3, 1, 0, 5]

Default: Every viewpoint ordered by ID
expandable expandable Whether to include UI to expand the viewer to fill the browser window.

Default: true
backgroundOpacity background-opacity Background fill color overlay opacity. Valid range is [0.0, 1.0]

Default: 1.0
transitionDuration transition-duration Duration in milliseconds of viewpoint transitions initiated by user interaction.

Default: 1200
showViewpointTargets show-viewpoint-targets When to display detail viewpoint targets (the floating "plus icons"). One of always, never or on-zoom.

Default: on-zoom
captureScrollWheel capture-scroll-wheel Whether wheel events over the the viewer will zoom the model or scroll the page. This only applies to desktop. One of always, never or on-focus.

Default: always
placeholder placeholder URL of image to display before the model is active.

Default: Displays a scaled image of the initial-viewpoint for a seamless transition into the 3D view.
animations See animations documentation.
viewpointFraming See viewpoint framing documentation.

Animations

Animations make it possible to move the camera to different viewpoints without direct user interaction. Currently there are three kinds of animations: turntable, slideshow, and scroll, each described below.

Animations are configured through the animations property. The animation set to play once the viewer loads can be configured through the initialAnimation property. Once the user initializes a click or touch event on the viewer the animation will stop, and the model will become freely interactable.

Multiple animations can be configured and controlled at runtime through the viewer.stopAnimation() and viewer.startAnimation() API calls.

const viewer = window.arcsineImaging.createViewer(document.getElementById('viewer-container'), {
  id: 'your-project-id',
  initialAnimation: 'turntable',
  animations: {
    turntable: {
      // options
    },
    slideshow: {
      // options
    },
    scroll: {
      // options
    }
  }
});

Turntable

The turntable animation will rotate the model around the scene's vertical axis at a constant speed. This animation will only play if the current viewpoint is an outer type.

{
  turntable: {
    speed: 0.5,              // Speed of the rotation. Negative values are counter-clockwise, positive are clockwise.
                             // Valid range is [-1.0, 1.0].
                             // Default: 0.25

    startingViewpointId: 0,  // Viewpoint to transition to when starting the turntable animation.
                             // Default: Value configured in `initialViewpoint`
  }
}

Slideshow

The slideshow animation will cycle through a sequence of viewpoints and perform customizable 3D movements on each "slide". To explore all slideshow possibilities check out the animation designer.

{
  slideshow: {
    
    // Use these top-level properties to override defaults for every slide in the sequence.

    slideDuration: 1500,
    zoomDistance: 0.5,
    orbitAngle: 90,
    orbitDistance: 0.5,
    transitionDuration: 1000,

    repeat: false,                // Whether to restart the slideshow when it reaches the last viewpoint in the sequence.

    sequence: [
      {
        viewpointId: 0,           // Active viewpoint for this slide.
                                  // Required.

        slideDuration: 2000,      // Time in milliseconds representing how long to dwell on each viewpoint.
                                  // Default: 2000

        zoomDistance: 0.3,        // Defines how far the camera should dolly in while a slide is active. Valid range is [0, 1].
                                  // Default: 0.0

        orbitAngle: 270,          // Direction of orbit in degrees clockwise from up. Valid range is [-360, 360].
                                  // Default: 0

        orbitDistance: 0.25       // Defines how far the camera should orbit. Valid range is [0, 1].
                                  // Default: 0.0

        transitionDuration: 1500  // Duration in milliseconds for transition to the next viewpoint in the sequence.
                                  // Default: Transition duration of the viewer.
      }

      //
      // Additional steps in the slideshow sequence.
      //
    ]
  }
}

Scroll Experimental

This is an experimental feature and the API may be subject to change in future releases.

The scroll animation will cycle through viewpoints and perform interpolated 3D movements based on the user's scroll position. It is usually used with a viewer styled with position: sticky; pointer-events: none; so that animations stay within the viewport and aren't disabled by touch events while scrolling. The animation is active when the top of the passed in boundingElement is above the top of the browser's viewport.

<!-- (scroll-animation-bounds height) = (viewer height) + (sum of sequence heights) -->

<div id="scroll-animation-bounds" style="height: 2100px; position: relative;">
  <div id="viewer-root" style="top: 0; height: 600px; position: sticky;"></div>
</div>
{
  scroll: {
    boundingElement: document.getElementById('scroll-animation-bounds'),
    sequence: [
      {
        viewpointId: 1,           // Active viewpoint for this portion of the scroll animation.
                                  // Required.

        zoomDistance: 0.3,        // Defines how far the camera should dolly in while a slide is active. Valid range is [0, 1].
                                  // Default: 0.0

        orbitAngle: 270,          // Direction of orbit in degrees clockwise from up. Valid range is [-360, 360].
                                  // Default: 0

        orbitDistance: 0.25       // Defines how far the camera should orbit. Valid range is [0, 1].
                                  // Default: 0.0

        height: 500               // Vertical scroll distance in CSS pixels wherein this viewpoint will be shown.
                                  // Required.

      },
      {
        viewpointId: 2,
        height: 500
      },
      {
        viewpointId: 3,
        height: 500
      }
    ]
  }
}


Viewpoint Framing

Viewpoint framing allows for viewpoints to be repositioned to better fit alongside page content, while maintaining the size and position of the viewer. This provides tighter control over how 3D content fits on the page as the viewer animates. Field of view is used to set the zoom of the viewpoint and the X & Y shifts control the pan. This feature is only available when using a Javascript embed. The viewpointFraming property can be used to customize framing before the viewer loads. See the synchronized text example for a demo of how this can be useful.

const viewer = window.arcsineImaging.createViewer(document.getElementById('viewer-container'), {
  id: 'your-project-id',
  viewpointFraming: {
    0: {                // Viewpoint ID for which the framing values will apply.

      fov: 2.0,         // Zooms camera to 200% of the viewpoint's intrinsic field of view.
                        // Valid range [0.25, 2.5]
                        // Default: 1.0

      shiftX: 0.5,      // Shifts camera right by 50% of viewport width.
                        // Valid range [-1.0, 1.0]
                        // Default: 0.0

      shiftY: -0.1      // Shifts camera down by 10% of viewport height.
                        // Valid range [-1.0, 1.0]
                        // Default: 0.0
    },
    4: {                // Additional viewpoints can be configured
                        // Omitted viewpoints or properties will retain their default values.
      shiftX: 0.1
      shiftY: 0.4
    }
  }
});

Additionally, viewport framing can be globally configured at runtime with viewer.enableViewpointFraming() and viewer.disableViewpointFraming(), or configured at the viewpoint level with viewpoint.setFov(), viewpoint.setShiftX(), and viewpoint.setShiftY().

API

Global Object

Available through window.arcsineImaging after the initial script loads.


createViewer(container: HTMLElement, options: Object): ArcsineViewer

Initialize a viewer embed inside of the passed container. The viewer will scale to the size of the container.


viewers : [ArcsineViewer]

Array of initialized viewer objects.


isSupportedBrowser : Boolean

Whether the client’s browser contains every feature needed to display the viewer.

ArcsineViewer Instance


element: HTMLElement

Element containing this viewer.


load(): Promise ASYNC

Load the model’s assets. This is called automatically if autoLoad is set to true.
All API methods aside from addEventListener and removeEventListener can only be called after the returned Promise resolves.


getViewpointById(id: Number): Viewpoint

Returns the viewpoint with the passed ID or null if none match.


setViewpointById(id: Number): Promise ASYNC

Performs an animated transition to passed viewpoint ID. The returned promise resolves with true when the transition is completed.
If a transition is already playing the viewer will attempt to wait for its conclusion before transitioning to the passed ID.
If the transition fails to play (usually a result of quickly repeated calls to this method) the returned promise will resolve with false.


getCurrentViewpoint(): Viewpoint

Returns the current viewpoint being projected onto the model.


getViewpoints(): [Viewpoint]

Returns an array of available viewpoints for this model.


resize()

Resizes the underlying <canvas> to fit the viewer's parent element.


startAnimation(animationType: String, config: Object): Promise ASYNC

Begin playing the animation configured for the passed animation type. The returned promise resolves with true when the animation begins to play.
If an animation is already playing or no animation is configured for the passed type the promise resolves with false.
The optional second argument will override any animation setup during viewer initialization and allow you to change animations at runtime.


stopAnimation()

Stop any running animation.


enableViewpointFraming()

Enable viewpoint framing for all viewpoints. Viewpoint framing is enabled by default.


disableViewpointFraming()

Disable viewpoint framing for all viewpoints.


addEventListener(eventName: String, listener: Function)
removeEventListener(eventName: String, listener: Function)
Event Name Listener Argument Fired When
viewer-ready None Viewer is fully loaded and interactable. As a convenience, the arcsine-viewer-ready class will be added to the viewer's container element when this event is fired.
animation-started animationType : String Animation begins playing
animation-stopped animationType : String Animation stops playing
transition-started viewpointId : Number Animated transition to a viewpoint starts.
transition-finished viewpointId : Number Animated transition to a viewpoint finishes.
viewpoint-id-changed viewpointId : Number Viewpoint being projected onto the model changes.

Viewpoint Instance

id : Number
type : String

One of outer, detail or posed-outer.


Viewpoint Framing

Note that viewpoint framing will only be applied after you transition to the viewpoint so applying new framing to the current viewpoint will not take immediate effect.

setFov(scale: Number)

Scale the viewpoint's intrinsic field of view by the passed value. Valid range is [0.25, 2.5].


setShiftX(shiftX: Number)

Set the viewpoint's horizontal lens shift. Valid range is [-1.0, 1.0].


setShiftY(shiftY: Number)

Set viewpoint's vertical lens shift. Valid range is [-1.0, 1.0].


Examples

Transparent background

Some designs require the model to run overtop of existing content without the default opaque background. Here's how to achieve this effect.


        
<div id="example-viewer-one" style="width: 100%; height: 300px; margin: auto;"></div>

<script>
const exampleViewerOne = window.arcsineImaging.createViewer(document.getElementById('example-viewer-one'), {
  id: 'nike-cosmic-unityv1_5f5899bfbea0',

  // First we configure the viewer to discard the background fill.
  //
  backgroundOpacity: 0.0,

  // The default placeholder is a JPG which doesn't support transparency. In order to create a
  // seamless transition with the fully-loaded 3D view we need to create a custom PNG of our initial
  // viewpoint with the background cut out.
  //
  // Note the `?fit=scaled` parameter. This will tell the viewer to apply the same aspect-fitting & framing to 
  // your custom placeholder as it does to the default placeholder.
  //  
  placeholder: 'https://arcsineimaging.com/assets/initial-viewpoint-transparent-0fbb0a4dd5e0bf7334343474731ae2e8a871cb02282eca39e88c1cc7803bd773.png?fit=scaled',

  autoLoad: false,
  expandable: false,
  navigation: 'none'
});
</script>


  

Synchronize copy to model movements

Here we'll use viewpoint framing to create a more guided product storytelling experience. We'll also show how to toggle interactivity in the viewer.

Feature A

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Feature B

Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Feature C

Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.


        
<div id="example-viewer-two"></div>

<div data-viewpoint="0" class="active feature" style="left: 0; top: 0;">
  <h3>Feature A</h3>
  <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
</div>

<div data-viewpoint="7" class="feature" style="right: 0; top: 0;">
  <h3>Feature B</h3>
  <p>Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
</div>

<div data-viewpoint="5" class="feature" style="bottom: 20%; left:0;">
  <h3>Feature C</h3>
  <p>Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p>
</div>

<button data-viewpoint="0" class="feature-button">Feature A</button>
<button data-viewpoint="7" class="feature-button">Feature B</button>
<button data-viewpoint="5" class="feature-button">Feature C</button>
<button class="interactive-button">Enable Interactivity</button>

<style>
  /* 
   * The `pointer-events` property can be used to toggle between a guided & fully-interactive experience. 
   */
  #example-viewer-two {
    pointer-events: none;
    width: 100%;
    height: 600px;
    margin: auto;
  }

  #example-viewer-two.interactive {
    pointer-events: initial;
  }

  .feature {
    text-align: left;
    position: absolute;
    width: 35%;
    z-index: 1;
    margin: 20px;

    opacity: 0;
    pointer-events: none;
    transition: opacity 0.5s linear;
  }

  .feature.active {
    transition-delay: 0.6s;
    opacity: 1;
    pointer-events: initial;
  }
</style>

<script>

const exampleViewerTwo = window.arcsineImaging.createViewer(document.getElementById('example-viewer-two'), {
  id: 'nike-cosmic-unityv1_5f5899bfbea0',
  initialViewpoint: 0,
  navigation: 'none',
  expandable: false,

  // Viewpoint framing is used here to position key angles alongside content on the page.
  //
  viewpointFraming: {
    0: {
      shiftX: 0.2,
      fov: 1.5
    },
    5: {
      shiftX: 0.15,
      fov: 0.6
    },
    7: {
      shiftX: -0.25,
      fov: 0.8
    }
  },

  // We'll configure an animation to play when the user chooses to make the viewer interactive.
  // Initially nothing is animated as no `initialAnimation` property is configured.
  // 
  animations: {
    turntable: {
      speed: -0.5,
      startingViewpointId: 3
    }
  }
});

exampleViewerTwo.load().then(() => {
  const featureButtons = document.querySelectorAll('.feature-button');
  const featureTextBlocks = document.querySelectorAll('.feature');
  const interactiveButton = document.querySelector('.interactive-button');

  let isInteractive = false;

  const setInteractivity = (value) => {
    if (isInteractive === value) { return; }

    featureTextBlocks.forEach((block) => {
      block.classList.remove('active');
    });

    exampleViewerTwo.element.classList.toggle('interactive', value);

    // Dynamically toggle between centered viewpoints and viewpoints shifted to fit next to text.
    //
    value ? exampleViewerTwo.disableViewpointFraming() : exampleViewerTwo.enableViewpointFraming();

    isInteractive = value;
  }

  const enableInteractivityClicked = () => {
    setInteractivity(true);
    exampleViewerTwo.startAnimation('turntable');
  };

  const showFeatureClicked = (event) => {
    // Use hardcoded HTML data attributes to match animated elements to 
    // buttons and viewpoints.
    //
    const viewpoint = parseInt(event.currentTarget.getAttribute('data-viewpoint'));

    // Ignore the event if the requested feature is already shown.
    //
    if (!isInteractive && viewpoint == exampleViewerTwo.getCurrentViewpoint().id)
    {
      return;
    }

    setInteractivity(false);
    exampleViewerTwo.setViewpointById(viewpoint);
  };

  // We could wait for `setViewpointById` to resolve in the above function to show the text blocks
  // but we'd have to wait for the entire transition to complete. (We'd also have to detect the case
  // where the transition fails to play).
  //
  // Binding to `transition-started` allows us to start the text fade-in right at the start of the 
  // transition & synchronize the movement a little better. 
  //
  exampleViewerTwo.addEventListener('transition-started', (id) => {
    if (!isInteractive)
    {
      featureTextBlocks.forEach((block) => {
        block.classList.remove('active');
      });

      const targetFeatureText = document.querySelector(`.feature[data-viewpoint="${id}"]`);
      targetFeatureText.classList.add('active');
    }
  });

  // Bind all button events.
  //
  featureButtons.forEach((button) => button.addEventListener('click', showFeatureClicked));
  interactiveButton.addEventListener('click', enableInteractivityClicked);
});

</script>

  

Dynamic custom buttons

In this example we'll dive deeper into the API to query model information at runtime. We'll iterate over the model's viewpoints and create buttons that control transitions & highlight the current viewpoint in a way that works independent of the project.


        
<div id="example-viewer-three" style="width: 100%; height: 400px; margin: auto;"></div>
<div id="button-container"></div>

<script>

// Initialize viewer that will wait for a load() call to fetch assets
//
const exampleViewerThree = window.arcsineImaging.createViewer(document.getElementById('example-viewer-three'), {
  id: 'nike-cosmic-unityv1_5f5899bfbea0',
  initialViewpoint: 0,
  navigation: 'none',
  expandable: false,
  autoLoad: false
});

const buttonContainer = document.getElementById('button-container');

const highlightViewpoint = (id) => {
  buttonContainer.querySelectorAll('button').forEach(el => {
    el.classList.remove('active');
  });

  const button = buttonContainer.querySelector('button[data-viewpoint="' + id + '"]');
  button.classList.add('active');
};


const createButton = (viewpoint) => {
  const button = document.createElement('button');

  // Fetch the viewpoint's associated button.
  button.innerHTML = viewpoint.id;
  button.setAttribute('data-viewpoint', viewpoint.id);

  // Animated transition to this viewpoint.
  button.addEventListener('click', () => {
    exampleViewerThree.setViewpointById(viewpoint.id);
  });

  buttonContainer.appendChild(button);
}

// Large web requests for textures/model begin here
//
exampleViewerThree.load().then(() => {
  const viewpoints = exampleViewerThree.getViewpoints();

  viewpoints.forEach(vp => {
    createButton(vp);
  });

  highlightViewpoint(exampleViewerThree.getCurrentViewpoint().id);

  // Bind to viewpoint-id-changed so highlighted button will update when user
  // interacts with the model.
  exampleViewerThree.addEventListener('viewpoint-id-changed', highlightViewpoint);
});

</script>

  

Additional Information

Supported Browsers

Unsupported browsers (IE11 & older) will show a simplified, non-3D experience. We query the browser's feature set to determine compatibility. You can use window.arcsineImaging.isSupportedBrowser before loading a viewer to implement special behavior in this case.

Previous Versions

Unless otherwise noted this documentation is valid for Arcsine Viewer SDK versions 2.X.X. See the the v1 docs for information on previous versions.

Copyright

All models and photographs appearing on this site are the property of Arcsine Imaging, LLC. They are protected by U.S. Copyright Laws, and are not to be downloaded or reproduced in any way without the written permission of Arcsine Imaging, LLC.