Videobana

Web component

Put a video on any web page with one script and one tag: <video-player>. It plays the same player as the dashboard and the gallery, with the voice-over, the captions and the controls.

Quick start

  1. 1
    Add the script
    Once per page. It is small (a few kilobytes) and only defines the tag. The player itself is downloaded when a video comes into view.
  2. 2
    Add the tag
    Point src at a video. Here it is the ad for Videobana, one of the gallery examples.
<script src="https://www.bana.video/widget.js" async></script>

<video-player src="https://www.bana.video/api/examples/ads-videobana"></video-player>

The demos on this page do not start by themselves, so the page stays quiet. Your tag does, unless you write autoplay="false".

Where the video comes from

The tag accepts the video in four ways. They can be combined with a change of mind later: set spec or src from your code and the new video replaces the old one.

<!-- A video of the platform, or any JSON file that your site or another one serves -->
<video-player src="https://www.bana.video/api/public/videos/0123456789abcdef"></video-player>
<video-player src="/videos/launch.json"></video-player>
Address or JSONWhat you get
https://www.bana.video/api/public/videos/<id>A video you made public in the dashboard. Its voice-over, timing and pictures come with it. The Share dialog has a ready-made snippet (the Web component tab).
https://www.bana.video/api/examples/<id>Any video of the gallery, with its recorded voice-over. The id is the last part of its gallery address.
A JSON file, or JSON textThe video document, as described in the JSON format. It is checked first; a problem is shown in the frame, with its place in the document, and sent as an error event.
Voice-over comes with a video of the platform
A JSON document has no recorded voice: it plays without narration, with its timing worked out from the text (music and sounds still play). Public videos and gallery examples bring their voice-over. Pictures and files in the JSON may be addresses on any site. A relative one is read from the place of the JSON file, or from the page for JSON written in it.

Options

Everything is an attribute, so it works in plain HTML, and a property of the same name, so it works from code. Yes/no options follow the HTML way: loop alone is yes, and loop="false" (or 0) is no. The last form is the only way to switch off something that is on by default, such as autoplay.

AttributePropertyDefaultWhat it does
srcsrc–The address of the video. A video of the platform, one of the gallery examples, or any JSON file.
specspec–The video itself, as JSON text. The property also takes an object. It wins over src.
captionscaptionsoffShow the subtitles. The viewer can still switch them with the button.
autoplayautoplayonStart by itself when it comes into view. autoplay="false" shows the title card with a Watch button.
looploopoffStart again when it ends.
mutedmutedoffStart without sound. The viewer can turn it on.
volumevolume1From 0 to 1.
playback-rateplaybackRate1Speed, from 0.25 to 4.
controlscontrolsallThe control bar: none, or a list of its parts. See below.
fit–widthHow the picture fills the element: width, contain or cover. See below.
aspect–the video'sShow the video in another shape: 16:9, 9:16, 1:1, 4:5 and the others the platform exports.

Controls

The bar is whole by default. controls="none" hides it (the video then plays by itself and a script can drive it), and a list keeps only some parts. The viewer's keyboard shortcuts follow the same list, and so does a click on the picture (it plays and pauses when play is there): space and K need play, the arrows need seek, M needs volume, F needs fullscreen, C needs captions.

PartWhat it is
playPlay and pause.
chaptersPrevious and next scene.
timeThe time and the length.
seekThe bar to jump around, with a mark for every scene.
captionsThe subtitles button.
speedThe playback speed button.
volumeMute, and the volume slider.
fullscreenFull screen.
<video-player src="…" captions controls="play time fullscreen"></video-player>

Autoplay and sound

Browsers refuse to start sound on a page that nobody has clicked yet. The tag tries to play with sound, and when the browser says no it plays the video muted and shows a button, Sound is off · Tap to turn it on. After the visitor has used the page (a click anywhere is enough) the next video starts with sound. If you want no sound at all, say so with muted: that is also what a background video should do.

Fit and size

The tag is a block that is as wide as its container. fit says what the picture does inside it:

fitThe pictureUse it for
widthis as wide as the element, and as tall as it needs, with the control bar under it. The element has no height of its own.Articles, landing pages, documentation.
containfits whole inside the height you give the element, with bars where the shapes differ.A fixed slot: a sidebar, a modal, a card.
coverfills the element and is cropped where the shapes differ. No control bar unless you ask for one.Hero sections and backgrounds.

With contain and cover the element is 16:9 until you give it a height (height, min-height or a grid or flex layout that sizes it). aspect shows a video in another shape than it was made for.

As a hero section

Whatever you write between the tags is put over the picture, so the tag can be the whole hero: the video fills it, and your headline and buttons stay your own HTML (indexable, translatable, styled by your CSS). Put the text in a panel or a gradient that keeps it readable over every scene.

