Jump to content

User Manual: VidPly Player

A practical guide for web developers on integrating VidPly into their websites.

VidPly Logo

Brief overview

VidPly is a universal, accessible media player that supports the following:

FeaturesSupport
VideoMP4, WebM, OGG
AudioMP3, OGG, WAV
YouTubeEmbedded with standardised controls
VimeoEmbedded with standardised controls
SoundCloudEmbedded via the Widget API with standardised controls
HLSAdaptive streaming with variable bitrate (hls.js in most browsers, natively on iOS / iPadOS)
DASHMPEG-DASH streaming via dash.js
Buffer loading indicatorCentred loading spinner whilst waiting/searching
Download buttonOptional controls with support for custom URLs
PlaylistsAudio, video and mixed media with thumbnails
AccessibilityWCAG 2.2 AA compliant
TypeScriptStrict TS source files + bundled .d.ts declarations
LanguagesEN, ES, FR, DE, JA + custom

Installation

1. Create the player

 
npm install
npm run build
 

This will create files in the dist/:

  • prod/vidply.esm.min.js - ES module (recommended)
  • legacy/vidply.min.js - IIFE for the script tag (global VidPly)
  • vidply.min.css - Styles

2. Embed it into your page

ES module (recommended):

 
<link rel="stylesheet" href="dist/vidply.min.css">
<script type="module">
  import Player from './dist/prod/vidply.esm.min.js';
</script>
 

Traditional script tag:

 
<link rel="stylesheet" href="dist/vidply.min.css">
<script src="dist/legacy/vidply.min.js"></script>
 

Basic usage

Video player

 
<video data-vidply width="800" height="450">
  <source src="video.mp4" type="video/mp4">
  <source src="video.webm" type="video/webm">
</video>
 

The data-vidply attribute automatically initialises the player.

Audio player

 
<audio data-vidply>
  <source src="audio.mp3" type="audio/mpeg">
  <source src="audio.ogg" type="audio/ogg">
</audio>
 

With poster image

 
<video data-vidply poster="thumbnail.jpg" width="800" height="450">
  <source src="video.mp4" type="video/mp4">
</video>
 

External services

YouTube

 
<video data-vidply src="https://www.youtube.com/watch?v=VIDEO_ID"></video>
 

Or with the full URL:

 
<video data-vidply src="https://youtu.be/VIDEO_ID"></video>
 

Vimeo

 
<video data-vidply src="https://vimeo.com/VIDEO_ID"></video>
 

SoundCloud

 
<audio data-vidply src="https://soundcloud.com/artist/track-name"></audio>
 

The SoundCloud widget API is automatically detected for any URL that soundcloud.com. The widget is loaded on demand and can be integrated with VidPly’s standard controls for play, pause, seek and volume.

When used mpc-vidply in TYPO3, the privacy layer displays a GDPR consent overlay before the SoundCloud iframe is loaded – visitors must first consent to the loading of external content.

HLS streaming

 
<video data-vidply src="https://example.com/stream.m3u8"></video>
 

hls.js 1.6.16 is loaded on demand when .m3u8 URLs are detected (CDN fallback if not already present on the page). Behaviour varies by platform:

PlatformHLS engineSubtitles / Transcript / Quality
Chrome / Firefox / Edgehls.jsFull VidPly user interface
Desktop version of Safari on macOShls.js (for compatibility with other browsers)Full VidPly user interface
iOS / iPadOS SafariNative <video> HLSFull VidPly user interface via the native TextTrack API bridge
 

Even on iOS, where hls.js cannot be run (no MSE), VidPly still integrates the subtitle menu, the interactive transcript and the quality menu into the browser’s native HLS text tracks – you won’t lose any UI controls.

 

DASH streaming

 
<video data-vidply src="https://example.com/manifest.mpd"></video>
 

