RedBlue

Branching Narratives

Self-hosted

XML

Code

Result

JSON-LD

Code

Result

Detailed Instructions

For this example, we’re creating a movie that presents the viewer with a choice: Red Pill or Blue Pill. Depending on the viewer’s choice, the movie shows one of two alternate endings.

To achieve this, we have split the movie into three video segments: the Introduction, the “Red Pill” Ending, and the “Blue Pill” Ending.

We also have two images that are displayed during the choice prompts: the Choice 1 Wait Screen: Morpheus’s hands presenting a red pill and a blue pill

…and the Closing Wait Screen: The End

While these could also be implemented as video segments, since there is no motion in them, using images saves on file size and thus download overhead.

Put together, the playback sequence looks like this: Intro → Choice 1 Wait Screen → ( “Red Pill” Ending OR “Blue Pill” Ending ) → Closing Wait Screen.

With RedBlue, you don’t have to write the logic to achieve this playback sequence yourself. You simply use declarative HVML markup to specify the available choices and which files to play for each choice. The player code handles the rest.

Setup

Import the RedBlue video player Custom Element. (This requires ES6 support to work directly in the browser; otherwise the source code can be transpiled to ES5.)
<script type="module">
  import RedBlueVideo from './modules/redblue-video-compat.js';
  customElements.define( RedBlueVideo.is, RedBlueVideo );
</script>
Include an HVML code block as a child of <redblue-video>. HVML can be represented as either XML or JSON-LD. When using the XML serialization, set the boolean hidden attribute to true to prevent the browser from rendering the metadata.
<redblue-video>
  <hvml xmlns="https://hypervideo.tech/hvml#" hidden="hidden"></hvml>
</redblue-video>
In the HVML, specify a single video element. (Not to be confused with HTML’s video element—though unless your page is served as XHTML, technically the browser will interpret it as such.) This is the container for all of your video’s metadata.
<redblue-video>
  <hvml xmlns="https://hypervideo.tech/hvml#" hidden="hidden">
    <video></video>
  </hvml>
</redblue-video>
Under video, set basic metadata:
  • title
  • published, the date of first public release
  • runtime, usually an integer denoting minutes or an ISO 8601 duration, but in this case the string “variable” indicates a branching narrative whose runtime depends on choices made
  • description
<redblue-video>
  <hvml xmlns="https://hypervideo.tech/hvml#" hidden="hidden"></hvml>
    <video>
      <title>RedBlue Test</title>
      <published>2014</published>
      <runtime>variable</runtime>
      <description>Branching narratives demo</description>
    </video>
  </hvml>
</redblue-video>

Media Files

Under video, include a presentation element. This is the container for instructions telling the RedBlue Player how to present a video on playback.
<redblue-video>
  <hvml xmlns="https://hypervideo.tech/hvml#" hidden="hidden">
    <video>
      <title>RedBlue Test</title>
      <published>2014</published>
      <runtime>variable</runtime>
      <description>Branching narratives demo</description>
      <presentation></presentation>
    </video>
  </hvml>
</redblue-video>
Under presentation, and for each media file, do this:
Define a file element.
Set its label attribute to an identifier that is shared between different versions of the same media segment—but is unique amongst different media segments. For instance, for the Introduction file, you might have a WebM and an MP4 version; each of which might have a 1080p and a 720p version. So all four of these file elements could have label="intro", which allows the RedBlue Player to select the most appropriate version depending on the viewer’s browser capabilities and connection speed.
Set its xlink:href attribute to the URL of the media file.
Optional: Set its xml:id attribute to a globally unique identifier, e.g. intro-webm-1080p, so it can be linked to directly.
<redblue-video>
  <hvml xmlns="https://hypervideo.tech/hvml#" hidden="hidden">
    <video>
      <title>RedBlue Test</title>
      <published>2014</published>
      <runtime>variable</runtime>
      <description>Branching narratives demo</description>
      <presentation>
        <file label="intro" xlink:href="media/webm/intro-1080p.webm">
        <file>
        <file label="choice-1-wait-screen" xlink:href="media/bitmap/choice-1-wait-screen.jpg"><file>
        <file label="red-pill-outcome" xlink:href="media/webm/red-pill-outcome-1080p.webm"><file>
        <file label="blue-pill-outcome" xlink:href="media/webm/blue-pill-outcome-1080p.webm"><file>
        <file label="the-end-wait-screen" xlink:href="media/bitmap/the-end-wait-screen.png"><file>
      </presentation>
    </video>
  </hvml>
