Video and content recommendations

Delight VR can display related content in 2D as well as VR. This allows for a very convenient user experience as users can jump from one content to another without leaving the experience. You can either add contents statically via HTML markup or dynamically through a Javascript hook. As of now related content can either be a <dl8-video>, <dl8-cinema>, <dl8-live-video> or an <dl8-external-content> element that opens a new URL and can optionally autostart Delight VR on this URL. This enables further use cases like placing ad banners inside the related content widget or redirection of users to other sites with Delight VR content.

The feature can be included by adding the <dl8-recommendation> element inside the <dl8-video>, <dl8-cinema> or <dl8-live-video> element. The <dl8-recommendation> has the following parameters:

title="<string>" [optional]

Specifies the text that is shown above the recommendations in VR to give the related items that are displayed a bit more context. For example if you host a video portal and want to display videos that are related to the current video you could write title=“Related videos” to make it clear for the users that the next clips are videos and related. The default value is “Recommendations”

mode=”<static|dynamic>” [optional]

This indicates the mode on how recommendations are going to be fetched. There is a “static” and a “dynamic” mode. The default is “dynamic”.
In “static” mode simply add <dl8-video> or <dl8-external-content> elements as children to the <dl8-recommendation> element like so:

 <dl8-video ...> 
    <source .../>
    <dl8-recommendation mode="static">
          title="Related Video 1" 
            <source …/>
          title="Related Content 2"

In “dynamic” mode new recommendations are dynamically queried via a Javascript hook that is user defined. This enables you to implement your own recommendation logic based on the current content. To implement dynamic recommendations just add the following parameter:

on-request-recommendation=”<function(requestId, count, recommendationCallback)>”