dash.js 5.2.0 (modern UMD) is loaded on demand as soon as .mpd URLs are detected. DASH streams support:

  • Quality selection with adaptive bitrate
  • TTML subtitles (rendered natively by dash.js)
  • WebVTT subtitles (processed by VidPly’s subtitle system with transcript support)

DASH + HLS + MP4 fallback

 
<video data-vidply width="800" height="450">
  <source src="dash/manifest.mpd" type="application/dash+xml">
  <source src="hls/master.m3u8" type="application/x-mpegURL">
  <source src="fallback.mp4" type="video/mp4">
  <track kind="subtitles" src="subtitles.en.vtt" srclang="en" label="English">
</video>
 

Adding subtitles

Simple subtitles

 
<video data-vidply width="800" height="450">
  <source src="video.mp4" type="video/mp4">
  <track kind="subtitles" src="captions-en.vtt" srclang="en" label="English">
  <track kind="subtitles" src="captions-de.vtt" srclang="de" label="Deutsch">
</video>
 

Track types

TypePurpose
subtitlesTranslation of dialogue
captionsDialogue + sound effects (for the deaf/hard of hearing)
descriptionsText descriptions of visual content
chaptersChapter markers for navigation

Subtitles enabled by default

 
<track kind="subtitles" src="captions.vtt" srclang="en" label="English" default>
 

Chapter

Add chapter navigation using a chapter track:

 
<video data-vidply>
  <source src="video.mp4" type="video/mp4">
  <track kind="chapters" src="chapters.vtt" srclang="en">
</video>
 

chapters.vtt:

 
WEBVTT

00:00:00.000 --> 00:02:30.000
Introduction

00:02:30.000 --> 00:08:00.000
Main Topic

00:08:00.000 --> 00:12:00.000
Examples

00:12:00.000 --> 00:15:00.000
Conclusion
 

Configuration options

About data attributes

 
<video 
  data-vidply
  data-vidply-autoplay="false"
  data-vidply-loop="false"
  data-vidply-muted="false"
  data-vidply-volume="0.8"
  data-vidply-language="en"
  data-vidply-keyboard="true"
  data-vidply-responsive="true"
  src="video.mp4">
</video>
 

About JavaScript

 
const player = new Player('#my-video', {
  // Display
  width: 800,
  height: 450,
  poster: 'poster.jpg',
  responsive: true,
  
  // Playback
  autoplay: false,
  loop: false,
  muted: false,
  volume: 0.8,
  playbackSpeed: 1.0,
  
  // Controls
  controls: true,
  hideControlsDelay: 3000,
  
  // Buttons (show/hide individual controls)
  playPauseButton: true,
  progressBar: true,
  volumeControl: true,
  speedButton: true,
  captionsButton: true,
  fullscreenButton: true,
  pipButton: true,
  downloadButton: false,        // Show a download button in the control bar
  downloadUrl: null,            // Optional explicit download URL (defaults to current src)

  // Custom Floating Player (in-page miniplayer / "own PiP")
  floating: false,                          // Enable the custom floating player; also disables native browser PiP
  floatingPosition: 'bottom-right',         // 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'
  floatingMinViewportWidth: 768,            // Floating feature is hidden below this viewport width (px)

  // Language
  language: 'en',
  
  // Keyboard
  keyboard: true,
  
  // Accessibility
  screenReaderAnnouncements: true,
  focusHighlight: true
});
 

Overview of all options

OptionTypeDefaultDescription
widthNumber800Player’s width
heightNumber450Player height
posterStringnullURL of the poster image
responsivebooltrueResponsive resizing
autoplayboolfalseStart playback automatically
loopboolfalseLoop playback
mutedboolfalseStart muted
volumeNumber0.8Default volume (0–1)
playbackSpeedNumber1.0Default speed
controlsbooltrueShow controls
hideControlsDelayNumber3000Delay before automatic hiding (ms)
keyboardbooltrueEnable keyboard shortcut
languageString'en'User interface language
captionsbooltrueEnable subtitle support
captionsDefaultboolfalseShow subtitles by default
transcriptboolfalseShow subtitle window
debugboolfalseDebug logging
preloadString'metadata''none', 'metadata', 'auto'
deferLoadboolfalseAvoid starting the network loading process during initialisation; only load when the user launches the application or upon an explicit load command
initialDurationNumber0Initial duration in seconds (UI only, before the media metadata is loaded)
requirePlaybackForAccessibilityTogglesboolfalseIf ‘true’: AD/SL displays a prompt before playback, rather than implicitly starting the download and playing it

