Video Controls Plugins
Create custom video control skins that integrate seamlessly with the MP4E player.
Overview
Video controls plugins let you create completely custom playback controls that replace or extend the default player controls. They receive real-time video events and can control playback through the mp4e API.
core:video-controls (modern),video-controls-retro (8-bit), and video-controls-minimal (glass-morphism).How It Works
Video controls plugins work differently from regular overlays:
- Full-size overlay - Controls cover the entire video area
- Video events - Receive
timeupdate,play,pause,volumechangeevents - Always visible - Not bound to specific time ranges
- Auto-scaling - Automatically scale with video dimensions
Creating Video Controls
A video controls plugin consists of HTML for structure, CSS for styling, and JavaScript for interactivity.
HTML Structure
1<!-- Basic video controls structure -->2<div class="controls-wrapper">3 <button id="play-pause" class="control-btn">4 <span id="play-icon">▶</span>5 <span id="pause-icon" style="display:none">⏸</span>6 </button>7 8 <div class="timeline">9 <div class="progress" id="progress"></div>10 </div>11 12 <div class="time-display" id="time">0:00 / 0:00</div>13 14 <button id="mute" class="control-btn">🔊</button>15 <button id="fullscreen" class="control-btn">⛶</button>16</div>CSS Styling
1/* Controls container - positioned at bottom */2.controls-wrapper {3 position: absolute;4 bottom: 0;5 left: 0;6 right: 0;7 padding: 16px;8 background: linear-gradient(transparent, rgba(0,0,0,0.8));9 display: flex;10 align-items: center;11 gap: 12px;12 pointer-events: auto; /* Enable clicks */13}1415/* Timeline/progress bar */16.timeline {17 flex: 1;18 height: 4px;19 background: rgba(255,255,255,0.3);20 border-radius: 2px;21 cursor: pointer;22 position: relative;23}2425.progress {26 height: 100%;27 width: 0%;28 background: white;29 border-radius: 2px;30 transition: width 0.1s linear;31}3233/* Buttons */34.control-btn {35 background: none;36 border: none;37 color: white;38 font-size: 18px;39 cursor: pointer;40 padding: 8px;41 opacity: 0.9;42}4344.control-btn:hover {45 opacity: 1;46}4748/* Time display */49.time-display {50 color: white;51 font-size: 14px;52 font-family: monospace;53 min-width: 100px;54}Video Events
The player dispatches video state events to your controls plugin. Listen to these to update your UI:
mp4e:video:timeupdate
Fired continuously during playback with current time and duration.
mp4e:video:play
Fired when video playback starts or resumes.
mp4e:video:pause
Fired when video playback is paused.
mp4e:video:volumechange
Fired when volume or mute state changes.
JavaScript Implementation
1// Video Controls Plugin JavaScript23// Get DOM elements4const playBtn = document.getElementById('play-pause');5const playIcon = document.getElementById('play-icon');6const pauseIcon = document.getElementById('pause-icon');7const progress = document.getElementById('progress');8const timeDisplay = document.getElementById('time');9const timeline = document.querySelector('.timeline');10const muteBtn = document.getElementById('mute');11const fsBtn = document.getElementById('fullscreen');1213let isPlaying = false;14let currentVolume = 1;1516// Format seconds to MM:SS17function formatTime(seconds) {18 if (!seconds || isNaN(seconds)) return '0:00';19 const m = Math.floor(seconds / 60);20 const s = Math.floor(seconds % 60);21 return m + ':' + s.toString().padStart(2, '0');22}2324// Listen to video events25document.addEventListener('mp4e:video:play', () => {26 isPlaying = true;27 playIcon.style.display = 'none';28 pauseIcon.style.display = 'inline';29});3031document.addEventListener('mp4e:video:pause', () => {32 isPlaying = false;33 playIcon.style.display = 'inline';34 pauseIcon.style.display = 'none';35});3637document.addEventListener('mp4e:video:timeupdate', (e) => {38 const { currentTime, duration } = e.detail.state;39 40 // Update progress bar41 const pct = (currentTime / duration) * 100;42 progress.style.width = pct + '%';43 44 // Update time display45 timeDisplay.textContent = formatTime(currentTime) + ' / ' + formatTime(duration);46});4748document.addEventListener('mp4e:video:volumechange', (e) => {49 const { volume, muted } = e.detail.state;50 currentVolume = volume;51 muteBtn.textContent = muted || volume === 0 ? '🔇' : '🔊';52});5354// Play/Pause button55playBtn.addEventListener('click', () => {56 if (isPlaying) {57 mp4e.pause();58 } else {59 mp4e.play();60 }61});6263// Timeline seek64timeline.addEventListener('click', (e) => {65 const rect = timeline.getBoundingClientRect();66 const pct = (e.clientX - rect.left) / rect.width;67 const duration = window.__mp4e_data__?.duration || 0;68 if (duration > 0) {69 mp4e.seekToTime(duration * pct);70 }71});7273// Mute toggle74muteBtn.addEventListener('click', () => {75 mp4e.emit('toggleMute');76});7778// Fullscreen toggle79fsBtn.addEventListener('click', () => {80 mp4e.emit('toggleFullscreen');81});Automatic Scaling
Controls plugins automatically scale based on the video display size. The reference size is 1920px width - controls are scaled proportionally for smaller or larger videos.
1/* CSS Variables for Scaling */23/* The player automatically provides these CSS variables: */4:root {5 --scale: 0.75; /* Example: 75% of 1920px reference */6 --s: 0.75; /* Shorthand alias */7}89/* The #mp4e-root wrapper is automatically scaled: */10#mp4e-root {11 transform: scale(var(--scale));12 transform-origin: top left;13}1415/* For responsive elements, use the scale factor: */16.control-btn {17 /* Font size scales with video size */18 font-size: calc(18px * var(--scale));19 padding: calc(8px * var(--scale));20}2122/* Or use the mp4e.scale() helper in JavaScript: */23// const scaledSize = mp4e.scale(16); // Returns scaled px value--scale will be 0.5. For a 2560px wide video, it will be ~1.33.Player Actions
Control video playback using the mp4e API methods:
1// Available player control methods23// Play/Pause4mp4e.play();5mp4e.pause();67// Seeking8mp4e.seekToTime(30); // Seek to 30 seconds910// Volume control11mp4e.setVolume(0.5); // Set volume to 50%1213// Emit events for player handling14mp4e.emit('toggleMute'); // Toggle mute state15mp4e.emit('toggleFullscreen'); // Toggle fullscreen1617// Access current state18const time = await mp4e.getCurrentTime();19const frame = await mp4e.getCurrentFrame();Menu Configuration
Controls plugins can access menu configuration to render custom settings menus. The menu config specifies which system items (playback speed, quality, subtitles, audio) are enabled and their order.
1// Get the menu configuration2const menuConfig = mp4e.getMenuConfig();34// menuConfig structure:5{6 systemItems: {7 playbackSpeed: { enabled: true, order: 0, label: 'Speed' },8 quality: { enabled: true, order: 1, label: 'Quality' },9 audioTracks: { enabled: true, order: 2, label: 'Audio' },10 subtitles: { enabled: true, order: 3, label: 'Subtitles' }11 },12 customItems: [...], // Custom menu items with actions13 settings: {14 position: 'bottom-right',15 closeOnSelect: true,16 showIcons: true17 }18}Chapter Markers API
Controls plugins can render chapter markers on the progress bar. The player provides marker data computed from scene groups configured in player.markers.
1// Get all chapter markers2const markers = mp4e.getMarkers();3// Returns: [{ id, name, startTime, endTime, color }, ...]45// Get the current active marker6const current = mp4e.getCurrentMarker();7// Returns: { id, name, startTime, endTime } or null89// Get marker at a specific time (for hover preview)10const marker = mp4e.getMarkerAtTime(30.5);1112// Get marker display settings13const settings = mp4e.getMarkerSettings();14// Returns: { progressBarStyle, progressBarHeight, showCurrentLabel, showInMenu }progressBarStyle can be "dots", "segments","lines", or "none". For YouTube-style chapters, use "segments".Thumbnail Preview API
Controls plugins can show thumbnail previews when users hover over the progress bar. Thumbnails are stored as sprite sheets in the video's media atom.
1// Get thumbnail configuration2const thumbs = mp4e.getThumbnails();3// Returns: { enabled, interval, columns, width, height } or null45// Get the sprite sheet URL6const spriteUrl = mp4e.getThumbnailSpriteUrl();7// Returns: "https://api.example.com/api/video/{id}/media/thumb_sprite" or null89// Get sprite position for a specific time10const pos = mp4e.getThumbnailPosition(30.5);11// Returns: { x: 160, y: 90 } - pixel offset within sprite sheet1213// Example: showing thumbnail on hover14timeline.addEventListener('mousemove', (e) => {15 const rect = timeline.getBoundingClientRect();16 const pct = (e.clientX - rect.left) / rect.width;17 const hoverTime = duration * pct;1819 const pos = mp4e.getThumbnailPosition(hoverTime);20 if (pos && spriteUrl) {21 preview.style.backgroundImage = `url(${spriteUrl})`;22 preview.style.backgroundPosition = `-${pos.x}px -${pos.y}px`;23 }24});Complete Example
Here's the complete JavaScript for a functional video controls plugin (combine with the HTML and CSS above):
1// Video Controls Plugin JavaScript23// Get DOM elements4const playBtn = document.getElementById('play-pause');5const playIcon = document.getElementById('play-icon');6const pauseIcon = document.getElementById('pause-icon');7const progress = document.getElementById('progress');8const timeDisplay = document.getElementById('time');9const timeline = document.querySelector('.timeline');10const muteBtn = document.getElementById('mute');11const fsBtn = document.getElementById('fullscreen');1213let isPlaying = false;14let currentVolume = 1;1516// Format seconds to MM:SS17function formatTime(seconds) {18 if (!seconds || isNaN(seconds)) return '0:00';19 const m = Math.floor(seconds / 60);20 const s = Math.floor(seconds % 60);21 return m + ':' + s.toString().padStart(2, '0');22}2324// Listen to video events25document.addEventListener('mp4e:video:play', () => {26 isPlaying = true;27 playIcon.style.display = 'none';28 pauseIcon.style.display = 'inline';29});3031document.addEventListener('mp4e:video:pause', () => {32 isPlaying = false;33 playIcon.style.display = 'inline';34 pauseIcon.style.display = 'none';35});3637document.addEventListener('mp4e:video:timeupdate', (e) => {38 const { currentTime, duration } = e.detail.state;39 40 // Update progress bar41 const pct = (currentTime / duration) * 100;42 progress.style.width = pct + '%';43 44 // Update time display45 timeDisplay.textContent = formatTime(currentTime) + ' / ' + formatTime(duration);46});4748document.addEventListener('mp4e:video:volumechange', (e) => {49 const { volume, muted } = e.detail.state;50 currentVolume = volume;51 muteBtn.textContent = muted || volume === 0 ? '🔇' : '🔊';52});5354// Play/Pause button55playBtn.addEventListener('click', () => {56 if (isPlaying) {57 mp4e.pause();58 } else {59 mp4e.play();60 }61});6263// Timeline seek64timeline.addEventListener('click', (e) => {65 const rect = timeline.getBoundingClientRect();66 const pct = (e.clientX - rect.left) / rect.width;67 const duration = window.__mp4e_data__?.duration || 0;68 if (duration > 0) {69 mp4e.seekToTime(duration * pct);70 }71});7273// Mute toggle74muteBtn.addEventListener('click', () => {75 mp4e.emit('toggleMute');76});7778// Fullscreen toggle79fsBtn.addEventListener('click', () => {80 mp4e.emit('toggleFullscreen');81});Setting the Controls Plugin
To use a custom controls plugin, set the pluginId in your video's player configuration:
1// In metadata.player, set the controls plugin:2{3 "player": {4 "controls": {5 "enabled": true,6 "pluginId": "mp4e:video-controls-kawaii" // Your custom controls7 },8 "autoplay": false9 }10}1112// Available built-in controls:13// - "core:video-controls" (Default modern controls)14// - "mp4e:video-controls-kawaii" (Kawaii anime style)15// - "mp4e:video-controls-retro" (8-bit retro style)16// - "mp4e:video-controls-minimal" (Glass-morphism minimal)1718// Or use any marketplace plugin ID