Iframe embed
The lowest-touch integration: point an iframe at the hosted player page on player-dev.oddin-video.gg and, optionally, control it over an origin-verified postMessage bridge. Playback runs isolated in its own browsing context, with no build step and no dependency on your page's framework.
Before anything plays
The parent page's origin must be on your key's AllowedIframeParents allow-list, and on the DRM CORS allow-list. See Getting started.
Plain iframe, no JavaScript
<iframe
src="https://player-dev.oddin-video.gg/embed/?baseUrl=https%3A%2F%2Ffeed-dev.oddin-video.gg&matchUrn=od%3Amatch%3A1234&apiKey=pk_test_your_key"
allow="autoplay; encrypted-media; picture-in-picture; fullscreen"
allowfullscreen
referrerpolicy="no-referrer"
style="border: 0; width: 100%; aspect-ratio: 16 / 9"
></iframe>The page boots a fully managed player (branded controls, go-live pickup, DRM, autoplay muted) and needs three query parameters. URL-encode the values:
| Parameter | Example | Notes |
|---|---|---|
baseUrl | https://feed-dev.oddin-video.gg | Required. Havik API environment. |
matchUrn | od:match:1234 | Required. The match to play. |
apiKey | pk_test_… | Required. See the api-key and the URL. |
parentOrigin | https://your-site.example | Enables the postMessage bridge. Without it the bridge stays disabled (fails closed). |
muted | false | Starts muted unless explicitly false. |
Without parentOrigin the player still runs, but the parent page cannot control it, which is fine for a plain drop-in. allow="autoplay; encrypted-media …" is required: without encrypted-media DRM cannot start inside a cross-origin iframe.
SDK embed with mountEmbed recommended
mountEmbed builds the iframe for you: the correct allow list, sandbox, referrer policy and URL encoding. It also gives you a typed handle over the postMessage bridge:
import { mountEmbed } from '@oddin-gg/havik-player';
const embed = mountEmbed({
container: document.querySelector('#player')!,
src: 'https://player-dev.oddin-video.gg/embed/',
baseUrl: 'https://feed-dev.oddin-video.gg',
matchUrn: 'od:match:1234',
apiKey: 'pk_test_your_key', // dev/demo only — see below
onEvent: (msg) => {
if (msg.type === 'oddin:state') console.log('state:', msg.state);
if (msg.type === 'oddin:error') console.error(msg.code, msg.message);
},
});
embed.play();
embed.setMuted(false);
embed.load('od:match:5678'); // switch match in place
// embed.destroy();The handle mirrors the player API: play, pause, setMuted, setVolume, setQuality, setMaxBitrate, setAudioTrack, setTextTrack, seekToLive, enterPip/exitPip, retry, load and destroy. on(cb) subscribes to every frame event. Track lists arrive as an oddin:tracks event; use them to build quality/caption menus. Full option and message tables: API reference → Mode C.
Both ends verify event.origin on every message, and the embed fails closed: with no trusted parentOrigin it neither accepts commands nor posts events. The api-key is never accepted over postMessage.
The api-key and the URL
The hosted page reads its config from the query string, which places the api-key in the iframe URL. The key is publishable by design: the origin allow-list is what protects it, and the page sets referrerpolicy="no-referrer" so the URL never leaks through Referer. For development and demos that is enough.
For production, decide by your threat model:
Hosted page + query key. Acceptable when you're comfortable that the key is origin-locked anyway. The URL, and so the key, is visible in the page source and in devtools, as is a key shipped in your own bundle.
Self-hosted embed page + injected key strictest Serve the page from your own origin and inject the config server-side. The query string is then ignored entirely, so the key never appears in any URL and a tampered
?baseUrl=can't redirect it:html<script> window.HAVIK_EMBED_CONFIG = { baseUrl: 'https://feed-dev.oddin-video.gg', matchUrn: 'od:match:1234', apiKey: 'pk_live_…', // injected by your server parentOrigin: 'https://your-site.example', }; </script> <script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@1.3.0/dist/havik-player.global.js"></script> <script> HavikPlayer.bootEmbed(); </script>A ready-to-host template lives in the repository at
embed/index.html. PointmountEmbed'ssrcat your page and omitapiKey. Pin the exact SDK version (and add Subresource Integrity) so the embed page can't skew ahead of your host SDK. You can also skip that manual step. Each npm release carries a copy of the template atnode_modules/@oddin-gg/havik-player/embed/index.html, and each GitHub release attaches one too. Both come pinned to that release, with the integrity hash included.
Sizing
The player fills the iframe, so give the iframe a real size. The usual responsive pattern is aspect-ratio: 16 / 9; width: 100%. For a fixed-height slot, the video letterboxes on #0e0f13 (themeable when self-hosting).
Limits of the hosted page
The hosted page runs the player with Oddin's default skin and copy. If you need your own theme, overlays, or analytics hooks inside the frame, you have two options. Self-host the embed page, which is one HTML file, or move to the managed player. The managed player has the same capabilities in your own DOM.