Documentation

Examples

Complete, self-contained pages for a few common scenarios, combining the Configuration Reference and the DiscoVR class. Copy one as a starting point and adapt it.

Virtual tour

An environment with a few 3D models placed around it, and free movement/teleport — no multiplayer, no demo content.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Virtual Tour</title>
</head>
<body>
    <discovr
        environment="preset:forest;groundColor:#2d4a1f"
        playerRig="enable:true"
    ></discovr>

    <script type="module" src="https://core.discovr-engine.eu/discovr-core.js"></script>
    <script type="module">
        function whenSceneReady(callback) {
            var scene = document.querySelector('a-scene');
            if (scene) {
                if (scene.hasLoaded) callback();
                else scene.addEventListener('loaded', callback);
            } else {
                setTimeout(() => whenSceneReady(callback), 100);
            }
        }

        whenSceneReady(() => {
            var discovr = new window.DiscoVR();
            discovr.addAsset("statue", "models/statue.glb");
            discovr.addModel("statue", { position: "0 0 -5" }, "statue1");
            discovr.addModel("statue", { position: "5 0 -8", rotation: "0 45 0" }, "statue2");
        });
    </script>
</body>
</html>

Multiplayer meetup room

A shared room with voice chat, text chat, and named avatars. Everyone who opens this page with the same roomName lands together.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Meetup Room</title>
</head>
<body>
    <discovr
        presets="defaultScene:true;defaultLights:true"
        environment="preset:default"
        playerRig="enable:true"
        network="enable:true;roomName:team-meetup;showChat:true;showCam:true;showMic:true;showAvatarName:true;positionalAudio:true"
        interface="userClick:true;title:Team Meetup"
    ></discovr>

    <script type="module" src="https://core.discovr-engine.eu/discovr-core.js"></script>
</body>
</html>

interface:userClick:true is required here so visitors get a "click to enter" prompt — browsers block microphone/audio access without that initial user gesture.

Physics playground

Enables the built-in physics demo content (a ball rolling down ramps) to sanity-check that PhysX is working, as a starting point for your own physics-driven scene.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Physics Playground</title>
</head>
<body>
    <discovr
        presets="defaultScene:true;defaultLights:true;defaultContents:true"
        physics="true"
        playerRig="enable:true"
    ></discovr>

    <script type="module" src="https://core.discovr-engine.eu/discovr-core.js"></script>
</body>
</html>

defaultContents:true together with physics:true is what brings in the demo ramps and ball — swap those out for your own physx-body entities once you've confirmed physics is working (see the DiscoVR class's addEntity() for adding arbitrary components like physx-body).

Embedded widget

A small, non-VR scene meant to sit inside an iframe or a section of a larger page — no VR button, drag instead of pointer-lock to look around.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Embedded Widget</title>
</head>
<body>
    <discovr
        presets="defaultScene:true;defaultLights:true"
        environment="preset:default"
        interface="vrButton:false;embedded:true"
    ></discovr>

    <script type="module" src="https://core.discovr-engine.eu/discovr-core.js"></script>
</body>
</html>

Product viewer (orbit camera)

A product or 3D configurator page. There is no walking: the camera orbits the object (drag to rotate, wheel or pinch to zoom), and a slow turntable runs until the visitor touches it. i-orbit-camera needs a camera on the same entity, and that entity's position is where the view starts.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Product Viewer</title>
</head>
<body>
    <discovr interface="vrButton:false"></discovr>

    <a-scene>
        <a-assets>
            <a-asset-item id="productModel" src="product.glb"></a-asset-item>
        </a-assets>
        <a-entity id="product" gltf-model="#productModel"></a-entity>
        <a-entity light="type: ambient; intensity: 0.6"></a-entity>
        <a-entity light="type: directional; intensity: 0.8" position="2 4 3"></a-entity>
        <a-entity camera="active: true" position="0 1.4 3"
                  i-orbit-camera="target: #product; minDistance: 1; maxDistance: 6; autoRotate: true"></a-entity>
    </a-scene>

    <script type="module" src="https://core.discovr-engine.eu/discovr-core.js"></script>
</body>
</html>

Product configurator (variants)

The product viewer above, with options the visitor can switch. Each i-variant__<name> is one choice of one option group. A choice can change the colour, texture (map), metalness or roughness of glTF materials or nodes, and can show or hide parts. With xray: on (or off) a choice switches the model's x-ray view, on the nodes listed in xrayNodes (empty = the whole model), with the look of the model's own i-xray settings. i-configurator-ui on the scene lists the groups as a panel.

The current choice of a group is the bus topic variant:<group>, so the options can also be changed from a script, for example DiscoVR.bus.set("variant:fabric", "Blue"), or from an i-bus rule.

<a-scene i-configurator-ui="title: Configure your chair; position: right; theme: dark">
    <a-assets>
        <a-asset-item id="chairModel" src="chair.glb"></a-asset-item>
        <a-asset-item id="linen" src="linen.jpg"></a-asset-item>
    </a-assets>
    <a-entity id="chair" gltf-model="#chairModel"
        i-variant__red="group: fabric; groupLabel: Fabric; label: Red; initial: true; materials: Fabric; color: #b3261e"
        i-variant__blue="group: fabric; label: Blue; materials: Fabric; color: #1e4fb3"
        i-variant__linen="group: fabric; label: Linen; materials: Fabric; color: #ffffff; map: #linen"
        i-variant__arms="group: arms; groupLabel: Armrests; label: With; initial: true; show: Arms"
        i-variant__noarms="group: arms; label: Without; hide: Arms"
        i-variant__outside="group: view; groupLabel: View; label: Outside; initial: true"
        i-variant__inside="group: view; label: Inside; xray: on; xrayNodes: Shell; show: Frame"></a-entity>
    <a-entity camera="active: true" position="0 1.2 2.5" i-orbit-camera="target: #chair; autoRotate: true"></a-entity>
</a-scene>

Sharing and embedding a configurator

i-configurator-api on the scene connects the configurator to the outside:

  • With url: true, the visitor's choices are kept in the page address, for example ?variant.fabric=Linen&variant.arms=Without. That makes the configuration a link that can be shared. A link with those parameters opens with the same choices.
  • With embed: true, a page that shows the configurator in an <iframe> can control it through postMessage.
  • i-configurator-ui can also show "Copy link" and "Download image" buttons, with share: true and download: true.
  • The visitor can fold the panel from its header, so it doesn't cover the model. collapsed sets how it opens: auto (the default: folded when the scene is narrower than 600px, as on a phone), open or closed.
<a-scene i-configurator-ui="title: Configure your chair; share: true; download: true"
         i-configurator-api="url: true; embed: true; origins: https://shop.example.com">

The page that embeds it:

<iframe id="viewer" src="https://example.com/p/my-chair"></iframe>
<script>
  const viewer = document.getElementById("viewer");
  window.addEventListener("message", (e) => {
    if (e.source !== viewer.contentWindow) return;
    // {type: "discovr:ready", groups, state}, then {type: "discovr:change", group, label, state}
    // {type: "discovr:state", requestId, state, groups} / {type: "discovr:screenshot", requestId, dataUrl}
    console.log(e.data);
  });
  viewer.contentWindow.postMessage({ type: "discovr:select", group: "fabric", label: "Blue" }, "*");
  viewer.contentWindow.postMessage({ type: "discovr:getState", requestId: 1 }, "*");
  viewer.contentWindow.postMessage({ type: "discovr:screenshot", requestId: 2, mimeType: "image/png" }, "*");
</script>

origins lists the pages allowed to talk to the configurator. *, or leaving it empty, allows any page.