Download button

Displays an optional download button in the control bar:

 
<video
  data-vidply
  data-vidply-download-button="true"
  data-vidply-download-url="/files/lecture.mp4"
  src="/streams/lecture/manifest.mpd">
</video>
 
const player = new Player('#video', {
  downloadButton: true,
  downloadUrl: '/files/lecture.mp4' // optional; falls back to current src
});
 

The button is accessible via the keyboard, is fully internationalised (player.download translation keys) and uses aria-label so that screen readers announce it correctly.

 

Note for streaming sources: downloadUrl is usually the URL of an MP4/MP3 fallback, as users generally do not .mpd or .m3u8 manifest as a single file.

 

Custom floating player (mini-player)

VidPly includes an optional, in-page floating player (‘custom PiP’) which appears in a configurable corner of the viewport, can be dragged and resized, and retains VidPly’s own subtitle display, controls and full-screen mode within the floating container.

As long as floating it is enabled, the browser’s native Picture-in-Picture API is automatically suppressed (disablepictureinpicture + disableremoteplayback) , ensuring a consistent experience for the user across browsers.

Behaviour

  • Automatic display on scrolling out – When the original player is scrolled out of the viewport, the video appears in the floating container. When it is scrolled back in, the video is docked back to its original container.
  • Manual docking/undocking – The PiP button in the control bar can be used to manually dock the floating container. Manual docking/undocking takes precedence over scroll-based automatic docking until the next user-triggered play.
  • Close button – The X button in the floating window pauses the video, returns it to its original container and disables automatic floating for the remainder of the playback session.
  • Drag and resize – The floating window can be dragged by its header and resized using the corner or edge handles. The geometry is retained per player via local storage.
  • Streamlined control bar – Only the essential controls are displayed within the floating window: Play/Pause, Rewind, Fast-forward, Volume, Subtitles, PiP and Full Screen. Tooltips appear above the buttons; the size of the subtitles is 90% and limited to 95% width.
  • Audio players are skipped – The floating function applies only to <video> players.
  • By default, only on the desktop – Below floatingMinViewportWidth (default 768 px), the function is disabled and the PiP floating button is hidden in the main control bar (it never appears in the overflow menu).

Enabling via HTML (data-vidply-options JSON)

The floating options are controlled via the data-vidply-options JSON blob (the same channel used by TYPO3 / mpc_vidply uses):

 
<video
  data-vidply
  data-vidply-options='{"floating": true, "floatingPosition": "bottom-right", "floatingMinViewportWidth": 768}'
  src="video.mp4"
  width="800" height="450">
  <track kind="subtitles" src="captions.vtt" srclang="en" label="English">
</video>
 

Enabling via JavaScript

 
const player = new Player('#video', {
  floating: true,
  floatingPosition: 'bottom-right', // or 'bottom-left' | 'top-right' | 'top-left'
  floatingMinViewportWidth: 768
});
 

Accessibility

  • The floating shell is displayed as role="dialog" with a translated aria-label (player.floatingPlayerDialog).
  • The PiP toggle uses aria-pressed to indicate the pinned status.
  • The focus returns to the original button when the floating button is closed using the keyboard.
  • All built-in languages (en, de, es, fr, ja) contain translations for player.floatingPlayer, player.floatingPlayerClose, player.floatingPlayerEnter, player.floatingPlayerExit and player.floatingPlayerDialog.

