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.

Built-in Controls
MP4E includes several built-in control skins: 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, volumechange events
  • 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

Controls HTML
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

Controls CSS
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}
14
15/* 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}
24
25.progress {
26 height: 100%;
27 width: 0%;
28 background: white;
29 border-radius: 2px;
30 transition: width 0.1s linear;
31}
32
33/* 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}
43
44.control-btn:hover {
45 opacity: 1;
46}
47
48/* 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

Controls JavaScript
1// Video Controls Plugin JavaScript
2
3// Get DOM elements
4const 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');
12
13let isPlaying = false;
14let currentVolume = 1;
15
16// Format seconds to MM:SS
17function 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}
23
24// Listen to video events
25document.addEventListener('mp4e:video:play', () => {
26 isPlaying = true;
27 playIcon.style.display = 'none';
28 pauseIcon.style.display = 'inline';
29});
30
31document.addEventListener('mp4e:video:pause', () => {
32 isPlaying = false;
33 playIcon.style.display = 'inline';
34 pauseIcon.style.display = 'none';
35});
36
37document.addEventListener('mp4e:video:timeupdate', (e) => {
38 const { currentTime, duration } = e.detail.state;
39
40 // Update progress bar
41 const pct = (currentTime / duration) * 100;
42 progress.style.width = pct + '%';
43
44 // Update time display
45 timeDisplay.textContent = formatTime(currentTime) + ' / ' + formatTime(duration);
46});
47
48document.addEventListener('mp4e:video:volumechange', (e) => {
49 const { volume, muted } = e.detail.state;
50 currentVolume = volume;
51 muteBtn.textContent = muted || volume === 0 ? '🔇' : '🔊';
52});
53
54// Play/Pause button
55playBtn.addEventListener('click', () => {
56 if (isPlaying) {
57 mp4e.pause();
58 } else {
59 mp4e.play();
60 }
61});
62
63// Timeline seek
64timeline.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});
72
73// Mute toggle
74muteBtn.addEventListener('click', () => {
75 mp4e.emit('toggleMute');
76});
77
78// Fullscreen toggle
79fsBtn.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.

Scaling system
1/* CSS Variables for Scaling */
2
3/* 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}
8
9/* The #mp4e-root wrapper is automatically scaled: */
10#mp4e-root {
11 transform: scale(var(--scale));
12 transform-origin: top left;
13}
14
15/* 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}
21
22/* Or use the mp4e.scale() helper in JavaScript: */
23// const scaledSize = mp4e.scale(16); // Returns scaled px value
Scale Factor
For a 960px wide video, --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:

Player control methods
1// Available player control methods
2
3// Play/Pause
4mp4e.play();
5mp4e.pause();
6
7// Seeking
8mp4e.seekToTime(30); // Seek to 30 seconds
9
10// Volume control
11mp4e.setVolume(0.5); // Set volume to 50%
12
13// Emit events for player handling
14mp4e.emit('toggleMute'); // Toggle mute state
15mp4e.emit('toggleFullscreen'); // Toggle fullscreen
16
17// Access current state
18const time = await mp4e.getCurrentTime();
19const frame = await mp4e.getCurrentFrame();

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.

Accessing menu configuration
1// Get the menu configuration
2const menuConfig = mp4e.getMenuConfig();
3
4// 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 actions
13 settings: {
14 position: 'bottom-right',
15 closeOnSelect: true,
16 showIcons: true
17 }
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.

Chapter markers API
1// Get all chapter markers
2const markers = mp4e.getMarkers();
3// Returns: [{ id, name, startTime, endTime, color }, ...]
4
5// Get the current active marker
6const current = mp4e.getCurrentMarker();
7// Returns: { id, name, startTime, endTime } or null
8
9// Get marker at a specific time (for hover preview)
10const marker = mp4e.getMarkerAtTime(30.5);
11
12// Get marker display settings
13const settings = mp4e.getMarkerSettings();
14// Returns: { progressBarStyle, progressBarHeight, showCurrentLabel, showInMenu }
Marker Styles
The 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.

Thumbnail preview API
1// Get thumbnail configuration
2const thumbs = mp4e.getThumbnails();
3// Returns: { enabled, interval, columns, width, height } or null
4
5// Get the sprite sheet URL
6const spriteUrl = mp4e.getThumbnailSpriteUrl();
7// Returns: "https://api.example.com/api/video/{id}/media/thumb_sprite" or null
8
9// Get sprite position for a specific time
10const pos = mp4e.getThumbnailPosition(30.5);
11// Returns: { x: 160, y: 90 } - pixel offset within sprite sheet
12
13// Example: showing thumbnail on hover
14timeline.addEventListener('mousemove', (e) => {
15 const rect = timeline.getBoundingClientRect();
16 const pct = (e.clientX - rect.left) / rect.width;
17 const hoverTime = duration * pct;
18
19 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):

Complete controls plugin
1// Video Controls Plugin JavaScript
2
3// Get DOM elements
4const 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');
12
13let isPlaying = false;
14let currentVolume = 1;
15
16// Format seconds to MM:SS
17function 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}
23
24// Listen to video events
25document.addEventListener('mp4e:video:play', () => {
26 isPlaying = true;
27 playIcon.style.display = 'none';
28 pauseIcon.style.display = 'inline';
29});
30
31document.addEventListener('mp4e:video:pause', () => {
32 isPlaying = false;
33 playIcon.style.display = 'inline';
34 pauseIcon.style.display = 'none';
35});
36
37document.addEventListener('mp4e:video:timeupdate', (e) => {
38 const { currentTime, duration } = e.detail.state;
39
40 // Update progress bar
41 const pct = (currentTime / duration) * 100;
42 progress.style.width = pct + '%';
43
44 // Update time display
45 timeDisplay.textContent = formatTime(currentTime) + ' / ' + formatTime(duration);
46});
47
48document.addEventListener('mp4e:video:volumechange', (e) => {
49 const { volume, muted } = e.detail.state;
50 currentVolume = volume;
51 muteBtn.textContent = muted || volume === 0 ? '🔇' : '🔊';
52});
53
54// Play/Pause button
55playBtn.addEventListener('click', () => {
56 if (isPlaying) {
57 mp4e.pause();
58 } else {
59 mp4e.play();
60 }
61});
62
63// Timeline seek
64timeline.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});
72
73// Mute toggle
74muteBtn.addEventListener('click', () => {
75 mp4e.emit('toggleMute');
76});
77
78// Fullscreen toggle
79fsBtn.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:

Setting controls in metadata
1// In metadata.player, set the controls plugin:
2{
3 "player": {
4 "controls": {
5 "enabled": true,
6 "pluginId": "mp4e:video-controls-kawaii" // Your custom controls
7 },
8 "autoplay": false
9 }
10}
11
12// 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)
17
18// Or use any marketplace plugin ID
Studio UI
In the Studio, you can change the controls plugin in the Player Settings panel under "Controls Skin".