Getting started
Havik Player plays Oddin's live esports streams (low-latency HLS with DRM) in any modern browser. There are three ways to integrate it:
| Mode | Best for | You provide | The SDK owns |
|---|---|---|---|
| Iframe embed | Fastest drop-in, CMS pages, no build step | an iframe slot | everything, behind a postMessage API |
| Managed player | Full control of look & behavior in your app | a <video> element | hls.js, LL-HLS tuning, DRM/EME, retries, controls |
| Bring your own player | Existing player stacks (hls.js, Shaka, wrapper) | the player + <video> | auth → catalog → playback resolution + DRM helpers |
All three modes need the same two things:
- a match URN, which identifies the stream, e.g.
od:match:1234 - a publishable api-key (
pk_live_…/pk_test_…), safe to ship in browser JavaScript because it only works from allow-listed origins
1. Get a key and a match
Onboarding is handled by Oddin: email havik-support@oddin.gg to get an api-key scoped to the tournaments you serve. Match URNs come from your odds-feed integration or from the SDK's catalog helpers.
2. Get your origins allow-listed
The api-key is publishable, and its only guardrail is a server-side origin allow-list. Until your origins are onboarded, every request fails its CORS preflight and nothing plays. Send havik-support the exact origins you serve from, for both of:
- the key's AllowedOrigins: every origin the player runs on (and AllowedIframeParents for iframe embeds: the parent page's origin)
- the DRM service's CORS allow-list, the same origins again. Miss this one and the license request dies in preflight, which looks like black video with no error at all
Never ship a wildcard
A ["*"] allow-list disables the only guardrail on a publishable key. Don't ask for one in production.
Allow a few minutes for propagation. Include http://localhost:5173 (or your dev port) if you want local development to work, and https://player-dev.oddin-video.gg if you want to exercise your key in the playground. Once the catalog loads there, your key and your allow-listing are known-good.
3. Point at the right environment
baseUrl selects the Havik environment. Don't hard-code it deeper than a config value. Serve it from your backend, and keep the backend current from the endpoint directory of the environment you target. This site's directory is status-dev.oddin-video.gg/v1/endpoints.json; the URL for the other environment is provided at onboarding.
Every sample on this site targets https://feed-dev.oddin-video.gg.
| Environment | API baseUrl | Hosted embed page |
|---|---|---|
| Integration | https://feed-dev.oddin-video.gg | https://player-dev.oddin-video.gg/embed/ |
| Production | https://feed.oddin-video.gg | https://player.oddin-video.gg/embed/ |
Test keys (pk_test_…) work against integration; production keys (pk_live_…) against production. A key on the wrong environment fails with UNAUTHORIZED.
Live service health for every Havik component is published at status-dev.oddin-video.gg, the status page for this site's environment. The other one is provided at onboarding.
4. Install
For the iframe embed there is nothing to install. For the SDK modes:
npm install @oddin-gg/havik-player<!-- Exposes window.HavikPlayer. Pin an exact version in production. -->
<script src="https://cdn.jsdelivr.net/npm/@oddin-gg/havik-player@1/dist/havik-player.global.js"></script>TypeScript definitions ship with the package, and the types are the source of truth for the API surface.
5. First playback
import { createPlayer } from '@oddin-gg/havik-player';
const player = await createPlayer({
video: document.querySelector('video')!,
baseUrl: 'https://feed-dev.oddin-video.gg',
matchUrn: 'od:match:1234',
credential: { apiKey: 'pk_test_…' },
autoplay: true,
muted: true, // required for reliable autoplay under browser sound policies
waitForLive: true, // arm on an upcoming match, auto-play at go-live
});
player.on('statechange', (s) => console.log('state:', s));
player.on('error', (e) => console.error(e.code, e.httpStatus, e.message));If the match hasn't started, waitForLive arms the player and it attaches the moment the stream goes live. See the managed player guide.
Browser support
Chrome, Firefox, and Edge play via hls.js + Widevine. Safari and iOS play via hls.js (ManagedMediaSource) or native HLS with FairPlay. Use detectDrmSupport() to check a browser before rendering the player. See the DRM guide.
Building a native app instead of a web page? See the iOS SDK (preview) and Android SDK (in development).
Next steps
- Embed the hosted player: the five-minute integration
- Managed player: options, events, live pickup
- Controls & theming: make it look like your product
- Troubleshooting: the common failures and their fixes