Buffering Spinner

Whilst buffering the media (waiting / seeking / initial loadstart) and disappears when canplay / playing. It works for HTML5, HLS and DASH renderers and takes into account prefers-reduced-motion.

You can customise the design using CSS variables:

 
.vidply-player {
  --vidply-spinner-color: #ffffff;
  --vidply-spinner-size: 56px;
  --vidply-z-buffering: 1; /* sits below the play overlay */
}
 

During the loading process, the container provides a .vidply-buffering class that you can use for styling or to integrate hooks:

 
.vidply-player.vidply-buffering .my-skin-overlay {
  opacity: 0.3;
}
 

Playlists

Audio playlist

 
<div id="audio-player"></div>

<script type="module">
  import { Player, PlaylistManager } from './dist/prod/vidply.esm.min.js';
  
  // When you pass a non-media element, VidPly will create the media element for you.
  const player = new Player('#audio-player', { mediaType: 'audio' });
  const playlist = new PlaylistManager(player, {
    autoAdvance: true,
    loop: false,
    showPanel: true
  });
  
  playlist.loadPlaylist([
    {
      src: 'track1.mp3',
      title: 'Track 1',
      artist: 'Artist Name',
      poster: 'cover1.jpg'
    },
    {
      src: 'track2.mp3',
      title: 'Track 2',
      artist: 'Artist Name',
      poster: 'cover2.jpg'
    }
  ]);
</script>
 

Video playlist

 
<div id="video-player"></div>

<script type="module">
  import { Player, PlaylistManager } from './dist/prod/vidply.esm.min.js';
  
  const player = new Player('#video-player');
  const playlist = new PlaylistManager(player, {
    autoAdvance: true,
    showPanel: true
  });
  
  playlist.loadPlaylist([
    {
      src: 'video1.mp4',
      title: 'Video 1',
      poster: 'thumb1.jpg',
      tracks: [
        { src: 'captions1.vtt', kind: 'captions', srclang: 'en', label: 'English' }
      ]
    },
    {
      src: 'video2.mp4',
      title: 'Video 2',
      poster: 'thumb2.jpg'
    }
  ]);
</script>
 

Playlist options

OptionDefaultDescription
autoAdvancetrueAutomatically play the next track
autoPlayFirsttrueAutomatically play the first track loadPlaylist() (if ‘false’: the first track is loaded/selected but not played)
loopfalseRepeat playlist
showPaneltrueShow playlist window

Playlist controls

 
playlist.next()           // Next track
playlist.previous()       // Previous track
playlist.goToTrack(2)     // Jump to track index
playlist.hasNext()        // Check if next exists
playlist.hasPrevious()    // Check if previous exists
 

Accessibility features

Audio description

VidPly supports spoken audio descriptions in two ways:

  1. Described video (recommended for production) – Upload or link to a pre-mixed MP4/WebM file in which the commentary is already embedded in the audio track. VidPly replaces the entire video source when the user switches the audio description on or off.

  2. VTT track (advanced fallback for audio description) – if no described video is available, a kind="descriptions" WebVTT track can control the browser’s text-to-speech function: the player pauses at each cue, reads out the description, and then resumes playback. The quality depends on the browser’s voice; use described videos if broadcast-quality narration is required.

Text description markers are always available to read in the transcript window, regardless of which audio track is active. Set audioDescriptionSpeech: false to use text-only descriptions (without TTS).

Mode (audioDescriptionMode, Default auto):

ValueBehaviour
autoVideo being described, if configured; otherwise, VTT language, provided VTT descriptions are available
swapReplaces the described video only
vtt_speechVTT language only
const player = new Player('#my-video', {
  audioDescription: true,
  audioDescriptionSrc: 'video-with-description.mp4', // optional
  audioDescriptionMode: 'auto',
  audioDescriptionSpeech: true,
  audioDescriptionExtended: true
});
 