<script src="https://www.bana.video/widget.js" async></script>

<style>
  .hero { height: 70vh; min-height: 24rem; }
  .hero-copy { align-self: center; max-width: 34rem; margin-left: 6vw; color: #fff; }
  .hero-copy h1 { font-size: clamp(2rem, 5vw, 3.5rem); line-height: 1.05; }
</style>

<video-player class="hero" src="https://www.bana.video/api/examples/ads-videobana" fit="cover" muted loop>
  <div class="hero-copy">
    <h1>Make videos from words.</h1>
    <p>Describe a topic. Get a narrated, animated video.</p>
    <a href="/signup">Start for free</a>
  </div>
</video-player>

Make videos from words.

This text is the page's own. The video plays behind it.

  • muted loop makes it a background: it starts everywhere, even where sound is refused, and never ends. Leave muted out for a hero whose voice-over you want heard; the sound button shows when the browser blocks it.
  • The slot is a grid that fills the element: align-self, justify-self and place-self on your child place it, and clicks go through to the video everywhere your content is not.
  • Only a video that is shown starts: the tag waits until it is near the viewport, so a hero below the fold costs nothing until it is scrolled to.

From JavaScript

The tag behaves like a <video>: the same names for the same things.

const player = document.querySelector("video-player");

player.addEventListener("ready", () => console.log("length:", player.duration, "seconds"));
player.addEventListener("ended", () => console.log("done"));
player.addEventListener("error", (event) => console.warn(event.detail.message));

document.querySelector("#watch").addEventListener("click", async () => {
  player.muted = false;      // a click is what lets a browser play sound
  player.currentTime = 0;
  await player.play();
});
MemberWhat it is
play() / pause()Start and pause. play() also loads the video, even when the element is not on screen.
currentTimeWhere the video is, in seconds. Set it to jump.
duration, paused, endedRead-only state. duration is NaN until the video has loaded.
muted, volume, playbackRate, loop, autoplay, captions, controlsThe options above, readable and writable.
src, specChange the video. Setting either starts the new video over.
errorWhy the video cannot be shown, or null.
EventWhen
readyThe video has been loaded. duration is now known.
playIt started or resumed, whoever caused it.
pauseIt was paused.
endedIt reached the end (with loop it starts again, and ended still fires).
volumechangeMuted or the volume changed, by the viewer or by your code.
ratechangeThe playback speed changed.
errorThe video could not be shown. event.detail.message says why.

Styling

The player lives in a shadow root, so your page's CSS cannot break it and its CSS cannot touch your page. You size and place the tag like any block, and two variables restyle it:

video-player {
  --vp-radius: 20px;       /* corners (12px when it is as wide as the page, square for contain and cover) */
  --vp-letterbox: #0b0b12; /* the bars of fit="contain" and fit="cover" */
  max-width: 960px;
  margin-inline: auto;
}

On your platform

The script goes in once, the tag goes where the video should be. Pick yours:

<!doctype html>
<html>
  <body>
    <video-player src="https://www.bana.video/api/examples/ads-videobana"></video-player>

    <!-- once per page, anywhere -->
    <script src="https://www.bana.video/widget.js" async></script>
  </body>
</html>

Good to know

  • Other sites need permission to read your JSON. The platform's script, public videos and examples answer every origin. If you host a JSON file yourself on another domain, it must send Access-Control-Allow-Origin.
  • Content-Security-Policy. Allow https://www.bana.video in script-src (the widget loads a second file from the same place), in connect-src (the video), and in img-src and media-src (pictures and the voice-over). The player's own styles are applied without <style> elements, so style-src needs nothing for them.
  • Fonts come from Google Fonts, loaded when a video needs them (the families a video uses, no others). They need style-src https://fonts.googleapis.com and font-src https://fonts.gstatic.com; a policy that blocks them gets system fonts instead.
  • Browsers: current Chrome, Edge, Firefox and Safari (custom elements, shadow DOM and ES2022).
  • The script is cached, the player is cached for good. Its file name carries a hash of its content, so a new release is picked up by the next load of https://www.bana.video/widget.js without any change on your side. Load the script from the platform's address; do not copy it.
Nothing appears?
Open the browser console. A video that cannot be shown says why in its frame and in an error event: an address that is wrong or blocked (CORS), a video that is not public, or a document that fails the checks. The script tag <script src="https://www.bana.video/widget.js" async></script> must be on the same page, and the tag must be <video-player> with a dash.

An example to start from: https://www.bana.video/api/examples/ads-videobana opens as JSON and is the payload the tag reads. To embed in a place that cannot run scripts, use the iframe from the Share dialog instead (see the HTTP API for the embed and oEmbed addresses).