</redblue-video>
If a video file, specify the container format, video codec, and audio codec using their respective MIME type identifiers. If an image file, specify a bare codec element.
<redblue-video>
  <hvml xmlns="https://hypervideo.tech/hvml#" hidden="hidden">
    <video>
      <title>RedBlue Test</title>
      <published>2014</published>
      <runtime>variable</runtime>
      <description>Branching narratives demo</description>
      <presentation>
        <file label="intro" xlink:href="media/webm/intro-1080p.webm">
          <!-- video/webm; codecs=vp8,vorbis -->
          <container>
            <mime>video/webm</mime>
            <codec type="video">
              <mime>vp8</mime>
            </codec>
            <codec type="audio">
              <mime>vorbis</mime>
            </codec>
          </container>
        <file>
        <file label="choice-1-wait-screen" xlink:href="media/bitmap/choice-1-wait-screen.jpg">
          <codec>
            <mime>image/jpeg</mime>
          </codec>
        <file>
        <file label="red-pill-outcome" xlink:href="media/webm/red-pill-outcome-1080p.webm">
          <!-- … -->
        <file>
        <file label="blue-pill-outcome" xlink:href="media/webm/blue-pill-outcome-1080p.webm">
          <!-- … -->
        <file>
        <file label="the-end-wait-screen" xlink:href="media/bitmap/the-end-wait-screen.png">
          <!-- … -->
        <file>
      </presentation>
    </video>
  </hvml>
</redblue-video>

Nonlinear Playlists

In HVML, branching narratives are achieved by what is termed a nonlinear playlist. Nonlinear playlists are like regular playlists in that they specify a list of media files to be played back sequentially. But in nonlinear playlists, choice prompts can be queued in addition to media files. When the playhead encounters a choice prompt, the viewer is presented with one or more options that, when selected, redirect the playhead to different parts of the playlist. Each choice prompt can be assigned a wait screen, a background video or image to display while waiting for the viewer’s input. In our example, the Red Pill/Blue Pill choice prompt features a closeup of Morpheus’s hands as its wait screen.

When creating nonlinear playlists, there are three HVML elements to be familiar with.

1. media

A media element instructs the RedBlue Player to play a single media file. It can link to a single media file directly, or in can link to a set of media file candidates. When a media file links to a set of media file candidates, the RedBlue Player will select one to play back based on the browser features, device specs, and/or network conditions of the viewer.

The playhead traverses from one media element to the next in top-down source order (linear progression), unless it encounters an instruction that diverts it elsewhere (nonlinear progression).

To make it possible to navigate to a media element nonlinearly, set its xml:id attribute to a globally-unique ID so the playhead can be sent there.

Linking to A Single Media File

Set the media element’s xlink:href attribute to the appropriate URL.

<media
  xml:id="intro"
  xlink:href="media/webm/intro-1080p.webm"
></media>
Linking to A Set of Media File Candidates

Set the media element’s xlink:href attribute to an XPointer expression.

If you aren’t familiar with XPointer expressions, they’re like advanced URL hashes. While URL hashes can only refer to one part of a document based on ID or anchor name, XPointer hashes can refer to one or more parts of a document based on arbitrary criteria. To do this, XPointer expressions take XPath selectors as their sole parameter. XPath selectors are similar in concept to CSS selectors, but more expressive. Compare:

URL hash
https://example.com/#section-two
CSS equivalent: #section-two
XPointer hash
https://example.com/#xpointer(//section[2])
CSS equivalent: section:nth-child(2)
XPointer hash (advanced)
https://example.com/#xpointer(/section[2]/p[@class="info"])
CSS equivalent: :root > section:nth-child(2) > p[class="info"]

The following specifies that when the playhead reaches this media element, the RedBlue Player should find every file element with a label of “intro” in the current document, and then of those candidates, queue up the best fit according to browser, device, and network conditions. It might choose between different file formats, resolutions, aspect ratios, etc. (Note: In the current beta, only WebM is supported as a file format.)

