Visiroll

DEVELOPER REFERENCE

Control the player.
React to playback.

Drive the embedded player from the page around it. Start and stop playback, build your own mute button, and react to events — including the moment a viewer finishes the video.

Quick start

Load the script, point it at your iframe, and call methods on the returned player. Every method returns a promise.

<iframe id="video" src="https://visiroll.com/v/VIDEO_ID"
        title="Campaign video" width="1920" height="1080"
        allow="autoplay; fullscreen"
        loading="lazy"
        style="border:0;aspect-ratio:1920/1080;width:100%;height:auto"></iframe>

<script src="https://visiroll.com/player.js"></script>
<script>
  var player = new Visiroll.Player(document.getElementById("video"));

  player.on("ended", function () {
    console.log("playback reached the end of a pass");
  });

  // Use the player’s Play button to start playback.
</script>

Commands sent before the iframe finishes loading are queued and delivered as soon as it is ready. Every call has a 30-second timeout, including time spent waiting for a lazy iframe. Catch rejected promises; retry after the iframe loads if necessary.

The iframe

The constructor accepts an iframe, a container with options, or a CSS selector. The allowattribute delegates autoplay permission to the iframe. It does not override browser policies or guarantee that play() will succeed.

var player = new Visiroll.Player("#video");        // selector
var player = new Visiroll.Player(iframeElement);   // element
// Create a responsive iframe inside a container:
var player = new Visiroll.Player("#video-container", {
  id: "YOUR-VIDEO-UUID", muted: true, loop: true,
  controls: true, autopause: true, start: 0
});
player.ready().then(function () { return player.play(); }).catch(console.error);

The repository also provides @visiroll/player with ESM, CommonJS and TypeScript definitions. It is not yet published to the npm registry. Imports are safe during server rendering; construct the player in the browser.

Repeated construction for the same iframe returns the same instance. Load the SDK before running integration code. An asynchronously loaded script may not exist yet when your next inline script runs.

Add ?autopause=1 to each participating iframe URL to pause the others when one starts. This is opt-in and requires player.js on the hosting page. Add dnt=1 to disable player analytics events for that embed; media authorization and delivery still operate. Combine options as ?autopause=1&dnt=1. Optional autoplay, muted,loop and controls accept 1/0 or true/false. start is a non-negative position in seconds. These apply to this embed only; omitted or invalid options retain defaults. Mandatory branding and domain authorization cannot be overridden.

preload accepts viewport (default: prepare near the screen),none (wait for play), hover (prepare on pointer entry or keyboard focus), or auto (prepare once the iframe loads). Visible autoplay and explicit play take precedence. Preloading prepares a small initial buffer; it does not download the entire video. Iframe loading is separate: most Share presets use lazy loading; Hero uses eager loading.

initial_quality=360p, for example, requests the starting rendition while keeping adaptive quality enabled afterwards. Supported values: 240p, 360p, 480p, 540p, 720p, 1080p, 1440p and 2160p. Unavailable sizes use the nearest lower rendition, or the smallest available one. Native HLS browsers such as Safari choose their own quality. The SDK option is initialQuality; use auto or omit it for the existing automatic selection.

Copy a ready-made embed from the video’s Share tab in the studio.

Presets and background video

Choose a preset in the studio’s Share tab. It changes the copied embed and preview without changing the video’s saved settings.

  • Saved: saved playback settings, viewport preparation, automatic quality, lazy iframe.
  • Normal video: manual playback, controls, no loop, autopause, hover/focus preparation, automatic quality, lazy iframe.
  • Hero / background: muted autoplay and loop, hidden title and controls, no autopause, automatic preparation, 360p starting hint, eager iframe.
  • Video list: manual playback, controls, no loop, autopause, viewport preparation, 360p starting hint, lazy iframe.

The URL option background=1 supplies autoplay, muted, loop and hidden-control defaults. Explicit autoplay, muted, loop and controls flags take precedence. Set loading, starting quality and autopause separately when building a URL yourself. Background presentation preserves the video aspect ratio and mandatory branding; it does not crop video to cover an arbitrary container.

If autoplay is blocked, the player keeps a fallback Play action. Native HLS may ignore the starting-quality hint. Autopause requires the SDK and autopause=1 on each participating embed.

Publish in WordPress or Webflow