This function will be called whenever the player needs to be fetched from the server. Through the given arguments you can formulate queries to your own server that then can respond with a set of recommendations. On server response you can create an array of contents (either dl8-video or dl8-external-content) that adhere to the Delight VR json spec and pass them as an argument to the recommendationCallback function. A complete example could look like this:

    // The requestRecommendations function could be named anything and could reside anywhere. 
    // The DVR.requestRecommendations is just an example.
    window.DVR = {
        requestRecommendations: function(id, count, recommendationCallback) {
            // pseudo code
            requestDataFromServer(id, count).then(res => {
                var recommendations = createDelightVRJsonSpecFromResponse(res)

 <dl8-video ...> 
    <source .../>
      on-request-content="DVR.requestRecommendations(id, count, recommendationCallback)"

autoplay & hidden [optional]

You can set the autoplay and hidden attribute to create a recommendation playlist. When the autoplay attribute is provided, the player automatically starts the first recommendation after the video has ended. If you enable hidden, the recommendation interface is hidden from the user. It is best used in conjunction with autoplay to create a hidden playlist.

 <dl8-video ...> 
    <source .../>
    <dl8-recommendation mode="static" autoplay hidden>
          title="Related Video 1" 
            <source …/>
          title="Related Content 2"



With this element you can link to other external URLs. It can only be added to other dl8 elements and can’t be used standalone. As of now it can be added inside a <dl8-recommendation> element and inside a <dl8-hub-content> element. Since it features a poster image and an optional title a common use case is to use this element for placing ads, affiliates, special content or use it as general traffic redirection to other sites with Delight VR content.

   title="External Link 1"

The element features the following attributes:

url="<uri>" [mandatory]

Specifies the URL of the external content. If the URL references a site with Delight VR the autostart attribute becomes important.

title="<string>" [optional]

The title text that will be displayed as a label on the content tile.

poster="<uri>" [optional]

The poster image that will be displayed on the content tile. Please note that a cover layout is used for the poster.

autostart [optional]

If set and the <URI> in the url attribute points to a Delight VR enabled website the Delight VR player on the target page will automatically start. The default is false

window-name="<string>" [optional]

A DOMString specifying the name of the browsing context (window, <iframe> or tab) into which to load the specified external content; if the name doesn’t indicate an existing context, a new window is created and is given the name specified by window-name. This name can then be used as the target of links and forms by specifying it as the targetattribute of <a> or <form> elements. The name should not contain whitespace. Keep in mind that this will not be used as the window’s displayed title.

window-features="<string>" [optional]

A DOMString containing a comma-separated list of window features given with their corresponding values in the form “name=value”. These features include options such as the window’s default size and position, whether or not to include scroll bars, and so forth. There must be no whitespace in the string. See Window features in the MDN for documentation of each of the features that can be specified.



The Hub is a fully customizable and data-driven Delight VR element that connects multiple VR contents through a navigation hub that is explorable in VR.


The interface presented in the hub is composed of widgets like content grids and menus. These widgets (like the content itself) are created through markup as child elements of the <dl8-hub>. A powerful action system based on filter and sorter actions ensures a flexible approach to what is shown in content views and in which order.

Getting started

The simplest reasonable form of the <dl8-hub> includes a few contents wrapped in a <dl8-hub-content> element and just one grid widget (<dl8-hub-grid>). All widgets and views (i.e. things that are actual interface elements) need to be grouped into a layout component to tell the system how to arrange the widgets around the user. In the current version this layout element is the <dl8-hub-vizor>.

With this basic building blocks at hand a good markup starting point for a simple hub would look like this:

    <dl8-video title="Video 1" poster="p1.jpg" format="STEREO_360_TB">
      <source src="video1.mp4" type="video/mp4"/>
    <!-- ... -->   
    <dl8-img title="Image 6" poster="p2.jpg" src="img.jpg" format="MONO_360">

The result might look something like this:

Hub Markup in Depth

In this section we will go over the different elements that make up the hub in detail and explain their markup and element API.


This is the root element. Since it’s also an embed element like other Delight VR contents (videos, images, etc.) it has the same common API (width, height, poster, title, etc.). There are two additional parameters to control the room that surrounds the hub which behave exactly like the equally named parameters of the <dl8-cinema> element and setup the 360° image for the room:

room-src=”<URI>” (optional)

room-format=”<string>” (optional)

There are several child elements that can be added to the <dl8-hub>. There are two different kinds: Elements that provide data and semantic (<dl8-hub-content>, <dl8-hub-filter>, <dl8-hub-sorter> and elements that provide the visual representation (<dl8-hub-vizor>)


This element groups up all the content that the hub should display. It is important to note that there is a separation of appearance and data at play here. Contents defined as child elements of this grouping element have no visual representation but only provide the data for the visual elements (aka widgets). The child elements can either be regular Delight VR contents (dl8-video, dl8-img, dl8-tour, etc.) or <dl8-hub-group> elements. Additionally you can add <dl8-external-content> elements which represent links to other sites and external content. This allows you to setup things like banner ads, affiliate links and other traffic redirection mechanisms. 

To account for use cases with massive amounts of data and server-side filtering and pagination logic the <dl8-hub-content> mode attribute can be set to dynamic. This in combination with two additional events on the element allow for dynamic fetching of contents for the widgets. The attributes are as follows:


on-request-content=”<function(startIdx, endIdx, filters, sorter, contentCallback)>”
This function will be called whenever the hub detects that a content is missing upon user interaction and needs to be fetched from the server. Through the given arguments you can formulate paginate queries to the server that then can respond with a subset of contents. On server response you can create an array of contents that adhere to the Delight VR json spec (see below) and pass them as an argument to the contentCallback function.

on-request-content-count=”<function(filters, contentCountCallback)>”
This function will be called when the hub needs to know the item count for a specific subset of the contents. This is used to build up pagination and other related things. When the server responds with the count just pass that as an argument to the contentCountCallback function.

The Delight VR JSON/Object Spec

To support dynamically adding contents when the dl8-hub-content mode is dynamic you need to pass an array of contents to the contentCallback function in the on-request-content handler. The data passed to this callback must adhere to the Delight VR Object specification. Here is an example of how a <dl8-video> element with two sources would look like:

  tagName: "dl8-video",
  title: "Example Video",
  poster: "example-poster.jpg",
  format: "STEREO_360_TB",
  _children: [{
    tagName: "source",
    src: "example-low.mp4",
    type: "video/mp4",
    quality: "low"
  }, {
    tagName: "source",
    src: "example-high.mp4",
    type: "video/mp4",
    quality: "high"

Generally a content is always described by the tagName, all subsequent attributes and optional children tags adhering to the same spec recursively in the _children property. Below you will find a description of the format:

<data> = {
  tagName: <a-valid-dl8-content-tag>,
  opt <attrNameInCamelCase>: <string>, // NOTE: camel case will be
  // converted to attribute
  // notation:
  // forExample -> for-example
  opt _children: [ // NOTE: recursive nesting is 
    // possible

Filter and sorter API

When the on-request-content and on-request-content-count handlers are called, your JavaScript function will be given a filters and a sorter argument. These arguments are arrays of filterIds or sorterIds that you can use to identify your filter and sorter objects to formulate a backend query.


The <dl8-hub-group> is a meta element that can group contents by defining actions (filtering, sorting) that are triggered when interacting with it. Like a menu item that can appear amidst contents in a content widget.

<dl8-hub-group title="Music" poster="poster.jpg">
  <dl8-hub-action-filter for="main" filter="musicFilter">


The <dl8-hub-filter> is a grouping element for all available filters. Filters are means to filter content according to content types, existing content attributes or even freely definable content attributes (x-attrs). They can be referenced by <dl8-hub-action-filter> actions. Each filter has a filter-id attribute to reference it and a value attribute to tell the system what should be filtered as well as a name attribute. The name attribute must be provided if the filter is displayed to the user (done through the dl8-hub-grid filter-list array). Attribute filter additionally have a attr attribute to declare over which content attribute to filter. Given that you are using the dynamic content mode you can use the <dl8-hub-filter-dynamic> to reference remote implementations. Possible child elements of <dl8-hub-filter> are:

filter-id=”<string>” attr=”<string>” value=”<string>” name=”<string>”
filter-id=”<string>” attr=”<string>” value=”<string>” name=”<string>”
filter-id=”<string>” attr=”<string>” value=”<array>” name=”<string>”
filter-id=”<string>” attr=”<string>” value=”<array>” name=”<string>”
filter-id=”<string>” value=”<string>” name=”<string>”
filter-id=”<string>” value=”<array>” name=”<string>”
filter-id=”<string>” name=”<string>”

This for example can be used in combination with a <dl8-hub-group> element to define a grid tile that, upon interacting, filters the grid view to only show videos:


 <dl8-video ...></dl8-video>
 <dl8-img ...></dl8-img>
 <dl8-hub-group title="Videos" poster="poster.jpg">
 <dl8-hub-action-filter for="main" filter="videoFilter">

 <dl8-hub-filter-element-is filter-id="videoFilter" value="dl8-video">

 <dl8-hub-grid view-id="main"></dl8-hub-grid>



This is a grouping element for all sorters. Sorter are means to sort content over freely defined content attributes. Sorter and Filter have lot in common. Both can be referenced by actions via the sorter-id and define the attribute over which to sort with the attr attribute. Sorting can either be done lexicographical or numerical. This is defined by the type attribute. Given you are working in a dynamic query mode (see above for details) you can use the dl8-hub-sorter-dynamic to reference your remote server implementation of a sort. Currently the following sorter are available:

sorter-id=”<string>” attr=”<string>” type=”<lexicographical|numerical>”


The vizor groups all children widget elements and lays them out horizontally on a virtual cylinder around the user. It does so by centering all widgets along the resting position (negative z-axis) and stretching widgets out vertically so they fill up the height of the vizor. It currently is the only available layout system for the hub. You can set the following attributes:

height=”<number>” (optional)

radius=”<number”> (optional)

spacing=”<number>” (optional)

show-brand-logo (boolean, optional)

show-home-button (boolean, optional)

no-shadow  (boolean, optional)


The grid is one of two currently available widgets. It is the main view for displaying content. It displays cover items with thumbnails and labels in a regular grid view with freely definable column and row count and can be paginated when not all items fit one page. The pagination can either be vertically or horizontally. The view can be filtered and sorted by default through setting filter and sorter attributes respectively. You can also provide a tag filtering system for the user by setting the filter-list value to a custom defined array. Each value must refer to an existing dl8-hub-filter.

As a bonus the grid can have a sorter dropdown by providing a sorter-list so the user can choose the ordering of the displayed contents. Possible attributes are:

title=”<string”> (optional)

view-id=”<string>” (optional)

width=”<number>” (optional)

rows=”<number>” (optional)

columns=”<number>” (optional)

tile-padding-x=”<number>” (optional)

tile-padding-y=”<number>” (optional)

scroll-mode=”<vertical|horizontal>” (optional)

show-no-cover-text (boolean, optional)

filter=”<filter-id>” (optional)

sorter=”<sorter-id”> (optional)

sorter-list=”<array>” (optional)

filter-list=”<array>” (optional)


The menu widget displays <dl8-menu-item> child elements in a vertical scrolling list. Menu items just like hub groups can trigger multiple actions (filter actions, sorter actions, etc.). The menu has the following attributes:

title=”<string”> (optional)

view-id=”<string”> (optional)

width=”<number”> (optional)

rows=”<string”> (optional)

default-menu-item=”<menu-item-id>” (optional)


A menu item is a text button that upon interaction triggers the declared child action elements. It has the following attributes:


menu-item-id=”<string”> (optional)

The Action System

As mentioned in the markup section, triggerable elements (<dl8-hub-group>, <dl8-hub-menu-item>) can declare action elements as children which are executed in order of appearance when a user interacts with these. There are different types of actions. Currently there are 3 types: filter actions, sorter actions and set-title actions. The action system is build in a way that novel actions can easily be introduced so that greater flexibility and interactivity of the hub can be achieved in the future.

The currently supported actions are:


This action triggers a filter on a specified view




This action triggers a sort operation on a specified view




This action triggers a title change on a specified view




Three column layout

This is a full blown hub with a menu, a full size grid and a small side grid for special items

 <dl8-hub title="Three Columns" room-src="room.jpg" room-format="STEREO_360_TB">

  <!-- Contents -->
    <dl8-model poster="model-poster.jpg" title="Model" src="model.json">
      <dl8-model-ibl src="ibl/forest.json"></dl8-model-ibl>
      <dl8-model-skybox src="forest.png"></dl8-model-skybox>
    <dl8-video title="Video 1" poster="video-poster1.jpg" format="MONO_360" x-dl8-attr-category="Music" x-dl8-attr-rating="1.0" x-dl8-attr-ts="123456778">
      <source src="video1.mp4" type="video/mp4" />
    <dl8-video title="Video 2" poster="video-poster2.jpg" format="STEREO_360_TB" x-dl8-attr-category="Animation" x-dl8-attr-rating="4.0" x-dl8-attr-ts="123456782">
      <source src="video2.mp4" type="video/mp4" />
    <dl8-video title="Video 3" poster="video-poster3.jpg" format="STEREO_360_TB" x-dl8-attr-category="Animation" x-dl8-attr-rating="4.0" x-dl8-attr-ts="123456782">
      <source src="video3.mp4" type="video/mp4" />
    <dl8-img title="Photo 1" poster="photo-poster1.jpg" src="photo1.vr.jpg" format="CARDBOARD_PHOTO" x-dl8-attr-category="Photo" x-dl8-attr-tags="[ 'sunny', 'cloudy' ]" x-dl8-attr-rating="8.0" x-dl8-attr-ts="123456781">
    <dl8-img title="Photo 2" poster="photo-poster2.jpg" src="photo2.jpg" format="MONO_360" x-dl8-attr-category="Photo" x-dl8-attr-tags="[ 'sunny', 'cloudy' ]" x-dl8-attr-rating="8.0" x-dl8-attr-ts="1234567811">
    <dl8-hub-group title="Music" poster="music-cover.jpg">
      <dl8-hub-action-filter for="main" filter="categoryMusicFilter"></dl8-hub-action-filter>
      <dl8-hub-action-set-title for="main" value="Category: Music"></dl8-hub-action-set-title>
    <dl8-hub-group title="Photo" poster="photo-cover.jpg">
      <dl8-hub-action-filter for="main" filter="categoryPhotoFilter"></dl8-hub-action-filter>
      <dl8-hub-action-set-title for="main" value="Category: Photo"></dl8-hub-action-set-title>
    <dl8-hub-group title="Animation" poster="animation-cover.jpg">
      <dl8-hub-action-filter for="main" filter="categoryAnimationFilter"></dl8-hub-action-filter>
      <dl8-hub-action-set-title for="main" value="Category: Animation"></dl8-hub-action-set-title>
    <dl8-hub-group title="All Groups" poster="all-cover.jpg">
      <dl8-hub-action-filter for="main" filter="groupFilter"></dl8-hub-action-filter>
      <dl8-hub-action-set-title for="main" value="Category: All Groups"></dl8-hub-action-set-title>

  <!-- Visual Representation -->
  <dl8-hub-vizor brand-src="brand-logo.jpg" height="0.7" spacing="0.05">
    <dl8-hub-menu width="0.3" title="Home" default-menu-item="all" rows="10">
      <dl8-hub-menu-item menu-item-id="all" title="All">
        <dl8-hub-action-filter for="main" filter="allContentFilter"></dl8-hub-action-filter>
        <dl8-hub-action-set-title for="main" value="All"></dl8-hub-action-set-title>
      <dl8-hub-menu-item title="Videos">
        <dl8-hub-action-filter for="main" filter="videosFilter"></dl8-hub-action-filter>
        <dl8-hub-action-set-title for="main" value="Videos"></dl8-hub-action-set-title>
      <dl8-hub-menu-item title="Images">
        <dl8-hub-action-filter for="main" filter="imagesFilter"></dl8-hub-action-filter>
        <dl8-hub-action-set-title for="main" value="Images"></dl8-hub-action-set-title>
      <dl8-hub-menu-item title="Music">
        <dl8-hub-action-filter for="main" filter="categoryMusicFilter"></dl8-hub-action-filter>
        <dl8-hub-action-set-title for="main" value="Music"></dl8-hub-action-set-title>
      <dl8-hub-menu-item title="Best Images">
        <dl8-hub-action-filter for="mostRecentList" filter="imagesFilter"></dl8-hub-action-filter>
        <dl8-hub-action-set-title for="mostRecentList" value="Best Images"></dl8-hub-action-set-title>
      <dl8-hub-menu-item title="Best Videos">
        <dl8-hub-action-filter for="mostRecentList" filter="videosFilter"></dl8-hub-action-filter>
        <dl8-hub-action-sort for="mostRecentList" sorter="timestampSorter"></dl8-hub-action-sort>
        <dl8-hub-action-set-title for="mostRecentList" value="Best Videos"></dl8-hub-action-set-title>
      <dl8-hub-menu-item title="Popular Videos">
        <dl8-hub-action-filter for="mostRecentList" filter="videosFilter"></dl8-hub-action-filter>
        <dl8-hub-action-sort for="mostRecentList" sorter="ratingSorter"></dl8-hub-action-sort>
        <dl8-hub-action-sort for="mostRecentList" sorter="timestampSorter"></dl8-hub-action-sort>
        <dl8-hub-action-set-title for="mostRecentList" value="Popular Videos"></dl8-hub-action-set-title>
    <dl8-hub-grid filter-list="['categoryMusicFilter','categoryAnimationFilter','categoryPhotoFilter']" view-id="main" width="1.0" scroll-mode="horizontal" columns="3" rows="3" title="Main" filter="allContentFilter" sorter="alphabeticalSorter"></dl8-hub-grid>
    <dl8-hub-grid view-id="mostRecentList" width="0.3" scroll-mode="vertical" columns="1" rows="3" title="Most Recent" filter="allContentFilter" sorter="timestampSorter" show-no-cover-text></dl8-hub-grid>

  <!-- Filters -->
    <dl8-hub-filter-attr-is filter-id="categoryMusicFilter" attr="x-dl8-attr-category" value="Music" name="Music"></dl8-hub-filter-attr-is>
    <dl8-hub-filter-attr-is filter-id="categoryAnimationFilter" attr="x-dl8-attr-category" value="Animation" name="Animation"></dl8-hub-filter-attr-is>
    <dl8-hub-filter-attr-is filter-id="categoryPhotoFilter" attr="x-dl8-attr-category" value="Photo" name="Photo"></dl8-hub-filter-attr-is>
    <dl8-hub-filter-attr-contains-one-of filter-id="tagFilterCloudyOr" attr="x-dl8-attr-tags" value="[ 'cloudy', 'sunny' ]"></dl8-hub-filter-attr-contains-one-of>
    <dl8-hub-filter-attr-contains-all-of filter-id="tagFilterCloudyAnd" attr="x-dl8-attr-tags" value="[ 'cloudy', 'sunny' ]"></dl8-hub-filter-attr-contains-all-of>
    <dl8-hub-filter-attr-contains filter-id="tagFilterSunny" attr="x-dl8-attr-tags" value="sunny"></dl8-hub-filter-attr-contains>
    <dl8-hub-filter-element-is filter-id="videosFilter" value="dl8-video"></dl8-hub-filter-element-is>
    <dl8-hub-filter-element-is filter-id="imagesFilter" value="dl8-img"></dl8-hub-filter-element-is>
    <dl8-hub-filter-element-is-one-of filter-id="groupFilter" value="[ 'dl8-hub-group' ]"></dl8-hub-filter-element-is-one-of>
    <dl8-hub-filter-element-is-one-of filter-id="allContentFilter" value="[ 'dl8-model', 'dl8-img', 'dl8-video', 'dl8-hub-group' ]"></dl8-hub-filter-element-is-one-of>
    <dl8-hub-filter-element-is filter-id="menuFilter" value="dl8-hub-menu-filter"></dl8-hub-filter-element-is>

  <!-- Sorter -->
    <dl8-hub-sorter-attr sorter-id="alphabeticalSorter" attr="title" type="lexicographical" order="ascending"></dl8-hub-sorter-attr>
    <dl8-hub-sorter-attr sorter-id="ratingSorter" attr="x-dl8-attr-rating" type="numerical" order="descending"></dl8-hub-sorter-attr>
    <dl8-hub-sorter-attr sorter-id="timestampSorter" attr="x-dl8-attr-ts" type="numerical" order="descending"></dl8-hub-sorter-attr>
    <dl8-hub-sorter-attr sorter-id="timestampSorter2" attr="x-dl8-attr-ts" type="numerical" order="ascending"></dl8-hub-sorter-attr>




The <dl8-live-video> element lets users enjoy 360°/180° and regular live streams with the UI and features tailored towards the live use case. The element works much like a regular <dl8-video> tag and builds upon all of the existing video markup and API (see the <dl8-video> documentation).

The <dl8-live-video> element works with HLS and MPEG/DASH stream sources (see adaptive streaming) and supports optional history seeking. In the future this element will become even more specialized to the live streaming use case with features like multi-cam, live chat and WebRTC support.

 <dl8-live-video title="Example Live Stream" poster="poster.jpg" author="John Doe"
             format="STEREO_180_LR" seekable>
    <source src="example-hls.m3u8" type="application/x-mpegurl" />

seekable [optional]

A Boolean attribute; if specified users are able to seek back in time as far as the live stream is recorded and kept via the seeking bar. When clicking on the LIVE button users can jump back to the live broadcast.



The <dl8-video> element is a video player that can playback all kinds of different video content in VR as well as on desktop and mobile. From an API standpoint the element tries to mirror the native video element as closely as possible. Video sources are described by child elements <source> with an URI and a type (see MDN Docs for more information)

 <dl8-video title="Example Video" author="Jane Doe" format="STEREO_180_LR"
            poster="example.jpg" display-mode="inline" loop>
    <source src="example.mp4" type="video/mp4" />
    <source src="example.webm" type="video/webm" />

format="<string>" [mandatory]

Defines the mono or stereo format the video is provided in. Delight VR supports all common stereo/mono 360/180 equirectangular and spherical formats, as well as 2D flat video and stereoscopic 3D flat video.

  • STEREO_180_LR: A 180 degree stereo equirectangular mapping, left and right eye being side by side.
  • STEREO_180_LR_SPHERICAL: A 180 degree stereo spherical mapping, left and right eye being side by side.
  • STEREO_180_TB: A 180 degree stereo equirectangular mapping, left and right eye being on top and bottom respectively.
  • STEREO_180_TB_SPHERICAL: A 180 degree stereo spherical mapping, left and right eye being on top and bottom respectively.
  • STEREO_360_TB: A 360 degree stereo equirectangular mapping, left and right eye being on top and bottom respectively.
  • STEREO_360_LR: A 360 degree stereo equirectangular mapping, left and right eye being side by side.
  • MONO_360: A 360 degree mono equirectangular mapping.
  • MONO_FLAT: A monoscopic 2D flat video.
  • STEREO_FLAT_LR: A stereoscopic 3D flat video, left and right eye being side by side. The pixel aspect ratio follows the video aspect ratio.
  • STEREO_FLAT_LR_SQUARE: A stereoscopic 3D flat video, left and right eye being side by side. The pixel aspect ratio is square (1:1).
  • STEREO_FLAT_TB: A stereoscopic 3D flat video, left and right eye being on top and bottom respectively. The pixel aspect ratio follows the video aspect ratio.
  • STEREO_FLAT_TB_SQUARE: A stereoscopic 3D flat video, left and right eye being on top and bottom respectively. The pixel aspect ratio is square (1:1).

On top of that Delight VR supports custom fisheye distortions. To configure these custom distortions you first have to define the format with the following meta tag:

 <meta name="dl8-custom-format" content='{"name": "MY_CUSTOM_FORMAT","base":"STEREO_FISHEYE_LR","params":{"fov": 180, "magnification": 1.0 }}'>

The name then can be used on all relevant elements (format=”MY_CUSTOM_FORMAT”). The “base” attribute controls where the custom format extends its base functionality from. At the time of writing “STEREO_FISHEYE_LR” is the only custom base format. The most common use case is VR180 camera output (two side-by-side mounted wide-angle fisheye lenses) from live streaming cameras that output the raw feed and don’t encode the video in equirectangular. The base format has “fov” (field of view) and “magnification” as params.

loop [optional]

A Boolean attribute; if specified, we will, upon reaching the end of the video, automatically seek back to the start.

disable-3d-hud [optional]

A Boolean attribute; if specified the 3d hud for controlling videos in VR is disabled.

muted [optional]

A Boolean attribute which indicates the default setting of the audio contained in the video. If set, the audio will be initially silenced. Its default value is false, meaning that the audio will be played when the video is played. Note that this attribute is deliberately unsupported on iOS devices. See Safari Developer Library for further details.

preload="<auto|metadata|none>" [optional]

Setting this attribute to “auto” (the default) informs the browser that video data should be prefetched when the page loads so that an optimal playback experience can be guaranteed once the user hits play. Note that this is only a preload hint for the browser. Each browser will interpret this hint as it sees fit. Some mobile browsers for example won’t prefetch any data to save bandwidth.

Setting to “metadata” only prefetches important metadata like duration and dimensions but not the actual video content.

Setting the attribute to “none” won’t prefetch any data before hitting play. Choose this option if minimum server traffic as well as conservation of your users bandwidth is more important in your use case than an optimal playback experience.

fps="<number>" [optional]

Specifies the frames per second that the video should playback. To avoid over- or undersampling please set this to the framerate of the video file. Setting a lower fps value may improve playback performance. The default is 30.

crossorigin="<string>" [optional]

Specify the CORS settings for the video. See the MDN documentation for more information on possible values and implications.

cors-fallback-url="<URL>" [optional]

Add this attribute with an URL as a value to the dl8-video element if your content is hosted on a different server but you want to fallback in case the users browser doesn’t support cross origin video (IE, Safari). Once the player encounters the CORS issue it will automatically redirect to that fallback URL and auto-open the Delight VR player on there. Therefore the URL should point to a special page where Delight VR is embedded and the videos are hosted from the same domain, if possible. If you don’t specify this attribute the user will just be presented with a message that CORS Video is not supported on Safari/IE. See the video caveats section for more information on CORS issues with videos.

cors-fallback-redirect-confirmation [optional]

This attribute determines what happens if the CORS fallback is triggered. If the boolean attribute “cors-fallback-redirect-confirmation” is set a confirmation prompt will be shown to the user asking him to confirm the redirect to the fallback URL. If nothing is set (the default) or the attribute is set to false the user will be automatically redirected.

cors-fallback-no-autostart [optional]

You can prevent autostart of the CORS fallback source by setting this attribute.

Progressive Streaming

Quality Selection

To define multiple qualities for your video you can add sources with different encodings and dimensions to the video element and flag them with the attribute “quality” like so:

 <dl8-video format="STEREO_180_LR">
    <source src="example-4k.mp4" type="video/mp4" quality="4k" />
    <source src="example-4k.webm" type="video/webm" quality="4k" />
    <source src="example-1080p.mp4" type="video/mp4" quality="1080p" />
    <source src="example-720p.mp4" type="video/mp4" quality="720p" />

The quality strings are freely definable and appear directly in the UI. Note that you can have multiple alternative sources for one quality like the “4k” quality in the example above. Also note that the order of qualities in the UI is dictated by the order of sources inside the video element; with the first source in the video element with its respective quality selected by default.

Additionally you can specify the “fps” and the “format” attributes for each source individually.

Desired Formats and Encoding

In general all notes on compatibility from regular HTML5 video elements also apply for the dl8-video element since it uses the in-built element internally (see for more information).


Regarding video encoding consider using the h.264 and webm video codecs because of their wide reaching support across browsers. For maximum compatibility provide both variants.


For maximum mobile compatibility you should provide videos with a maximum resolution of 1920×1080. In general it is wise to use dimensions that are power of two and don’t have extremely stretched aspect ratios (i.e. 2048×2048 for stereo over/under, or 2048×1024 for mono 360).

Adaptive Streaming

To use adaptive streaming you must provide at least one adaptive streaming <source> element on your <dl8-video> or <dl8-cinema> element. Depending on whether you want to use MPEG-DASH or HLS you would add different sources pointing to the manifest files:

For MPEG-DASH you provide an .mpd manifest file and set the type attribute to “application/dash+xml”.

For example:

<source src="path/to/your/dash.mpd" type="application/dash+xml" />

For HLS you provide an .m3u8 HLS playlist file and set the type attribute to “application/x-mpegurl”

For example:

<source src="path/to/your/hls.m3u8" type="application/x-mpegurl" />

We recommend to use MPEG-DASH and HLS streams if you want to go for maximum compatibility. Especially iOS/Safari do not support MPEG-DASH as of writing. Therefore you can add an HLS and MPEG-DASH source.

Be aware that the Cross-Origin issues on Safari and IE apply for adaptive streaming sources as well. (See also: Cross origin fallback).

Complete example (If you want to support both MPEG-DASH and HLS):

 <dl8-video format="STEREO_180_LR">
    <source src="path/to/your/dash.mpd" type="application/dash+xml" />
    <source src="path/to/your/hls.m3u8" type="application/x-mpegurl" />

Spatial Audio

To provide spatial audio playback in VR Delight VR supports the AmbiX Ambisonics format. AmbiX is based on first order ambisonics (FOA) which stores four spherical harmonics coefficients into 4 audio channels of a 5.1 audio source. For more information on how to produce, author and encode VR video with AmbiX audio please refer to our feature page.

To enable spatial audio via AmbiX just add the audio-format=”ambix” parameter to your AmbiX video source.

For example:

<dl8-video format="STEREO_360_TB">
    <source src="path/to/your/ambisonics-video.mp4" type="video/mp4" audio-format="ambix" />
    <source src="path/to/your/fallback-video-for-ios.mp4" type="video/mp4" />

Note that currently iOS is not able to decode video sources with multi-channel audio. If you want maximum compatibility between all platforms you therefore have to provide a fallback video source with a regular stereo track.

Seeking Preview Thumbnails

Delight VR is able to show preview thumbnails when the user hovers over the seeking bar on mobile, desktop as well as in VR. These give the user a rough overview of the content and can avoid unnecessary seeking. The player supports setting up preview thumbnails through sprite sheets as <dl8-video> sub elements.

Creating Preview Thumbnails

The first step is to setup a proper workflow for generating preview thumbnails from your videos. This is either done by your existing encoding service or manually by you.

Spritesheet Markup

After generating the thumbnails use declarative markup inside your <dl8-video> element to enable seeking previews. We use sprite sheets (a grid layout of multiple preview thumbnails) to save bandwidth and increase performance. Additionally to support longer video content each video can have multiple sprite sheets. To simplify the markup it is assumed that each thumbnail is sampled equidistant from the video. Here is a complete sample markup that uses three 5×5 sprite sheets that have an aspect ratio of 16 by 9:

<dl8-video format="STEREO_360_TB">
    <source src="example.mp4" type="video/mp4" />
    <dl8-video-preview-sprite frame-aspect="16:9">
        <dl8-video-preview-sprite-sheet src="S0.jpg" columns="5" rows="5" frames="25"></dl8-video-preview-sprite-sheet>
        <dl8-video-preview-sprite-sheet src="S1.jpg" columns="5" rows="5" frames="25"></dl8-video-preview-sprite-sheet>
        <dl8-video-preview-sprite-sheet src="S2.jpg" columns="5" rows="5" frames="11"></dl8-video-preview-sprite-sheet>

The default frame-aspect is “16:9”. The values colums=”5″, rows=”5″ and frames=”25″ are also the defaults. Note that columns*rows doesn’t have to equal frames. This enables sprite sheets that aren’t fully filled. This is especially important at the end of a thumbnail sequence.
Of course you can just use single thumbnail images instead of sprite sheets. For this you would need to set colums=”1″, rows=”1″ and frames=”1″ for each thumbnail.


Media Events

We offer an event API that is similar to the media events API ( of the native HTML5 video element. To register for these events you need to call addEventListener on an <dl8-video> element that you’ve queried beforehand.

For example:

var element = document.querySelector("dl8-video") 
element.addEventListener('play', function() { console.log("play") })

Programmatic Actions

To control video playback via Javascript Delight VR offers the following APIs on the element.

Start playback:

var element = document.querySelector("dl8-video")

Pause playback:

var element = document.querySelector("dl8-video") 

Seek video:

var element = document.querySelector("dl8-video") // t is a number that represents the seeking time in seconds

Playback Info

Furthermore we export playback status properties from the underlying video element that help you programmatically control the playback flow in your application.

Get the video duration:

var element = document.querySelector("dl8-video") 
console.log("duration", element.duration)

Get the current playback time:

var element = document.querySelector("dl8-video") 
console.log("currentTime", element.currentTime)


  • Imprecise tracking on Android: On Android mobile chrome browsers < 50 users have to navigate to chrome://flags/#enable-experimental-web-platform-features and enable that setting to work around a bug in Android Chrome < 50, which had issues with tracking precise head orientations based on gyro data.
  • Cross origin issue on Safari and IE: Videos do not work on iOS and OSX Safari as well as Internet Explorer when the media file ist hosted on a different domain. See this post on Apple Developer Forums for a detailed discussion. We currently support a custom specified fallback URL that is presented to the user in that case. See the “cors-fallback-url” description for more details.
  • h.264 and Chromium: Currently the special WebVR enabled Chromium build that can be downloaded here can’t playback .mp4 videos with h.264 encoding. This is due to licensing issues with the codec and the free open source Chromium project. This issue will be resolved once WebVR lands in Chrome. For now try to always provide .webm encoded videos as alternative sources with your video elements as they will playback in Chromium.
  • NPOT video textures in Chrome/Android: Currently there is an issue with video textures in Chrome on Android that are non power of two. Here video data is not decoded/sampled correctly which results in low resolution and/or pixelated appearance. For maximum compatibility and best VRAM usage try to keep video resolution at power of two sizes like 2048×2048 or 4096×2048.
  • IE11 video resolution: The h.264 video decoder used in Internet Explorer 11 decodes a maximum resolution of 1920×1088 pixels. Videos bigger than that throw an error.

Beta 2 - Feature Update

Beta 2 - Feature Update

Published on: Jun 13, 2016

Get ready for the next round: Beta 2 released.

Check out what’s new:

White-label support:

Starting with Beta 2, the white-label option drastically improves how you can integrate Delight VR seamlessly into your corporate identity! In addition to custom colors it is now possible to change the logo, watermark, brand url and player name to your liking!

Video Quality switching:

Multiple video sources with freely selectable quality configurations and quality names are now possible! It’s as easy as adding new video sources with the “quality” attribute to the Delight VR Video element. Make the best out of your user’s available bandwidth and raise the bar on compatibility by defining multiple qualities today!

Native "Cardboard Camera" Image format support:

Did you know that you can take amazing stereoscopic panoramic images using Google’s Cardboard Camera app on Android? Try it out! And better yet: Now you can directly share those images on your website by using the new Delight VR Image format “CARDBOARD_PHOTO” and the image from your phone as a source.

And it doesn’t stop there: With the upcoming release of the Delight VR Tour you can transparently use these panoramic images to define your own 360° 3D stereoscopic tours without investing in expensive hardware!

Better iOS/Safari Video support:

Delight VR now has better support for audio on iOS. No extra fallback solutions for iOS required: Just use the same video source for all platforms using Delight VR and let your users enjoy a truly cross-platform compatible experience without annoying pop-ups or caveats. Delight VR also informs your users about potential CORS problems with Safari and let’s you specify fallback-urls to minimize friction for your customers.

Controller Support:

Preliminary support for external controllers such as the HTC Vive’s or Gamepads has been added and they can now be used to interact with content in Delight VR. We continue our mission towards being truly headset and input-device agnostic! Stay tuned for more exciting news on how to interact with content in Delight VR.

Delight VR Beta Launch

Delight VR Beta Launch

Published on: May 10, 2016

Say hello: Delight VR Beta Launch

As of today you can try out DELIGHT Engine with our newest product Delight VR.
Delight VR enables you to smoothly integrate a multitude of VR content like 360 Video, Panoramic Images and 3D Models with your website using declarative HTML only.

We’re very excited about the product and invite you to check it out!