Skip to content

Start Now Button

By default, once assets finish downloading, Kivicube shows a "Start Now" button and waits for the user to click before entering the scene.

Why This Button Exists by Default

It is not just a UI element. It also helps with browser media policy:

  • it makes the browser treat camera access as user-initiated, which improves success rate
  • it provides a user gesture to unlock later audio and video playback
  • it avoids jumping directly into the experience as soon as assets finish downloading, which can feel abrupt
  • it gives the host layer time to show explanations or permission guidance

Force Hide It

You can force-hide it with the hideStart property:

js
await kivicubeIframePlugin.openKivicubeScene(iframe, {
  sceneId,
  hideStart: true,
});

Or directly in HTML:

html
<iframe id="kivicubeScene" scene-id="..." hide-start></iframe>

Impact After Hiding It

  • the AR experience background may turn black or white because the camera fails to open
  • after asset download finishes, the flow jumps directly into scene loading and AR without showing the button
  • if the scene contains video or audio, the browser may block playback

Safer Alternatives

Compared with directly using hideStart, it is usually better to:

  1. keep the platform's default start flow
  2. or provide a custom host-layer "Start Now" button
  3. let the flow continue only after the user clicks

If your feature depends on a button click to work reliably, a host-layer button coordinated with the default flow is safer than forcing auto-entry.

Good Cases for Hiding It

  • pure Web3D experiences with no audio/video autoplay requirements
  • very short flows where removing one click matters a lot
  • experiences with lots of video or audio that need autoplay
  • flows where users should explicitly grant permission or read guidance first

Customize the Start Now Button

If you want to keep the native Kivicube Start Now Button and its default behavior, but align it with your host page UI, the advanced Scene / Collection APIs provide:

ts
getStartButtonRect(): Promise<DOMRect | undefined>
setStartButtonRect(rect: Partial<CSSStyleDeclaration>): Promise<void>
  • getStartButtonRect() returns the real getBoundingClientRect() result of the button inside the iframe.
  • setStartButtonRect(rect) merges the given object into the button's inline style. Common fields include position, left, top, right, bottom, width, height, transform, etc.

How to Get the API

On a scene page, get SceneApi from the ready event:

js
let api = null;

iframe.addEventListener('ready', (event) => {
  api = event.detail.api;
});

On a collection page, get CollectionApi from ready:

js
let api = null;

iframe.addEventListener('ready', (event) => {
  api = event.detail.api;
});

In Scenes

When to Call

  • You can call them in the ready event and any later event, such as downloadAssetEnd. You can fetch or change the button's position and size even before it appears.
  • If hideStart: true is configured, the methods can still be called but no longer serve a purpose.

The host page overlays a custom button at the same position and sets CSS pointer-events: none so the click lands on the real button inside the iframe.

Common Approach A: Move and Resize the Button Inside the iframe Directly

Use setStartButtonRect() to move the button inside the iframe to a new position, then align it with host-layer titles, hint text, and brand decoration.

js
const hostStartButton = document.querySelector('#hostStartButton');
hostStartButton.style.visibility = 'hidden'; // Hide the button by default
hostStartButton.style.pointerEvents = 'none'; // Must be set
iframe.addEventListener("ready", async (e) => {
  const api = e.detail.api;

  // Make sure hostStartButton is already rendered in the DOM and laid out at this point.
  const rect = hostStartButton.getBoundingClientRect();
  // Keep the start button inside the iframe below aligned with the host button above.
  // This way, when the user clicks the host button, the click passes through to the real start button below.
  // It also covers the plugin's internal button, which is required to customize the button style.
  await api.setStartButtonRect({
      left: rect.left + 'px',
      top: rect.top + 'px',
      width: rect.width + 'px',
      height: rect.height + 'px'
  });
});
iframe.addEventListener("downloadAssetEnd", async (e) => {
  // Assets finished downloading; show the button
  hostStartButton.style.visibility = 'visible';
});
iframe.addEventListener("loadSceneStart", (e) => {
  // The iframe start button was clicked and the scene starts loading; hide the button now
  hostStartButton.style.display = 'none';
});

Common Approach B: Read the Button Area and Overlay a Host-Layer Button

Use getStartButtonRect() to keep the custom button's position and size in sync with the iframe button. This also covers the plugin's internal button, which is necessary to customize the button style.

js
const hostStartButton = document.querySelector('#hostStartButton');
iframe.addEventListener('ready', (event) => {
  const api = event.detail.api;

  const rect = await api.getStartButtonRect();
  Object.assign(hostStartButton.style, {
    visibility: 'hidden', // Hidden at this point
    position: 'fixed',
    left: `${rect.left}px`,
    top: `${rect.top}px`,
    width: `${rect.width}px`,
    height: `${rect.height}px`,
    pointerEvents: 'none', // Important; must be set
  });
});
iframe.addEventListener("downloadAssetEnd", async (e) => {
  // Assets finished downloading; show the button
  hostStartButton.style.visibility = 'visible';
});
iframe.addEventListener("loadSceneStart", (e) => {
  // The iframe start button was clicked and the scene starts loading; hide the button now
  hostStartButton.style.display = 'none';
});

Whether you use Approach A or B, the idea is to keep the iframe button and the host-layer button aligned. When the user clicks the host-layer button, the click passes through to the iframe button, which triggers the iframe button's click event.

In Collections

To be improved, but the core logic is the same as for scenes.