The iframe is enough for normal playback. The JavaScript API is optional and is needed only when the surrounding page must control playback or react to player events.

Keep the script tag even when you never call the API. Safari gives the iframe only your site’s origin as referrer, and the script tells every Visiroll player on the page its exact URL, so the analytics report names the page rather than the site root. The script reads nothing but the page address, passes on only its origin, path and UTM values, sets no cookies and loads nothing else.

  • WordPress guide — Custom HTML block, permissions, domains and common sanitization problems.
  • Webflow guide — Code Embed, staging hostname, components and CMS use.

Both guides preserve the video’s real aspect ratio, including square and vertical content.

Methods

MethodReturnsNotes
loadVideo(idOrUrl)Promise<string>Loads another authorized video in the same iframe. IDs retain embed options. Resolves with its UUID when the new API is ready.
unload()Promise<null>Pauses and releases media resources; play() loads the media again.
setLoop(boolean)Promise<boolean>Changes automatic repeat without resetting the analytics session.
getQualities()Promise<{id,label}[]>Available quality choices; native HLS may offer only auto.
getQuality()Promise<string>Selected quality ID, including auto.
setQuality(id)Promise<string>Selects an ID returned by getQualities().
ready()Promise<void>Confirms API readiness, not media metadata or autoplay permission. Times out after 30 seconds.
destroy()Promise<void>Detaches the SDK and rejects pending commands. Removes SDK-created iframes; caller-owned iframes and playback remain intact.
getLoop()Promise<boolean>Reads the video’s configured loop setting.
getEnded()Promise<boolean>Native playback-ended state; remains false while looping.
getPlaybackRate()Promise<number>Current playback speed.
getSeeking()Promise<boolean>Whether a seek is in progress.
getBuffered()Promise<number[][]>Media ranges as [start, end] pairs in seconds.
getPlayed()Promise<number[][]>Media ranges as [start, end] pairs in seconds.
getSeekable()Promise<number[][]>Media ranges as [start, end] pairs in seconds.
getVideoId()Promise<string>Visiroll video UUID.
getVideoTitle()Promise<string>Video title.
getVideoWidth()Promise<number>Intrinsic media dimension; zero before metadata.
getVideoHeight()Promise<number>Intrinsic media dimension; zero before metadata.
play()Promise<null>Starts playback. Rejects when the browser blocks it, or media cannot start; inspect the rejection name and message.
pause()Promise<null>Pauses playback.
getPaused()Promise<boolean>Whether playback is currently paused.
setMuted(muted)Promise<boolean>Mutes or unmutes. Returns the resulting state.
getMuted()Promise<boolean>Current mute state.
setVolume(volume)Promise<number>Volume between 0 and 1. Values outside the range are clamped.
getVolume()Promise<number>Current volume between 0 and 1.
setCurrentTime(seconds)Promise<number>Requires loaded metadata. Resolves after seeked; rejects invalid positions or an interrupted seek.
getCurrentTime()Promise<number>Current position in seconds.
getDuration()Promise<number>Total duration in seconds, or 0 before metadata loads.
setPlaybackRate(rate)Promise<number>Finite speed from 0.25 to 4, where 1 is normal; device restrictions may apply.
player.getDuration().then(function (seconds) {
  console.log("duration", seconds);
});

player.setVolume(0.4)
  .then(function (volume) { console.log("volume is now", volume); })
  .catch(function (error) { console.error(error); });

Events

Subscribe with on(name, listener) and remove with off(name, listener), or off(name) to drop every listener for that event. Each listener receives a single data object.

EventPayloadNotes
ready{ id }API initialized. Use ready() if subscribing after initialization.
playing{ currentTime, seconds, percent, duration }Media is playing, including after buffering.
seeking{ currentTime, seconds, percent, duration }A seek started.
bufferstart / bufferendMedia is waiting / able to play again. May also occur during preload or seek.
error{ name, message, code? }Playback error. Command failures reject their own promises.
durationchange{ duration }Media duration changed.
progress{ buffered: [[start, end], ...] }Buffered ranges changed. This payload is Visiroll-specific.
playbackratechange{ playbackRate }Playback speed changed.
qualitychange{ quality }Selected quality label changed; auto denotes adaptive selection.
loaded{ id, duration, currentTime, seconds, percent }Media metadata is available. May repeat after source renewal; not an instruction to autoplay.
play{ currentTime, seconds, percent, duration }Playback started or resumed.
pause{ currentTime, seconds, percent, duration }Playback paused.
ended{ duration, currentTime, seconds, percent, loop }Playback reached the end, including each automatic loop. Does not prove the whole video was watched.
timeupdate{ currentTime, seconds, percent, duration }Position changed. Fires several times per second while playing.
volumechange{ volume, muted }Volume or mute state changed, from your code or from the player controls.
seeked{ currentTime, seconds, percent, duration }A seek finished.

