Augmented Reality
Dopple Visual allows 3D products to be viewed in augmented reality (AR) on a user’s device, directly within the web browser.
Before launching an AR experience, an AR scene needs to be generated by Dopple in the cloud, then presented to the user by one of two methods:
- Displaying a QR code for the user to scan, linking to the AR experience — ideal for when the user is on a device that does not support augmented reality.
- Showing a “Launch AR” button for the user to click — ideal for when a user is on an AR-supported device, such as a smartphone or tablet.
Note
AR scene generation may be time consuming and computationally heavy in certain cases. For the best user experience, it is strongly recommended to keep track of when users make changes to their product’s configuration so that new AR scenes and QR codes only need to be generated for new configurations, and not repeated for existing configurations.
See the tracking configuration changes section below for more info.
Generating the AR scene
To create an AR scene of the current product configuration, use the create
method available on Atlatl.ARScene
, passing in the Visual instance as its only parameter.
const renderCanvas = document.getElementById('my-canvas')
const visual = new Atlatl.Visual(renderCanvas)
const createArScene = async () => {
const arScene = await Atlatl.ARScene.create(visual)
}
This will begin the process of converting the user’s scene (handled for you in the cloud by Dopple) into an AR scene that can be launched within their web browser. Once the AR scene has finished generating, a QR code linking to the AR experience or a button to launch the experience directly may then be displayed to the user.
Creating and displaying the QR code
To generate a QR code, an HTML <img>
element needs to first be created in the DOM. This may be done either by adding an <img>
tag to your HTML document, or using JavaScript (for example, document.createElement('img')
).
<img id="qr-code-img" alt="QR code" />
This image’s src
attribute will be populated by the base64 data returned by the generateQRCode
method on the Atlatl.ARScene
.
const createQrCode = async () => {
// Generate a new AR scene with the current product configuration
const arScene = await Atlatl.ARScene.create(visual)
// Generate a PNG image of the QR code onto the page
const qrCodeData = 'data:image/png;base64,' + arScene.generateQRCode()
document.getElementById('qr-code-img').setAttribute("src", qrCodeData)
}
Launching AR
To launch the AR experience directly, call the launchAR
method on the Atlatl.ARScene
after the AR scene has been generated.
const launchAr = async () => {
// Generate a new AR scene with the current product configuration
const arScene = await Atlatl.ARScene.create(visual)
// Launch the AR experience
if (confirm('Warning! You are about to leave this page. To continue to the AR experience, select "OK".')) {
arScene.launchAR()
}
}
The launchAr()
function may then be triggered anytime after Dopple Visual has loaded, such as when the user clicks a <button>
on the webpage.
<button id="launch-ar">Launch AR</button>
document.getElementById('launch-ar').addEventListener('click', () => {
launchAr()
})
Note
On non-AR devices, such as a desktop computer, the AR experience will still be launched when launchAR
is called. However, the user will be taken to an AR preview page without the ability to use their device for AR.
Detecting AR-supported devices
If the user is currently on a device that supports AR, it may be a desirable user experience to display only the “Launch AR” button and not the QR code (or vice versa if the user is on a non-AR device).
One method is to use a regular expression to check the user agent of the browser for known mobile devices, since these devices will typically have AR capabilities.
const isMobileDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
Then, if isMobileDevice
returns true
, the launch button or the QR code image may be shown or hidden accordingly.
if (isMobileDevice) {
// Hide the QR code (show only the Launch AR button)
document.getElementById('qr-code-img').style.display = 'none'
} else {
// Hide the Launch AR button (show only the QR code)
document.getElementById('launch-ar').style.display = 'none'
}
caution
User agent detection can be unreliable. Depending on your unique needs for AR device detection, a more robust way of detecting a device’s AR capabilities may need to be built. See MDN’s browser detection using the user agent article for more information.
Showing a loader while AR generates
Since the AR scene creation process may take a few seconds, it is recommended to show a loading screen or indicator to the user while they wait.
A custom loading screen may be created by placing a “loading” message in the page’s HTML, then conditionally showing or hiding the message depending on the loading state of the AR scene.
<p id="ar-loading-screen">
Your AR experience is loading...
<span id="ar-loading-percent">0%</span>
<progress id="ar-progress-bar" max="100" value="0"></progress>
</p>
const renderCanvas = document.getElementById('my-canvas')
const visual = new Atlatl.Visual(renderCanvas)
const createArScene = async () => {
// Show the "loading" message as the AR scene generates
document.getElementById('ar-loader').style.display = ''
// Generate the AR scene
const arScene = await Atlatl.ARScene.create(visual,
onProgress(e) {
console.log(e.currentStep + '/' + e.totalSteps + ' (' + e.overallProgress * 100 + '%)')
}
)
// Hide the "loading" message once the AR scene has finished generating
document.getElementById('ar-loader').style.display = 'none'
}
Additionally, ARScene.create()
also accepts an onProgress()
callback function which gives access to the current step, total number of steps, and overall progress during the AR generation process.
const arScene = await Atlatl.ARScene.create(visual, {
// Get the current step, total number of steps, and overall progress of the AR scene generation
onProgress(e) {
console.log(e.currentStep)
console.log(e.totalSteps)
console.log(e.overallProgress * 100 + '%')
}
})
Tracking configuration changes
As mentioned earlier, AR scene generation is usually a time consuming process. Depending on the complexity of the product and the scene, this could take upwards of 10 or 20 seconds. To help mitigate this, avoid calling Atlatl.ARScene.create()
for repeat views of the same product configuration in AR.
One method is to wrap your Atlatl.ARScene.create()
functionality in a conditional statement that first checks to see if the product configuration has been changed since the last time it was called.
// Default to true so that a new AR scene may be generated on the first try
let configHasChanged = true
// Generate the AR scene only if the product configuration is new
if (this.configHasChanged) {
const arScene = await Atlatl.ARScene.create(visual)
// Set back to false after AR scene creation to prevent repeat creations of an unchanged product
configHasChanged = false
}
Then, when the user makes an update to the product’s configuration, such as when using setValue()
(see Updating the product using setValue()
), the configHasChanged
variable can be set back to true
to allow your code to re-generate the AR scene with the updated configuration.
// Update the product's configuration when a button is clicked
document.getElementById('my-button').addEventListener('click', () => {
myProduct.setValue('some_property', 'some_value')
// Set to true to allow for a new AR scene to be generated
configHasChanged = true
})
tip
This optimization also applies to products that do not have any configurable options. If your 3D product is static, you should still only call Atlatl.ARScene.create()
once, then show or hide the same QR code or “Launch AR” button to users for any subsequent times after that.
Full code example
- HTML
- JS
<html lang="en">
<head>
<meta charset="utf-8">
<title>My Page</title>
<!-- Link to the Visual Component's scripts -->
<script src="https://builds.dopple.io/atlatl-visual-api/releases/current/index.js" defer></script>
<!-- Link to custom scripts -->
<script src="scripts.js" defer></script>
</head>
<body>
<!-- Canvas to render the 3D scene to -->
<canvas id="my-canvas"></canvas>
<div>
<button id="generate-qr">Generate QR Code</button>
<button id="launch-ar">Launch AR</button>
<img id="qrCodeOutput" alt="" />
</div>
</body>
</html>
// Initialize some global variables for the Visual instance
let visual;
let myProduct;
let namespace = 'demo-assets';
let name = 'arrow_shoe';
// Initialize Dopple Visual when the document loads
window.addEventListener('load', async () => {
// Initialize the Visual instance on the page's canvas
const renderCanvas = document.getElementById('my-canvas')
visual = new Atlatl.Visual(renderCanvas)
// Replace with your client ID here
await visual.initClient('a1a1a1a1-b2b2-c3c3-d4d4-e5e5e5e5e5e5')
// Create and load the ProductTemplate
const template = new Atlatl.ProductTemplate(visual, namespace, name)
await template.load()
// Create the Product instance
myProduct = new Atlatl.Product(template)
// Hide the loading screen to show the product once the product is ready
await myProduct.ready()
visual.loadingScreen.hide()
})
// Initialize some global variables for the AR feature
let arScene
let arScenePromise
let configHasChanged = true
// Create a new AR scene, generate a QR code image, and display the Launch AR button
const createNewArScene = async () => {
// Create a new AR scene
arScene = await Atlatl.ARScene.create(visual, {
onProgress(e) {
// Show the AR loading %
document.getElementById('ar-loading-percent').innerHTML = e.overallProgress * 100 + '%'
// Update the AR loading progress bar
document.getElementById('ar-progress-bar').value = e.overallProgress * 100
}
})
// Generate a PNG image of the QR code onto the page
const qrCodeData = 'data:image/png;base64,' + arScene.generateQRCode()
document.getElementById('qr-code-img').setAttribute('src', qrCodeData)
// Display the AR container with the QR code and Launch AR button
document.getElementById('ar-container').style.display = ''
// Show/hide the QR code or "Launch AR" button depending on user's device type
const isMobileDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
if (isMobileDevice) {
document.getElementById('qr-code-img').style.display = 'none'
} else {
document.getElementById('launch-ar').style.display = 'none'
}
}
// Initialize the AR scene when the "View in AR" button is clicked
document.getElementById('view-in-ar').addEventListener('click', () => {
// Call createNewArScene() to generate a new AR scene ONLY if the product configuration has been updated
if(configHasChanged) {
createNewArScene()
// Set to false to prevent repeat AR scene creations of an unchanged product
configHasChanged = false
}
})
// Launch the AR experience when the "Launch AR" button is clicked
document.getElementById('launch-ar').addEventListener('click', () => {
if (confirm('Warning! You are about to leave this page. To continue to the AR experience, select "OK".')) {
arScene.launchAR()
}
})
document.getElementById('change-product-config').addEventListener('click', () => {
myProduct.setValue('some_property', 'some_value')
// Set to true to allow for a new AR scene to be created
configHasChanged = true
})