Users can toggle the subtitles using the AD button. Demo: `demo/single-player-vtt-speech.html`.

Sign language

Add an overlay for sign language interpretation:

 
const player = new Player('#my-video', {
  signLanguage: true,
  signLanguageSrc: 'sign-language.mp4',
  signLanguagePosition: 'bottom-right'
});
 

Positioning options: bottom-right, bottom-left, top-right, top-left

Interactive transcripts

 
const player = new Player('#my-video', {
  transcript: true,
  transcriptPosition: 'external',
  transcriptContainer: '#transcript-panel'
});
 

Features:

  • Click on any line to jump to that point
  • Automatic scrolling during playback
  • Searchable text
  • The window can be dragged and resized

Keyboard shortcuts

VidPly offers comprehensive keyboard control:

KeyAction
Space bar / P / KPlay/Pause
FToggle full screen
MMute/Unmute
/ Increase/decrease volume
/ Skip forwards and backwards ±10 seconds
CTurn subtitles on/off
A‘Subtitle Style’ menu
<** / **>Decrease/increase speed
SSpeed menu
Q‘Quality’ menu
JChapter menu
TShow/hide subtitles
DDrag mode (transcript/sign language)
RResize mode
HomeReset position
EscExit mode/Close menu

Custom keyboard shortcuts

 
const player = new Player('#my-video', {
  keyboardShortcuts: {
    'play-pause': [' ', 'p', 'k'],
    'seek-forward': ['ArrowRight', 'l'],
    'seek-backward': ['ArrowLeft', 'j'],
    'volume-up': ['ArrowUp'],
    'volume-down': ['ArrowDown'],
    'mute': ['m'],
    'fullscreen': ['f'],
    'captions': ['c']
  }
});
 

Design & Themes

CSS variables

 
.vidply-player {
  /* Colors */
  --vidply-primary-color: #3b82f6;
  --vidply-background: rgba(0, 0, 0, 0.8);
  --vidply-text-color: #ffffff;
  
  /* Sizing */
  --vidply-button-size: 40px;
  --vidply-icon-size: 20px;
  
  /* Spacing */
  --vidply-gap-sm: 4px;
  --vidply-gap-md: 8px;
  --vidply-gap-lg: 12px;
  
  /* Border radius */
  --vidply-radius-sm: 4px;
  --vidply-radius-md: 8px;
  --vidply-radius-lg: 12px;
  
  /* Transitions */
  --vidply-transition-fast: 150ms;
  --vidply-transition-normal: 300ms;
}
 