These browser events are for your page logic. Visiroll’s qualified views, engaged views and retention are calculated independently; see analytics definitions.

Unlike Vimeo, Visiroll emits ended during loop playback. Existing currentTimefields are preserved; seconds, percent (0–1) and duration are additive aliases. On a loop completion, seconds describes the completed pass while currentTime is the actual restarted position.

Call player.destroy() when removing the iframe. Pending calls reject withDestroyedError. Reloading an initialized iframe rejects in-flight calls withPlayerReloadedError; await ready() before retrying. Unanswered calls reject withTimeoutError. Invalid arguments reject with TypeError or RangeError.

Rewards must be validated and deduplicated on your server. A browser completion event can follow a seek and may recur on every loop; it is not proof of uninterrupted viewing.

Worked example

Custom buttons can request playback and sound. Keep the player’s own controls available as a fallback. Initialize before the click and send the command directly from its handler; waiting for a request, timer or state getter first can lose user activation. SDK commands cross an iframe boundary, so browser policies still apply.

<button id="play-video" disabled>Play video</button>
<button id="enable-sound" disabled>Enable sound</button>
<p id="player-status" role="status"></p>

<script>
  // Load player.js and the #video iframe as shown in Quick start.
  var player = new Visiroll.Player("#video");
  var playButton = document.getElementById("play-video");
  var soundButton = document.getElementById("enable-sound");
  var status = document.getElementById("player-status");

  function reportError(error) {
    status.textContent = "Use the controls inside the video to play or enable sound.";
    console.error(error.name, error.message);
  }

  player.ready().then(function () {
    playButton.disabled = false;
    soundButton.disabled = false;
  }).catch(reportError);

  playButton.onclick = function () {
    player.play().then(function () { status.textContent = ""; }).catch(reportError);
  };
  soundButton.onclick = function () {
    player.setMuted(false).then(function (muted) {
      status.textContent = muted ? "Enable sound inside the video." : "Sound enabled.";
    }).catch(reportError);
  };
</script>

In a component or single-page application, remove your host-page listeners and call destroy()during cleanup. For a caller-owned iframe, pause first if it will remain on the page: destroying the SDK does not stop its playback. For an SDK-created iframe, destruction also removes the iframe. Do not start playback from every loaded event.

Domains and security

The player only accepts commands from pages whose origin appears in that website’s allowed domains, wildcards included. Replies and events are posted back to that exact origin, never to *. A page that is not on the list can neither embed the video nor control it.

Add every staging or production hostname that will contain the player. Remove obsolete hostnames as soon as a publishing destination is retired.

Allowed domains are managed per website under Websites → Settings → Allowed domains.

Troubleshooting

A method promise never settles

Confirm the iframe has loaded, its src points to Visiroll, and the embedding hostname is allowed. Calls time out after 30 seconds. Listen for iframe load before retrying a late lazy embed; inspect the error name and allowed hostname.

play() rejects

Inspect the error name and message: browser policy, unavailable media, loading or authorization can prevent playback. Start muted for autoplay, catch failures, and keep the player’s Play button available. A host-page click can request playback but does not guarantee permission on every browser.

Autoplay starts without sound

Visiroll starts autoplay muted to accommodate browser policies. The volume control lets the viewer request sound. You can also request sound with setMuted(false) directly from a click on your page and check the returned mute state.

iPhone autoplay stays paused

Low Power Mode can block even muted autoplay. Battery percentage alone is not the rule. Keep a poster and the fallback Play action; test on a physical iPhone with Low Power Mode enabled. Browser emulation does not verify this setting.

No events arrive

Create one Player instance for the correct iframe and keep it alive. Do not remove the iframe before calling destroy().