
Getting started
1. Insert the CSS
<link rel="stylesheet" href="src/styles/vidply.css">
2. Add your media element
<!-- Video -->
<video data-vidply width="800" height="450">
<source src="your-video.mp4" type="video/mp4">
<track kind="subtitles" src="captions.vtt" srclang="en" label="English">
</video>
<!-- Audio -->
<audio data-vidply>
<source src="your-audio.mp3" type="audio/mpeg">
</audio>
3. Import the player
<script type="module">
import Player from './dist/prod/vidply.esm.min.js';
// Player auto-initializes elements with data-vidply attribute
</script>
For development using raw TypeScript source files (e.g. via vite or tsx):
import Player from './src/index';
const player = new Player('#video');
Common use cases
Automatic playback with sound muted
const player = new Player('#video', {
autoplay: true,
muted: true
});
Start at a specific time
const player = new Player('#video', {
startTime: 30 // Start at 30 seconds
});
Custom keyboard shortcuts
const player = new Player('#video', {
keyboardShortcuts: {
'play-pause': ['Space', 'Enter'],
'seek-forward': ['d'],
'seek-backward': ['a']
}
});
Play video on a loop
const player = new Player('#video', {
loop: true
});
Change language
Built-in languages
const player = new Player('#video', {
language: 'es' // Spanish (available: en, es, fr, de, ja)
});
Load custom language files
const player = new Player('#video', {
language: 'pt', // Portuguese
languageFiles: {
'pt': 'languages/pt.json',
'it': 'languages/it.json'
}
});
Using data attributes
<video
data-vidply
data-vidply-language-files='{"pt": "languages/pt.json", "it": "languages/it.json"}'
src="video.mp4"
></video>
Automatic detection from HTML
The player automatically detects the language based on the HTML lang :
<html lang="pt">
<video
data-vidply
data-vidply-language-file='{"pt": "languages/pt.json"}'
src="video.mp4"
></video>
</html>
Customising subtitle styling
const player = new Player('#video', {
captionsFontSize: '120%',
captionsFontFamily: 'Arial',
captionsColor: '#FFFF00',
captionsBackgroundColor: '#000000',
captionsOpacity: 0.9
});
Event handling
const player = new Player('#video');
player.on('play', () => {
console.log('Video started playing');
});
player.on('timeupdate', (currentTime) => {
console.log('Current time:', currentTime);
});
player.on('ended', () => {
console.log('Video ended');
// Redirect or show related content
});
player.on('error', (error) => {
console.error('Player error:', error);
});
Programmatic control
const player = new Player('#video');
// Play/Pause
document.getElementById('playBtn').addEventListener('click', () => {
player.play();
});
document.getElementById('pauseBtn').addEventListener('click', () => {
player.pause();
});
// Seek
document.getElementById('seek30').addEventListener('click', () => {
player.seek(30);
});
// Volume
document.getElementById('volumeSlider').addEventListener('input', (e) => {
player.setVolume(e.target.value / 100);
});
// Speed
document.getElementById('speedSelect').addEventListener('change', (e) => {
player.setPlaybackSpeed(parseFloat(e.target.value));
});
YouTube/Vimeo/SoundCloud integration
<!-- YouTube -->
<video data-vidply src="https://www.youtube.com/watch?v=dQw4w9WgXcQ"></video>
<!-- Vimeo -->
<video data-vidply src="https://vimeo.com/76979871"></video>
<!-- SoundCloud -->
<audio data-vidply src="https://soundcloud.com/artist/track-name"></audio>
<!-- These will automatically use the appropriate renderer -->
HLS streaming
<video data-vidply src="https://example.com/stream.m3u8"></video>
// Access HLS-specific features
const player = new Player('#video');
// Listen for quality levels (hls.js path)
player.on('hlsmanifestparsed', (data) => {
console.log('Available qualities:', data.levels);
});
// Listen for live cue updates (works for both hls.js and native iOS HLS)
player.on('textcuesupdate', () => {
console.log('New subtitle cues available');
});
// Switch quality manually (if using HLS renderer)
if (player.renderer.switchQuality) {
player.renderer.switchQuality(2); // Switch to quality level 2
}
On iOS/iPadOS, where MSE is not available, VidPly utilises the browser’s native HLS support, but continues to display subtitles and video quality via the same VidPly user interface using the native
TextTrackAPI bridge.
DASH streaming (MPEG-DASH)
<video data-vidply src="https://example.com/manifest.mpd"></video>
// Access DASH-specific features
const player = new Player('#video');
// Listen for quality changes
player.on('dashqualitychanged', (data) => {
console.log('Quality changed:', data);
});
// Get available qualities
if (player.renderer.getQualities) {
const qualities = player.renderer.getQualities();
console.log('Available qualities:', qualities);
}
// Switch quality manually
if (player.renderer.switchQuality) {
player.renderer.switchQuality(2); // Switch to quality level 2
}
DASH subtitle processing:
- TTML/stpp subtitles – are rendered natively by dash.js; the subtitles are styled by the stream itself. The interactive transcript is not available for TTML tracks.
- WebVTT subtitles – are processed by the VidPly subtitle system, with full support for subtitle styling and interactive transcripts.
DASH + HLS + MP4 fallback
To ensure maximum compatibility across different devices and browsers, please provide all three formats:
<video data-vidply width="800" height="450" poster="preview.jpg">
<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="vtt/subtitles.de.vtt" srclang="de" label="Deutsch">
<track kind="subtitles" src="vtt/subtitles.en.vtt" srclang="en" label="English">
</video>
VidPly automatically selects the best renderer based on the source file’s file extension.
Download button
Display a download button in the control bar so that visitors can save the media file:
<video
data-vidply
data-vidply-download-button="true"
data-vidply-download-url="/files/video.mp4"
src="/streams/video/manifest.mpd">
</video>
const player = new Player('#video', {
downloadButton: true,
downloadUrl: '/files/video.mp4' // optional; defaults to current src
});
For streaming sources (.mpd, .m3u8), it is strongly recommended that you include an explicit downloadUrl that links to a single MP4/MP3/WebM file – manifests cannot be downloaded directly.
In playlists, the file can be specified per track (downloadUrl, downloadFormat, downloadFileSize), and the button then follows the selection – see the playlist function.
Custom floating player (mini-player)
Enables the floating player within the page (“custom PiP”). If the original video is scrolled out of view, VidPly opens a movable and resizable floating overlay in the selected corner; when the original is scrolled back into view, it is docked again. The PiP button toggles manual docking/undocking. The browser’s native PiP is automatically suppressed whilst the floating player is active, ensuring a consistent experience for users.
<video
data-vidply
data-vidply-options='{"floating": true, "floatingPosition": "bottom-right", "floatingMinViewportWidth": 768}'
src="video.mp4">
<track kind="subtitles" src="en.vtt" srclang="en" label="English">
</video>
const player = new Player('#video', {
floating: true,
floatingPosition: 'bottom-right', // or 'bottom-left' | 'top-right' | 'top-left'
floatingMinViewportWidth: 768
});
Notes:
- Ignore audio-only players
floating. - Closing the floating window pauses playback and prevents it from automatically reappearing until the next user-initiated
play. - The floating overlay displays a reduced control bar (play/pause, rewind, fast-forward, volume, subtitles, PiP, full screen) and retains its size and position for each player.
- Below
floatingMinViewportWidth(by default768), the feature is disabled and the floating PiP button is hidden – it also never appears in the overflow menu.
Buffer indicator
A centred loading spinner appears automatically whilst the player is buffering (waiting, seeking, on start-up loadstart) and disappears when canplay / playing. It is enabled 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;
}
The container provides a .vidply-buffering class; you can use this via CSS:
.vidply-player.vidply-buffering .my-overlay { opacity: 0.3; }
Selecting a subtitle track
If multiple subtitle tracks are available, clicking the CC button displays a menu for selecting the language:
<video data-vidply src="video.mp4">
<track kind="captions" src="en.vtt" srclang="en" label="English">
<track kind="captions" src="es.vtt" srclang="es" label="Español">
<track kind="captions" src="fr.vtt" srclang="fr" label="Français">
<track kind="captions" src="de.vtt" srclang="de" label="Deutsch">
</video>
Switching tracks automatically:
const player = new Player('#video');
// Get available tracks
const tracks = player.captionManager.getAvailableTracks();
console.log(tracks);
// [{index: 0, language: 'en', label: 'English', kind: 'captions'}, ...]
// Switch to specific track
player.captionManager.switchTrack(1); // Switch to Spanish
// Or by finding the track you want
const frenchTrack = tracks.find(t => t.language === 'fr');
if (frenchTrack) {
player.captionManager.switchTrack(frenchTrack.index);
}
Interactive transcript
Display a clickable, scrollable transcript alongside your video, with drag-and-drop and resizing functions:
<video
data-vidply
data-transcript="true"
data-transcript-button="true"
src="video.mp4"
>
<track kind="captions" src="captions.vtt" srclang="en" label="English">
</video>
const player = new Player('#video', {
transcript: true,
transcriptButton: true
});
// Show/Hide Transcript
player.transcriptManager.showTranscript();
player.transcriptManager.hideTranscript();
player.transcriptManager.toggleTranscript();
// Drag & Resize Modes (Desktop only, screen width >= 768px)
player.transcriptManager.toggleKeyboardDragMode(); // Toggle drag mode (D key)
player.transcriptManager.togglePointerResizeMode(); // Toggle resize mode (R key)
// Check State
if (player.transcriptManager.isVisible) {
console.log('Transcript is showing');
}
Keyboard shortcuts:
- T – Show/hide transcript window
- D – Toggle drag mode on/off (move using the arrow keys)
- R – Toggle resize mode on/off (adjust size using the arrow keys)
- Home – Reset position to centre
- Esc – Exit drag/resize mode
Settings menu: The transcript window contains a settings menu (⚙️ icon) with the following options:
- Enable/disable drag mode
- Enable/disable resize mode
- Close the transcript window
You can find the full documentation in the file ‘Interactive Transcript Function’.
Video overlay for sign language
Display a video of a sign language interpreter synchronised with the main video:
Single sign language video
<video
data-vidply
src="main-video.mp4"
data-sign-language-src="sign-language-video.mp4"
data-sign-language-position="bottom-right"
>
</video>
const player = new Player('#video', {
signLanguageSrc: 'path/to/sign-language-video.mp4',
signLanguageButton: true,
signLanguagePosition: 'bottom-right' // Options: 'bottom-right', 'bottom-left', 'top-right', 'top-left'
});
// Control programmatically
player.enableSignLanguage(); // Show sign language video
player.disableSignLanguage(); // Hide sign language video
player.toggleSignLanguage(); // Toggle visibility
// Check state
if (player.state.signLanguageEnabled) {
console.log('Sign language is enabled');
}
Multiple sign language videos (language switching)
You can provide multiple sign language videos for different languages. The player automatically displays a language selection when multiple sources are available:
<video
data-vidply
src="main-video.mp4"
data-sign-language-src-en="sign-language-en.mp4"
data-sign-language-src-de="sign-language-de.mp4"
data-sign-language-src-es="sign-language-es.mp4"
data-sign-language-position="bottom-right"
>
</video>
const player = new Player('#video', {
signLanguageSources: {
en: 'path/to/sign-language-en.mp4',
de: 'path/to/sign-language-de.mp4',
es: 'path/to/sign-language-es.mp4'
},
signLanguageButton: true,
signLanguagePosition: 'bottom-right'
});
// Switch sign language programmatically
player.switchSignLanguage('de'); // Switch to German sign language
When multiple sign language sources are available:
- A language selection menu appears in the header of the sign language video
- The sign language video switches automatically when the subtitles change (provided the language codes match)
- Users can switch the language manually using the selection menu
‘Sign Language Settings’ menu
The sign language video contains a settings menu (⚙️ icon) with the following options:
- Enable/disable drag mode – Toggle drag mode using the keyboard (keyboard shortcut: D key)
- Enable/disable resize mode – Turn resize mode on or off to adjust the video size (keyboard shortcut: ‘R’ key)
- Language selection – Switch between the available sign language videos (if there are several)
- Close menu – Close the settings menu
Sign language features:
- Automatic synchronisation with the main video’s playback
- Adjusts the playback speed to match the main video
- Muted by default (the main video’s audio is used)
- Displayed as an overlay on top of the main video
- Can be moved and resized (desktop only, >= 768px)
- Supports keyboard navigation:
- D – Toggle drag mode (move using the arrow keys)
- R – Toggle resize mode (resize using the arrow keys)
- Home – Reset position
- Esc – Exit drag/resize mode
- Includes a settings menu for dragging, resizing, language switching and closing options
- Automatically switches the language when the subtitle language changes (provided a suitable sign language is available)
Audio description
VidPly supports three complementary audio description channels:
| Channel | Usage | Behaviour |
|---|---|---|
| Described video is replaced | audioDescriptionSrc or <source data-desc-src> | Replaces the video with a pre-mixed, described MP4/WebM file; the playback position is retained |
| VTT voice-over (advanced subtitles) | kind="descriptions" Track, no captioned video | Pauses the video, reads the cue text speechSynthesis, and resumes playback as soon as the voice output ends |
| Text descriptions | Descriptions: VTT always available | Displayed in the transcript window; toggle via track mode when TTS is disabled |
Mode resolution (audioDescriptionMode, default auto):
auto— Switch to the descriptive video, if configured; otherwise, the VTT language, provided a description track is availableswap— descriptive video onlyvtt_speech— VTT audio only (URL for captioned video is ignored)
Switch to captioned video
<video
data-vidply
src="regular-version.mp4"
data-audio-description-src="described-version.mp4"
data-audio-description-button="true"
>
</video>
Or with <source> elements:
<source src="regular.mp4" type="video/mp4"
data-desc-src="described.mp4" data-orig-src="regular.mp4">
VTT audio track (extended AD)
No captioned video file – just a WebVTT track with captions:
<video
data-vidply
data-audio-description-mode="vtt_speech"
data-audio-description-button="true"
>
<source src="video.mp4" type="video/mp4">
<track kind="descriptions" src="descriptions-en.vtt" srclang="en" label="Descriptions">
</video>
See also: `demo/single-player-vtt-speech.html`.
JavaScript API
const player = new Player('#video', {
audioDescriptionSrc: 'path/to/described-version.mp4', // optional
audioDescriptionButton: true,
audioDescriptionMode: 'auto', // 'auto' | 'swap' | 'vtt_speech'
audioDescriptionSpeech: true, // false = text-only (transcript / track toggle)
audioDescriptionExtended: true // resume after TTS ends, not at cue.endTime
});
// Control programmatically
await player.enableAudioDescription();
await player.disableAudioDescription();
await player.toggleAudioDescription();
// Check state
if (player.state.audioDescriptionEnabled) {
console.log('Audio description is active');
}
// Events — swap mode
player.on('audiodescriptionenabled', () => {
console.log('Audio description enabled');
});
player.on('audiodescriptiondisabled', () => {
console.log('Audio description disabled');
});
// Events — VTT speech mode (per cue)
player.on('audiodescriptioncuestart', ({ time, text, cue }) => {
console.log('Speaking description at', time, text);
});
player.on('audiodescriptioncueend', ({ time, text }) => {
console.log('Finished description at', time);
});
HTML data attributes (CamelCase in data-vidply-*):
data-audio-description-srcdata-audio-description-buttondata-audio-description-mode—auto,swaporvtt_speechdata-audio-description-speech—true/falsedata-audio-description-extended—true/false
Note: The playback position is retained when switching between captioned video sources. For VTT speech, a browser with
speechSynthesis ; otherwise, the descriptions are reset to plain text (track.mode = 'showing').
Chapter navigation
Jump to video chapters if chapter tracks are available:
<video data-vidply src="video.mp4">
<track kind="chapters" src="chapters.vtt" srclang="en" label="Chapters">
</video>
Chapters in VTT format:
WEBVTT
00:00:00.000 --> 00:01:30.000
Introduction
00:01:30.000 --> 00:05:00.000
Getting Started
00:05:00.000 --> 00:10:00.000
Advanced Features
00:10:00.000 --> 00:15:00.000
Conclusion
The ‘Chapters’ button appears automatically in the control bar as soon as chapter tracks are detected. Users can click on it to open a menu and jump to any chapter.
Subtitle layout
VidPly has TWO buttons for subtitles:
- CC button – Select subtitle language/track
- Aa button – Customise subtitle display (font, size, colour)
const player = new Player('#video', {
captions: true,
captionsButton: true, // Shows CC button for track selection
captionStyleButton: true // Shows Aa button for styling
});
// Programmatically set caption styles
player.captionManager.setCaptionStyle('fontSize', '120%');
player.captionManager.setCaptionStyle('fontFamily', 'serif');
player.captionManager.setCaptionStyle('color', '#FFFF00');
player.captionManager.setCaptionStyle('backgroundColor', '#000000');
player.captionManager.setCaptionStyle('opacity', 0.9);
Multiple players on the same page
<video id="player1" data-vidply src="video1.mp4"></video>
<video id="player2" data-vidply src="video2.mp4"></video>
<video id="player3" data-vidply src="video3.mp4"></video>
// All will auto-initialize
// By default, playing one will pause the others
// To allow multiple simultaneous playback:
const player1 = new Player('#player1', {
pauseOthersOnPlay: false
});
Responsive player
const player = new Player('#video', {
responsive: true,
fillContainer: false
});
Disable specific controls
const player = new Player('#video', {
controls: true,
playPauseButton: true,
progressBar: true,
volumeControl: false, // Hide volume
speedButton: false, // Hide speed
captionsButton: true,
fullscreenButton: true,
pipButton: false // Hide PiP
});
Custom callbacks
const player = new Player('#video', {
onReady: function() {
console.log('Player ready!');
console.log('Duration:', this.getDuration());
},
onPlay: function() {
console.log('Started playing');
// Track analytics
gtag('event', 'video_play', { video_title: 'My Video' });
},
onPause: function() {
console.log('Paused at:', this.getCurrentTime());
},
onEnded: function() {
console.log('Video finished');
// Show "Watch Next" overlay
}
});
Clean-up
const player = new Player('#video');
// Later, when done:
player.destroy();
Best practice for accessibility
1. Always provide subtitles
<video data-vidply>
<source src="video.mp4" type="video/mp4">
<track kind="subtitles" src="en.vtt" srclang="en" label="English">
<track kind="subtitles" src="es.vtt" srclang="es" label="Español">
</video>
2. Enable keyboard navigation
const player = new Player('#video', {
keyboard: true,
screenReaderAnnouncements: true
});
3. Provide descriptive labels
const player = new Player('#video', {
ariaLabels: {
play: 'Start video playback',
pause: 'Pause video playback',
// ... custom labels
}
});
4. Support for high contrast
The player automatically adapts to high-contrast mode. Test this by going to:
- Windows: Settings > Accessibility > Contrast themes
- CSS:
@media (prefers-contrast: high)
5. Reduced motion
The player automatically takes prefers-reduced-motion settings automatically.
Tips on performance
1. Pre-loading strategy
// Don't preload (better for mobile)
const player = new Player('#video', {
preload: 'none'
});
// Preload metadata only
const player = new Player('#video', {
preload: 'metadata'
});
// Preload entire video
const player = new Player('#video', {
preload: 'auto'
});
2. Lazy loading
VidPly can prevent premature loading over the network (useful if you have lots of players on a page):
<video
data-vidply
preload="none"
data-vidply-options='{"deferLoad": true, "preload": "none"}'
src="video.mp4"
></video>
Notes:
- When
deferLoad: trueVidPlymedia.load()during initialisation (and HLS/DASH do not start loading) until the user starts playback. - Depending on
preloadand their buffering strategy.
3. Responsive Images for posters
<video
data-vidply
poster="poster-small.jpg"
data-poster-medium="poster-medium.jpg"
data-poster-large="poster-large.jpg"
></video>
Troubleshooting
Video won’t play
- Check the console for errors
- Check whether the video format is supported
- Enable debug mode:
const player = new Player('#video', {
debug: true
});
Subtitles are not displayed
- Check the VTT file format
- Check the CORS headers if you are loading from a different domain
- Ensure that the audio track
kind="subtitles"orkind="captions"
YouTube/Vimeo is not loading
- Check your internet connection
- Check the format of the video URL
- Check the browser console for API loading errors
Problems with the HLS stream
- Check whether the M3U8 URL is accessible
- Check the CORS headers
- Test in Safari (native HLS support)
- hls.js 1.6.16 is loaded if necessary, if it is not already present on the page (overwritten via
hlsScriptUrl)
Problems with DASH streams
- Check that the MPD URL is accessible
- Check the CORS headers on the streaming server
- dash.js 5.2.0 (modern UMD) is loaded if necessary, provided it is not already present on the page (overwritten via
dashScriptUrl) - TTML subtitles are rendered natively by dash.js; WebVTT subtitles use the VidPly subtitle system
- If no quality levels are displayed, check whether the MPD manifest contains multiple renditions
- Enable debug mode for dash.js logs:
{ debug: true }
Advanced configuration
Passing options via the ‘data’ attribute
<video
data-vidply
data-vidply-options='{"autoplay": true, "loop": true, "volume": 0.5}'
src="video.mp4"
></video>
Create a player using JavaScript
// Create video element dynamically
const video = document.createElement('video');
video.src = 'video.mp4';
document.body.appendChild(video);
// Initialize player
const player = new Player(video, {
controls: true,
autoplay: false
});
Access the native video element
const player = new Player('#video');
// Access underlying video/audio element
const videoElement = player.element;
videoElement.playbackRate = 2;
Commands in the browser console
When debug mode is enabled, you can control the player via the browser console:
// Find player instance
const player = document.querySelector('.vidply-player')._vidply;
// Control playback
player.play();
player.pause();
player.seek(60);
// Check state
player.state.currentTime;
player.state.duration;
player.state.playing;
A complete example of accessibility
Here is a video with all accessibility features enabled:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Fully Accessible Video</title>
<link rel="stylesheet" href="dist/vidply.min.css">
</head>
<body>
<video
id="accessible-video"
data-vidply
src="main-video.mp4"
data-audio-description-src="described-version.mp4"
data-sign-language-src="sign-language-video.mp4"
data-sign-language-position="bottom-right"
data-transcript="true"
data-transcript-button="true"
>
<!-- Multiple caption languages -->
<track kind="captions" src="captions-en.vtt" srclang="en" label="English" default>
<track kind="captions" src="captions-es.vtt" srclang="es" label="Español">
<track kind="captions" src="captions-fr.vtt" srclang="fr" label="Français">
<!-- Chapters for navigation -->
<track kind="chapters" src="chapters-en.vtt" srclang="en" label="Chapters">
<!-- Audio descriptions (if not using alternate video) -->
<track kind="descriptions" src="descriptions-en.vtt" srclang="en" label="Descriptions">
</video>
<script type="module">
import Player from './dist/prod/vidply.esm.min.js';
// Player auto-initializes with all features enabled
</script>
</body>
</html>
This offers:
- Multiple subtitle languages with an easy-to-use switch function
- An interactive transcript for reading and navigation
- Video overlay with sign language
- Alternative audio description track
- Chapter navigation
- Full keyboard accessibility
- Support for screen readers
Next steps
- Discover
demo.htmlfor live examples - Read the API documentation at
README.md - For information on transcript functions, see Interactive Transcript Function
- For information on playlist functions, see Playlist function
- You can find the source code in
src/for customisations - Join the community discussions
Enjoy coding!