# User's Guide: VidPly Player
A practical guide for web developers on how to integrate VidPly into your websites.
---
## Quick Overview
VidPly is a universal, accessible media player supporting:
| Feature | Support |
|---------|---------|
| **Video** | MP4, WebM, OGG |
| **Audio** | MP3, OGG, WAV |
| **YouTube** | Embedded with unified controls |
| **Vimeo** | Embedded with unified controls |
| **SoundCloud** | Embedded via Widget API with unified controls |
| **HLS** | Adaptive bitrate streaming (`hls.js` on most browsers, native on iOS / iPadOS) |
| **DASH** | MPEG-DASH streaming via dash.js |
| **Buffering Spinner** | Centered loading spinner during waiting/seeking |
| **Download Button** | Optional control with custom URL support |
| **Playlists** | Audio, video & mixed media with thumbnails |
| **Accessibility** | WCAG 2.2 AA compliant |
| **TypeScript** | Strict TS sources + bundled `.d.ts` declarations |
| **Languages** | EN, ES, FR, DE, JA + custom |
---
## Installation
### 1. Build the Player
```bash
npm install
npm run build
```
This creates files in `dist/`:
- `prod/vidply.esm.min.js` - ES Module (recommended)
- `legacy/vidply.min.js` - IIFE for script tag (global `VidPly`)
- `vidply.min.css` - Styles
### 2. Include in Your Page
**ES Module (Recommended):**
```html
```
**Traditional Script Tag:**
```html
```
---
## Basic Usage
### Video Player
```html
```
The `data-vidply` attribute auto-initializes the player.
### Audio Player
```html
```
### With Poster Image
```html
```
---
## External Services
### YouTube
```html
```
Or with full URL:
```html
```
### Vimeo
```html
```
### SoundCloud
```html
```
The SoundCloud Widget API is auto-detected for any URL containing `soundcloud.com`. The widget is loaded on demand and integrates with the standard VidPly play / pause / seek / volume controls.
When using `mpc-vidply` in TYPO3, the **Privacy Layer** displays a GDPR consent overlay before the SoundCloud iframe is loaded — visitors must accept loading external content first.
### HLS Streaming
```html
```
`hls.js` **1.6.16** is loaded on demand when `.m3u8` URLs are detected (CDN fallback if not already on the page). Behavior per platform:
| Platform | HLS engine | Captions / Transcript / Quality |
|----------|------------|---------------------------------|
| Chrome / Firefox / Edge | `hls.js` | Full VidPly UI |
| Desktop macOS Safari | `hls.js` (for parity with other browsers) | Full VidPly UI |
| iOS / iPadOS Safari | Native `` HLS | Full VidPly UI via the native `TextTrack` API bridge |
> Even on iOS where `hls.js` cannot run (no MSE), VidPly still wires the captions menu, interactive transcript and quality menu into the browser's native HLS text tracks — you don't lose any UI controls.
### DASH Streaming
```html
```
**dash.js 5.2.0** (modern UMD) is loaded on demand when `.mpd` URLs are detected. DASH streams support:
- Adaptive bitrate quality selection
- TTML subtitles (rendered natively by dash.js)
- WebVTT subtitles (handled by VidPly's caption system with transcript support)
### DASH + HLS + MP4 Fallback
```html
```
---
## Adding Captions & Subtitles
### Basic Captions
```html
```
### Track Types
| Kind | Purpose |
|------|---------|
| `subtitles` | Translation of dialogue |
| `captions` | Dialogue + sound effects (deaf/hard of hearing) |
| `descriptions` | Text descriptions of visual content |
| `chapters` | Chapter markers for navigation |
### Default Captions On
```html
```
---
## Chapters
Add chapter navigation with a chapters track:
```html
```
**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
### Via Data Attributes
```html
```
### Via JavaScript
```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
});
```
### All Options Reference
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `width` | number | 800 | Player width |
| `height` | number | 450 | Player height |
| `poster` | string | null | Poster image URL |
| `responsive` | bool | true | Responsive sizing |
| `autoplay` | bool | false | Auto-start playback |
| `loop` | bool | false | Loop playback |
| `muted` | bool | false | Start muted |
| `volume` | number | 0.8 | Default volume (0-1) |
| `playbackSpeed` | number | 1.0 | Default speed |
| `controls` | bool | true | Show controls |
| `hideControlsDelay` | number | 3000 | Auto-hide delay (ms) |
| `keyboard` | bool | true | Enable keyboard shortcuts |
| `language` | string | 'en' | UI language |
| `captions` | bool | true | Enable captions support |
| `captionsDefault` | bool | false | Show captions by default |
| `transcript` | bool | false | Show transcript panel |
| `debug` | bool | false | Debug logging |
| `preload` | string | 'metadata' | `'none'`, `'metadata'`, `'auto'` |
| `deferLoad` | bool | false | Avoid starting network loading during init; load on user play / explicit load |
| `initialDuration` | number | 0 | Initial duration in seconds (UI only, before media metadata is loaded) |
| `requirePlaybackForAccessibilityToggles` | bool | false | If true: AD/SL before playback shows a notice instead of implicitly loading/playing |
---
## Download Button
Show an opt-in download button in the control bar:
```html
```
```javascript
const player = new Player('#video', {
downloadButton: true,
downloadUrl: '/files/lecture.mp4' // optional; falls back to current src
});
```
The button is keyboard accessible, fully internationalized (`player.download` translation key) and uses `aria-label` so screen readers announce it correctly.
> Hint for streaming sources: `downloadUrl` is normally the URL of an MP4/MP3 fallback because users typically can't download a `.mpd` or `.m3u8` manifest as a single file.
---
## Custom Floating Player (Miniplayer)
VidPly ships an optional in-page floating player ("own PiP") that pops up in a
configurable corner of the viewport, can be dragged and resized, and keeps
VidPly's own caption rendering, transport controls and fullscreen working
inside the floating shell.
While `floating` is enabled, the native browser Picture-in-Picture API is
suppressed automatically (`disablepictureinpicture` + `disableremoteplayback`)
so the user gets a single, consistent experience across browsers.
### Behavior
- **Auto-float on scroll-out** - When the original player scrolls out of the viewport, the video pops into the floating shell. When it scrolls back in, the video docks back to its original container.
- **Manual pin/unpin** - The PiP button in the control bar manually pins the floating shell. Manual pin/unpin overrides scroll-based auto-floating until the next user-initiated `play`.
- **Close button** - The X button in the floating shell pauses the video, returns it to its original container and suppresses auto-float for the rest of the play session.
- **Drag & resize** - The floating shell can be dragged by its header and resized via the corner/edge handles. Geometry is persisted per player via local storage.
- **Reduced control bar** - Inside the floating shell only essential controls are shown: play/pause, rewind, forward, volume, captions, PiP and fullscreen. Tooltips open above the buttons; captions are sized at `90%` and capped to `95%` width.
- **Audio players are skipped** - The floating feature only applies to `` players.
- **Desktop only by default** - Below `floatingMinViewportWidth` (default 768 px) the feature is disabled and the floating PiP button is hidden in the main control bar (it never falls into the overflow menu).
### Enable via HTML (data-vidply-options JSON)
Floating options are passed via the `data-vidply-options` JSON blob (the same
channel TYPO3 / `mpc_vidply` uses):
```html
```
### Enable via JavaScript
```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 announced as a `role="dialog"` with a translated `aria-label` (`player.floatingPlayerDialog`).
- The PiP toggle uses `aria-pressed` to expose the pinned state.
- Focus is returned to the originating button when the floating shell is closed via keyboard.
- All built-in languages (en, de, es, fr, ja) include translations for `player.floatingPlayer`, `player.floatingPlayerClose`, `player.floatingPlayerEnter`, `player.floatingPlayerExit` and `player.floatingPlayerDialog`.
---
## Buffering Spinner
A centered loading spinner appears automatically while the media is buffering (`waiting` / `seeking` / initial `loadstart`) and disappears on `canplay` / `playing`. It works for HTML5, HLS and DASH renderers and respects `prefers-reduced-motion`.
You can theme it via CSS variables:
```css
.vidply-player {
--vidply-spinner-color: #ffffff;
--vidply-spinner-size: 56px;
--vidply-z-buffering: 1; /* sits below the play overlay */
}
```
The container exposes a `.vidply-buffering` class while loading, which you can use to style or hook into:
```css
.vidply-player.vidply-buffering .my-skin-overlay {
opacity: 0.3;
}
```
---
## Playlists
### Audio Playlist
```html
```
### Video Playlist
```html
```
### Playlist Options
| Option | Default | Description |
|--------|---------|-------------|
| `autoAdvance` | true | Auto-play next track |
| `autoPlayFirst` | true | Auto-play first track on `loadPlaylist()` (if false: first track is loaded/selected but not played) |
| `loop` | false | Loop playlist |
| `showPanel` | true | Show playlist panel |
### Playlist Controls
```javascript
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 description in two ways:
1. **Described video (recommended for production)** — upload or link a pre-mixed MP4/WebM where narration is already
embedded in the audio track. VidPly swaps the entire video source when the user toggles AD.
2. **VTT speech (extended AD fallback)** — when no described video is available, a `kind="descriptions"` WebVTT track
can drive browser text-to-speech: the player pauses at each cue, speaks the description, then resumes playback.
Quality depends on the browser voice; use described video when broadcast-grade narration is required.
Text description cues are always available in the **transcript panel** for reading, regardless of which spoken path
is active. Set `audioDescriptionSpeech: false` to use text-only descriptions (no TTS).
**Mode** (`audioDescriptionMode`, default `auto`):
| Value | Behavior |
|-------|----------|
| `auto` | Described video if configured, else VTT speech if descriptions VTT present |
| `swap` | Described video swap only |
| `vtt_speech` | VTT speech only |
```javascript
const player = new Player('#my-video', {
audioDescription: true,
audioDescriptionSrc: 'video-with-description.mp4', // optional
audioDescriptionMode: 'auto',
audioDescriptionSpeech: true,
audioDescriptionExtended: true
});
```
Users toggle AD via the AD button. Demo: [`demo/single-player-vtt-speech.html`](../demo/single-player-vtt-speech.html).
### Sign Language
Add sign language interpretation overlay:
```javascript
const player = new Player('#my-video', {
signLanguage: true,
signLanguageSrc: 'sign-language.mp4',
signLanguagePosition: 'bottom-right'
});
```
Position options: `bottom-right`, `bottom-left`, `top-right`, `top-left`
### Interactive Transcripts
```javascript
const player = new Player('#my-video', {
transcript: true,
transcriptPosition: 'external',
transcriptContainer: '#transcript-panel'
});
```
Features:
- Click any line to seek to that point
- Auto-scrolls during playback
- Searchable text
- Draggable/resizable window
---
## Keyboard Shortcuts
VidPly includes comprehensive keyboard controls:
| Key | Action |
|-----|--------|
| **Space** / **P** / **K** | Play/Pause |
| **F** | Toggle fullscreen |
| **M** | Mute/Unmute |
| **↑** / **↓** | Volume up/down |
| **←** / **→** | Seek ±10 seconds |
| **C** | Toggle captions |
| **A** | Caption style menu |
| **<** / **>** | Decrease/increase speed |
| **S** | Speed menu |
| **Q** | Quality menu |
| **J** | Chapters menu |
| **T** | Toggle transcript |
| **D** | Drag mode (transcript/sign) |
| **R** | Resize mode |
| **Home** | Reset position |
| **Escape** | Exit mode/close menu |
### Custom Keyboard Shortcuts
```javascript
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']
}
});
```
---
## Styling & Theming
### CSS Variables
```css
.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
```css
.vidply-progress-played {
background: linear-gradient(90deg, #667eea, #764ba2);
}
```
### Custom Buttons
```css
.vidply-button:hover {
background: rgba(59, 130, 246, 0.2);
}
.vidply-button:focus {
outline: 2px solid var(--vidply-primary-color);
outline-offset: 2px;
}
```
---
## Internationalization
### Built-in Languages
- English (en)
- Spanish (es) - Español
- French (fr) - Français
- German (de) - Deutsch
- Japanese (ja) - 日本語
### Set Language
```javascript
const player = new Player('#my-video', {
language: 'de' // German
});
```
### Auto-detect from HTML
```html
```
### Load Custom Language
**Via data attribute:**
```html
```
**Via JavaScript (options):**
```javascript
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:**
```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
```javascript
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
```javascript
player.setVolume(0.5) // Set volume (0-1)
player.getVolume() // Get volume
player.mute() // Mute
player.unmute() // Unmute
player.toggleMute() // Toggle mute
```
### Speed
```javascript
player.setPlaybackSpeed(1.5) // Set speed (0.25-2.0)
player.getPlaybackSpeed() // Get speed
```
### Fullscreen
```javascript
player.enterFullscreen() // Enter fullscreen
player.exitFullscreen() // Exit fullscreen
player.toggleFullscreen() // Toggle fullscreen
```
### Captions
```javascript
player.enableCaptions() // Enable captions
player.disableCaptions() // Disable captions
player.toggleCaptions() // Toggle captions
```
### State
```javascript
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
```javascript
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) => {})
```
### Cleanup
```javascript
player.destroy() // Remove player
```
---
## Browser Support
| Browser | Version |
|---------|---------|
| Chrome | 90+ |
| Firefox | 88+ |
| Safari | 14+ |
| Edge | 90+ |
| iOS Safari | 14+ |
| Android Chrome | 90+ |
---
## Troubleshooting
### Video Not Playing
| Issue | Solution |
|-------|----------|
| Black screen | Check video URL is accessible |
| CORS error | Ensure proper CORS headers on server |
| Format unsupported | Provide MP4 fallback |
| Autoplay blocked | Set `muted: true` for autoplay |
### Captions Not Showing
| Issue | Solution |
|-------|----------|
| VTT not loading | Check file URL; validate VTT syntax |
| CORS error | Serve VTT from same domain or enable CORS |
| Wrong encoding | Save VTT as UTF-8 |
### YouTube/Vimeo Not Working
| Issue | Solution |
|-------|----------|
| Not loading | Check video URL format |
| API errors | Ensure videos are embeddable |
| Controls missing | VidPly uses native service players |
### HLS Not Playing
| Issue | Solution |
|-------|----------|
| Stream not loading | Verify .m3u8 URL is accessible |
| CORS issues | Configure CORS on streaming server |
| Segments failing | Check segment URLs in manifest |
### DASH Not Playing
| Issue | Solution |
|-------|----------|
| Stream not loading | Verify .mpd URL is accessible |
| CORS issues | Configure CORS on streaming server |
| No quality levels | Check MPD has multiple representations |
| TTML captions missing | dash.js renders TTML natively; ensure tracks are in the manifest |
| Transcript not available | TTML tracks don't support transcript; use WebVTT for transcript |
---
## Best Practices
### Performance
- ✅ Use `responsive: true` for fluid layouts
- ✅ Provide poster images
- ✅ Use appropriate video resolutions
- ✅ Compress videos for web delivery
### Accessibility
- ✅ Always provide captions
- ✅ Use meaningful track labels
- ✅ Test with keyboard only
- ✅ Test with screen readers
### Cross-browser
- ✅ Provide MP4 + WebM sources
- ✅ Test on mobile devices
- ✅ Handle fullscreen differences on iOS
---
## Live Demos
Try VidPly in action:
- [Main Demo](https://matthiaspeltzer.github.io/vidply/demo/demo.html)
- [Audio Playlist](https://matthiaspeltzer.github.io/vidply/demo/playlist-audio.html)
- [Video Playlist](https://matthiaspeltzer.github.io/vidply/demo/playlist-video.html)
- [Mixed Media Playlist](https://matthiaspeltzer.github.io/vidply/demo/playlist-mixed.html)
- [HLS Streaming](https://matthiaspeltzer.github.io/vidply/demo/hls-test.html)
- [DASH Streaming](https://matthiaspeltzer.github.io/vidply/demo/dash-test.html)
---
**Need help?** Check the [API documentation](USAGE.md) or open an issue on GitHub.