Custom progress bar

 
.vidply-progress-played {
  background: linear-gradient(90deg, #667eea, #764ba2);
}
 

Custom buttons

 
.vidply-button:hover {
  background: rgba(59, 130, 246, 0.2);
}

.vidply-button:focus {
  outline: 2px solid var(--vidply-primary-color);
  outline-offset: 2px;
}
 

Internationalisation

Integrated languages

  • English (en)
  • Spanish (es) – Español
  • French (fr) – Français
  • German (de) – Deutsch
  • Japanese (ja) – 日本語

Set language

 
const player = new Player('#my-video', {
  language: 'de'  // German
});
 

Detect automatically from HTML

 
<html lang="de">
  <!-- Player auto-detects German -->
</html>
 

Load custom language

About data attribute:

 
<video 
  data-vidply 
  data-vidply-language-files='{"pt": "languages/pt.json"}'
  src="video.mp4">
</video>
 

Via JavaScript (options):

 
import Player from './dist/prod/vidply.esm.min.js';

const player = new Player('#my-video', {
  language: 'pt',
  languageFiles: { pt: 'languages/pt.json' }
});
 

Language file format

languages/pt.json:

 
{
  "player": {
    "play": "Reproduzir",
    "pause": "Pausar",
    "mute": "Silenciar",
    "unmute": "Ativar som",
    "fullscreen": "Tela cheia",
    "captions": "Legendas",
    "settings": "Configurações"
  },
  "time": {
    "currentTime": "Tempo atual",
    "duration": "Duração"
  }
}
 

API Reference

Playback

 
player.play()              // Start playback
player.pause()             // Pause playback
player.stop()              // Stop and reset
player.toggle()            // Toggle play/pause
player.seek(30)            // Seek to 30 seconds
player.seekForward(10)     // Skip forward 10s
player.seekBackward(10)    // Skip backward 10s
 

Volume

 
player.setVolume(0.5)      // Set volume (0-1)
player.getVolume()         // Get volume
player.mute()              // Mute
player.unmute()            // Unmute
player.toggleMute()        // Toggle mute
 

Speed

 
player.setPlaybackSpeed(1.5)   // Set speed (0.25-2.0)
player.getPlaybackSpeed()      // Get speed
 

Full screen

 
player.enterFullscreen()   // Enter fullscreen
player.exitFullscreen()    // Exit fullscreen
player.toggleFullscreen()  // Toggle fullscreen
 

Subtitles

 
player.enableCaptions()    // Enable captions
player.disableCaptions()   // Disable captions
player.toggleCaptions()    // Toggle captions
 

Status

 
player.getCurrentTime()    // Current time in seconds
player.getDuration()       // Total duration
player.isPlaying()         // Is playing?
player.isPaused()          // Is paused?
player.isEnded()           // Has ended?
player.isMuted()           // Is muted?
player.isFullscreen()      // Is fullscreen?
 

Events

 
player.on('ready', () => {})
player.on('play', () => {})
player.on('pause', () => {})
player.on('ended', () => {})
player.on('timeupdate', (time) => {})
player.on('volumechange', (volume) => {})
player.on('fullscreenchange', (isFullscreen) => {})
player.on('captionsenabled', (track) => {})
player.on('error', (error) => {})
 

Clean-up

 
player.destroy()           // Remove player
 

Browser support

BrowsersVersion
Chrome90+
Firefox88+
Safari14+
Edge90+
iOS Safari14+
Android Chrome90+

Troubleshooting

Video won’t play

ProblemSolution
Black screenCheck that the video URL is accessible
CORS errorEnsure that the CORS headers on the server are correct
Format not supportedProvide an MP4 fallback
Autoplay blockedSet up muted: true autoplay

Subtitles are not displayed

ProblemSolution
VTT is not loadingCheck the file URL; validate the VTT syntax
CORS errorServe the VTT from the same domain or enable CORS
Incorrect encodingSave the VTT as UTF-8

YouTube/Vimeo not working

ProblemSolution
Not loadingCheck the format of the video URL
API errorEnsure that the videos can be embedded
Controls missingVidPly uses native service players

HLS is not playing

ProblemSolution
Stream is not loadingCheck whether the .m3u8 URL is accessible
CORS issuesConfigure CORS on the streaming server
Segments are failingCheck the segment URLs in the manifest

DASH is not playing

ProblemSolution
Stream is not loadingCheck that the .mpd URL is accessible
CORS issuesConfigure CORS on the streaming server
No quality levelsCheck whether MPD has multiple renditions
TTML subtitles are missingdash.js renders TTML natively; ensure that the titles are included in the manifest
Transcript not availableTTML tracks do not support transcripts; use WebVTT for transcripts

Best practice

Performance

  • ✅ Use responsive: true for fluid layouts
  • ✅ Provide poster images
  • ✅ Use suitable video resolutions
  • ✅ Compress videos for web delivery

Accessibility

  • ✅ Always provide subtitles
  • ✅ Use descriptive titles for audio tracks
  • ✅ Test usability using only the keyboard
  • ✅ Test with screen readers

Across browsers

  • ✅ Provide MP4 and WebM sources
  • ✅ Test on mobile devices
  • ✅ Take into account differences in full-screen mode on iOS

Live demos

Try VidPly in action:

Need help? Check the API documentation or create a ticket on GitHub.

Share page