A JavaScript library for interacting with iframes that support Player.js spec.
const player = new playerjs.Player('iframe');
player.on('ready', async () => {
player.on('play', () => {
console.log('play');
});
const duration = await player.getDuration();
console.log(duration);
if (player.supports('method', 'mute')) {
await player.mute();
}
await player.play();
});
Player.js is hosted on JSDelivr's CDN:
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/@gumlet/player.js@3.0/dist/main.global.js"></script>
Install via npm:
npm install @gumlet/player.js
Player.js v3.0.0+ includes full TypeScript support with type definitions included. The library is written in TypeScript and provides comprehensive type safety:
import playerjs from '@gumlet/player.js';
const player = new playerjs.Player('iframe');
player.on('ready', async () => {
try {
const duration = await player.getDuration();
console.log(`Duration: ${duration} seconds`);
if (player.supports('method', 'mute')) {
await player.mute();
}
await player.play();
} catch (error) {
console.error('Player error:', error);
}
});
All player methods return promises, making it easier to work with async/await patterns:
// Get multiple values concurrently
const [duration, currentTime, volume] = await Promise.all([
player.getDuration(),
player.getCurrentTime(),
player.getVolume()
]);
// Chain operations
await player.setCurrentTime(10);
await player.setVolume(75);
await player.play();
Because of the dance that we need to do between both iframes, you should always wait till the ready
event fires before interacting with the player object. However, the player will internally queue messages until ready is called:
const player = new playerjs.Player('iframe');
player.on('ready', async () => {
await player.setCurrentTime(20);
});
The timing between when the iframe is added and when the ready event is fired is important. Sadly we cannot fire the ready event till the iframe is loaded, but there is no concrete way of telling when postmessage is available to us.
The best way is to do one of the following:
const iframe = document.createElement('iframe');
iframe.src = 'https://example.com/iframe';
document.body.appendChild(iframe);
const player = new playerjs.Player(iframe);
In this case, Player.js will listen to the onload event of the iframe and only try to communicate when ready.
<iframe src="//example.com/iframe"></iframe>
<script>
$(document).on('ready', () => {
$('iframes').each(async () => {
const player = new playerjs.Player(this);
player.on('ready', async () => {
await player.play();
});
});
});
</script>
At this point we can reasonably assume that the iframe's been loaded and the ready. Player.js will take care of listening for ready events that were fired before the player is set up.
All methods return promises for modern async/await patterns:
Play the media:
await player.play();
Pause the media:
await player.pause();
Determine if the media is paused:
const isPaused = await player.getPaused();
console.log('paused:', isPaused);
Mute the media:
await player.mute();
Unmute the media:
await player.unmute();
Determine if the media is muted:
const isMuted = await player.getMuted();
console.log('muted:', isMuted);
Set the volume. Value needs to be between 0-100:
await player.setVolume(50);
Get the volume. Value will be between 0-100:
const volume = await player.getVolume();
console.log('volume:', volume);
Get the duration of the media in seconds:
const duration = await player.getDuration();
console.log('duration:', duration);
Perform a seek to a particular time in seconds:
await player.setCurrentTime(50);
Get the current time in seconds of the video:
const currentTime = await player.getCurrentTime();
console.log('currentTime:', currentTime);
Set the playback rate which are available in the player. Doesn't return an error if the passed playback rate is not available:
await player.setPlaybackRate(0.5);
Get the current playback rate of the player:
const rate = await player.getPlaybackRate();
console.log('playbackRate:', rate);
Set whether the media should loop:
await player.setLoop(true);
Get whether the media is set to loop:
const isLooping = await player.getLoop();
console.log('looping:', isLooping);
Remove an event listener. If the listener is specified it should remove only that listener, otherwise remove all listeners:
player.off('play');
player.off('play', playCallback);
Add an event listener:
player.on('play', () => console.log('play'));
Determines if the player supports a given event or method:
player.supports('method', 'getDuration');
player.supports('event', 'ended');
player.supports('method', ['play', 'pause']);
Player.js provides comprehensive event handling to monitor playback state and user interactions. All events can be listened to using the on()
method.
Fired when the media is ready to receive commands. Always wait for this event before calling player methods.
player.on('ready', async () => {
console.log('Player is ready!');
// Safe to call player methods now
await player.play();
});
Note: As outlined in the PlayerJs Spec, you may run into inconsistencies if you have multiple players on the page with the same src
. To avoid this, append a UUID or timestamp to the iframe's src.
Fired when playback starts.
player.on('play', () => {
console.log('Video started playing');
});
Fired when playback is paused.
player.on('pause', () => {
console.log('Video paused');
});
Fired when playback reaches the end of the media.
player.on('ended', async () => {
console.log('Video finished');
// Reset to beginning if needed
await player.setCurrentTime(0);
});
Fired regularly during playback with current time information.
player.on('timeupdate', (data) => {
const { seconds, duration } = data;
const progress = (seconds / duration) * 100;
console.log(`Progress: ${progress.toFixed(1)}%`);
console.log(`Current time: ${formatTime(seconds)} / ${formatTime(duration)}`);
});
function formatTime(seconds) {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins}:${secs.toString().padStart(2, '0')}`;
}
Fired when the media is buffering/loading additional content.
player.on('progress', (data) => {
const { percent } = data;
console.log(`Buffered: ${(percent * 100).toFixed(1)}%`);
});
Fired when the user seeks to a different time position.
player.on('seeked', (data) => {
const { seconds, duration } = data;
console.log(`Seeked to ${seconds}s of ${duration}s`);
});
Fired when the volume level changes, including mute/unmute actions.
player.on('volumeChange', async () => {
const volume = await player.getVolume();
const isMuted = await player.getMuted();
console.log(`Volume: ${volume}%, Muted: ${isMuted}`);
});
Fired when playback speed is changed.
player.on('playbackRateChange', async () => {
const rate = await player.getPlaybackRate();
console.log(`Playback rate: ${rate}x`);
});
Fired when fullscreen mode is toggled.
player.on('fullscreenChange', (data) => {
const { isFullScreen } = data;
console.log(`Fullscreen: ${isFullScreen}`);
});
Fired when Picture-in-Picture mode is toggled.
player.on('pipChange', (data) => {
const { isPIP } = data;
console.log(`Picture-in-Picture: ${isPIP}`);
});
Fired when video quality/resolution is changed.
player.on('qualityChange', (data) => {
const { quality } = data;
console.log(`Quality changed to: ${quality}`);
});
Fired when the audio track is changed.
player.on('audioChange', (data) => {
console.log('Audio track changed:', data);
});
Fired when an error occurs during playback.
player.on('error', (error) => {
console.error('Playback error:', error);
// Handle error appropriately for your application
});
Here's a comprehensive example showing how to listen to multiple events:
const player = new playerjs.Player('video-iframe');
player.on('ready', async () => {
console.log('Player is ready!');
// Get initial player state
const duration = await player.getDuration();
const volume = await player.getVolume();
const isPaused = await player.getPaused();
console.log(`Duration: ${duration}s, Volume: ${volume}%, Paused: ${isPaused}`);
});
// Listen to playback events
player.on('play', () => console.log('Started playing'));
player.on('pause', () => console.log('Paused'));
player.on('ended', () => console.log('Playback finished'));
// Listen to progress events
player.on('timeupdate', (data) => {
const { seconds, duration } = data;
console.log(`${seconds.toFixed(1)}s / ${duration.toFixed(1)}s`);
});
// Listen to user interaction events
player.on('volumeChange', async () => {
const volume = await player.getVolume();
const muted = await player.getMuted();
console.log(`Volume: ${volume}%, Muted: ${muted}`);
});
player.on('seeked', (data) => {
console.log(`Seeked to: ${data.seconds}s`);
});
// Listen to display events
player.on('fullscreenChange', (data) => {
console.log(`Fullscreen: ${data.isFullScreen}`);
});
player.on('pipChange', (data) => {
console.log(`Picture-in-Picture: ${data.isPIP}`);
});
// Handle errors
player.on('error', (error) => {
console.error('Player error:', error);
});
- Receiver Implementation Guide - For implementing the Player.js spec in your video player
- Legacy Callback API - Documentation for backward-compatible callback-based methods