Embedding
Embedding the player in an <iframe> on a third-party page, or controlling an embedded player from the host page.
This covers the postMessage protocol for cross-frame communication and browser permission requirements.
Why iframes
Embedding a player in an iframe isolates it from the host page’s JavaScript, CSS, and security context. It is the standard model for embed codes (YouTube, Vimeo, Spotify).
The player itself does not require iframe embedding, you can drop it directly in any page. The iframe model is for cases where you are distributing an embed to third-party sites that cannot or should not install the SDK.
Setting up the embed page
Use EmbedPlugin, it handles the full postMessage protocol (inbound commands, outbound events, origin validation) without boilerplate.
The manual window.addEventListener approach in the example below is what EmbedPlugin replaces.
With EmbedPlugin (recommended)
<!-- embed.html -->
<!DOCTYPE html>
<html>
<head>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #000;
overflow: hidden;
}
#player {
width: 100vw;
height: 100vh;
}
</style>
</head>
<body>
<div id="player"></div>
<script type="module">
import { nmplayer } from '@nomercy-entertainment/nomercy-video-player';
import {
DesktopUiPlugin,
EmbedPlugin,
} from '@nomercy-entertainment/nomercy-video-player/plugins';
const player = nmplayer('embed')
.addPlugin(DesktopUiPlugin)
.addPlugin(EmbedPlugin, {
allowedOrigins: ['https://yoursite.com', 'https://partner.example.com'],
// forwardEvents defaults to: ['ready','play','pause','ended','time','volume','mute']
})
.setup({ playlist: [] });
await player.ready();
</script>
</body>
</html>
EmbedPlugin listens for nm:command messages from allowed origins and dispatches them to the player.
It forwards player events as nm:event messages to the host page.
postMessage protocol
Inbound commands (host → embed):
action | Effect |
|---|---|
'play' | player.play() |
'pause' | player.pause() |
'stop' | player.stop() |
'seek' | player.time(time) |
'volume' | player.volume(level), 0–100 scale |
'mute' | player.mute() |
'unmute' | player.unmute() |
'next' | player.next() |
'previous' | player.previous() |
Message shape: { type: 'nm:command', action: '...', ...payload }.
Outbound events (embed → host):
Message shape: { type: 'nm:event', name: '...', data: { ... } }.
window.addEventListener('message', (event) => {
if (event.origin !== 'https://player.yoursite.com') return;
const { type, name, data } = event.data;
if (type !== 'nm:event') return;
// name: 'ready' | 'play' | 'pause' | 'ended' | 'time' | 'volume' | 'mute'
if (name === 'ended') showNextVideoSuggestions();
if (name === 'time') updateProgressBar(data.time);
});
Host page integration
All messages between host and iframe use the nm:command / nm:event protocol defined by EmbedCommand and EmbedEventMessage.
<!-- Host page on a third-party site -->
<iframe
id="nomercy-player"
src="https://player.yoursite.com/embed.html"
allow="autoplay; fullscreen; picture-in-picture"
allowfullscreen
style="width: 100%; aspect-ratio: 16/9; border: none;"
></iframe>
<script>
const iframe = document.getElementById('nomercy-player');
const PLAYER_ORIGIN = 'https://player.yoursite.com';
// Control the player using nm:command messages:
function sendCommand(action, extra = {}) {
iframe.contentWindow.postMessage(
{ type: 'nm:command', action, ...extra },
PLAYER_ORIGIN,
);
}
function play() { sendCommand('play'); }
function pause() { sendCommand('pause'); }
function next() { sendCommand('next'); }
function previous() { sendCommand('previous'); }
function seek(time) { sendCommand('seek', { time }); }
function volume(level) { sendCommand('volume', { level }); }
// Listen to nm:event messages from the iframe:
window.addEventListener('message', (event) => {
if (event.origin !== PLAYER_ORIGIN) return;
const msg = event.data;
if (msg?.type !== 'nm:event') return;
if (msg.name === 'ended') {
showNextVideoSuggestions();
}
if (msg.name === 'time') {
updateProgressBar(msg.time);
}
});
</script>
Autoplay and the allow directive
Browsers block autoplay with audio unless the user has interacted with the page.
The allow attribute on the <iframe> grants the embedded player the same autoplay permission as the host:
<iframe allow="autoplay; fullscreen; picture-in-picture" ...></iframe>
Without allow="autoplay", the player can only autoplay muted.
If your use case requires autoplay with sound, the user must interact with the host page first (not just the iframe).
Fullscreen
Fullscreen requires allow="fullscreen" on the iframe and allowfullscreen attribute.
When fullscreen is triggered inside the iframe, the <video> element expands to fill the screen, not the iframe.
This is browser-correct behavior.
<iframe allow="autoplay; fullscreen; picture-in-picture" allowfullscreen ...></iframe>
Origin security
Always validate event.origin in your message handler.
Never use '*' as the target origin for sensitive commands (like loading authenticated content):
const ALLOWED_ORIGINS = new Set(['https://yoursite.com', 'https://app.yoursite.com']);
window.addEventListener('message', (event) => {
if (!ALLOWED_ORIGINS.has(event.origin)) return; // drop unknown origins
// process command
});
What to read next
- Advanced: Multi-Instance, multiple players in one non-iframe page
- Advanced: Distributed Playback, syncing across frames and devices
- Recipes: Auth and Tokens, securing embed streams with bearer tokens