<media
  xml:id="intro"
  xlink:href="#xpointer(//file[@label='intro'])"
></media>

2. choicePrompt

A choicePrompt element instructs the RedBlue Player to present the viewer with one or more options to choose from. Under it, you can provide:

  • A name element, instructing the RedBlue Player to render heading text on top of the wait screen;
  • A media element, specifying a video or image file to use as the wait screen;
  • One or more choice elements, specifying the options to present to the user.
<choicePrompt xml:id="red-pill-or-blue-pill">
  <name>What will it be, Neo?</name>
  <media
    xlink:href="#xpointer(//file[@label='choice-1-wait-screen'])"
  ></media>
  <choice>
    <!-- … -->
  </choice>
  <choice>
    <!-- … -->
  </choice>
</choicePrompt>

3. choice

A choice element instructs the RedBlue Player to render one of a set of options, usually in the form of a link. choices can be overlaid onto specific regions of the player, making them hotspots. When the viewer selects a choice, an action is performed, typically moving the playhead to another point in the playlist.

Under choice, you can provide:

  • A name element, setting the link text. Link text may be invisble if the choice represents a hotspot;
  • A goto element, instructing the RedBlue Player to follow the URL specified in its xlink:href attribute. If the URL resolves to another playlist item, the playhead will be moved there. A goto’s xlink:actuate attribute being set to “onRequest” specifies that the viewer needs to request the URL manually (here, by clicking on the link) before the player will follow the URL.
<choice xml:id="red">
  <name>Red Pill</name>
  <goto
    xlink:actuate="onRequest"
    xlink:href="#wakes-up-to-reality"
  ></goto>
</choice>

Putting It All Together

With all of our file elements set up, let’s assemble the nonlinear playlist to put them to use.

Intro

Under presentation, add a playlist element.
Set its type attribute to “nonlinear”. This attribute activates RedBlue’s interactive playback mode.
<redblue-video>
  <hvml xmlns="https://hypervideo.tech/hvml#" hidden="hidden">
    <video>
      <title>RedBlue Test</title>
      <published>2014</published>
      <runtime>variable</runtime>
      <description>Branching narratives demo</description>
      <presentation>
        <!-- <file>s -->
        <playlist type="nonlinear"></playlist>
      </presentation>
    </video>
  </hvml>
</redblue-video>
Under playlist, add a media element.
Set its xml:id attribute to a globally-unique ID so it can be independently referenced.
Set its xlink:href attribute to the XPointer expression #xpointer(//file[@label='intro']). This selects every file with a label attribute equal to “intro”.
<redblue-video>
  <hvml xmlns="https://hypervideo.tech/hvml#" hidden="hidden">
    <video>
      <title>RedBlue Test</title>
      <published>2014</published>
      <runtime>variable</runtime>
      <description>Branching narratives demo</description>
      <presentation>
        <!-- <file>s -->
        <playlist type="nonlinear">
          <media
            xml:id="intro"
            xlink:href="#xpointer(//file[@label='intro'])"
          ></media>
        </playlist>
      </presentation>
    </video>
  </hvml>
</redblue-video>
Under media, add a goto element. This sets up a playhead redirect.
Set its on attribute to durationEnd. This tells the RedBlue Player not to activate the redirect until the parent media element is done playing.
Set its xlink:href to the ID selector #red-pill-or-blue-pill. This will point to a choicePrompt element shortly.
<redblue-video>
  <hvml xmlns="https://hypervideo.tech/hvml#" hidden="hidden">
    <video>
      <title>RedBlue Test</title>
      <published>2014</published>
      <runtime>variable</runtime>
      <description>Branching narratives demo</description>
      <presentation>
        <!-- <file>s -->
        <playlist type="nonlinear">
          <media
            xml:id="intro"
            xlink:href="#xpointer(//file[@label='intro'])"
          >
            <goto
              on="durationEnd"
              xlink:href="#red-pill-or-blue-pill"
            ></goto>
          </media>
        </playlist>
      </presentation>
    </video>
  </hvml>
</redblue-video>