Video Players
ImpossibleFX renders personalized videos on demand and returns a standard video URL. You can use that URL as a source in any video player that accepts external media. See Rendering Videos for how to obtain render URLs and tokens.
This page covers the built-in ImpossibleFX Player and how to use render URLs with popular third-party players.
ImpossibleFX Player
The ImpossibleFX Player (FXPlayer) is a built-in HTML5 video player that handles rendering and playback in a single step. It streams via HLS using hls.js and includes custom controls, keyboard shortcuts, fullscreen support, and CTA overlays.
Authentication
There are two ways to use the player with ImpossibleFX rendering:
Token-based (recommended) — Your server makes the render request and obtains a token. The player then uses that token to stream the video. No API key is needed on the client — tokens do not require additional authentication. Pass the token as src and the player auto-detects it:
const player = new FXPlayer("player", {
src: "v2:eu-west-1:abc123-def456-789",
controls: true,
});
By default this streams via HLS. To play as MP4 instead, set format:
const player = new FXPlayer("player", {
src: "v2:eu-west-1:abc123-def456-789",
format: "mp4",
controls: true,
});
Direct rendering — The player calls the render backend directly from the browser using the impossible config. This is simpler but means the projectId is exposed in client-side code. By default, projects allow unauthenticated rendering, so this works without an API key:
const player = new FXPlayer("player", {
impossible: {
projectId: "YOUR-PROJECT-ID",
movie: "welcome-video",
params: { name: "Alex" },
},
});
player.play();
If your project is configured to require authentication, you must also provide an apiKey:
const player = new FXPlayer("player", {
impossible: {
projectId: "YOUR-PROJECT-ID",
movie: "welcome-video",
apiKey: "YOUR-API-KEY",
params: { name: "Alex" },
},
});
If the player calls the render backend directly and your project requires authentication, create a dedicated API key for this purpose so it can be revoked independently without affecting other integrations. For maximum security, use the token-based approach instead — your server handles authentication and the client never sees any credentials.
Quick setup
Include the player CSS and script, then create a player with your project details:
<link rel="stylesheet" href="https://cdn.impossible.io/fxplayer/v3/fxplayer.css" />
<script src="https://cdn.impossible.io/fxplayer/v3/fxplayer.js"></script>
<div id="player" style="width: 640px; height: 360px;"></div>
<script>
const player = new fxplayer.FXPlayer("player", {
impossible: {
projectId: "YOUR-PROJECT-ID",
movie: "welcome-video",
params: {
name: "Alex",
company: "Acme Inc",
},
},
});
player.play();
</script>
The player makes the render request, streams the video via HLS, and starts playback automatically.
Installation
<link rel="stylesheet" href="https://cdn.impossible.io/fxplayer/v3/fxplayer.css" />
<script src="https://cdn.impossible.io/fxplayer/v3/fxplayer.js"></script>No build step needed. The script adds fxplayer.FXPlayer to the global scope.
npm install fxplayerThen import in your code:
import { FXPlayer } from "fxplayer";
import "fxplayer/dist/fxplayer.css"; Creating a player
// Pass the ID of a container element
const player = new FXPlayer("player", options);
// Or pass the element directly
const el = document.getElementById("player");
const player = new FXPlayer(el, options);
The player creates a <video> element inside the container you provide.
Options
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
src | String | No | — | Video source URL or a render token (e.g. "v2:eu-west-1:abc123"). Tokens are auto-detected and resolved to a render URL using the format option. |
format | String | No | "auto" | Video format: "hls", "mp4", or "auto". Auto-detects from URL extension. When using a render token as src, this determines the output format (defaults to HLS). |
controls | Boolean | No | true | Show player controls. |
autoplay | Boolean | No | false | Auto-play when loaded. |
muted | Boolean | No | false | Start muted. |
poster | String | No | — | Poster image URL shown before playback. |
loop | Boolean | No | false | Loop video on end. |
keyboard | Boolean | No | true | Enable keyboard shortcuts (Space, arrows, F for fullscreen, M for mute). |
debug | Boolean | No | false | Enable debug logging to the console. |
accentColor | String | No | — | CSS color for the player UI accent (progress bar, playhead, volume). |
contextMenu | Boolean | Object | No | — | Context menu behavior. false disables it entirely (blocks Save Video, PiP, etc.). true replaces the native menu with a custom one. An object like { playPause: true, loop: true } shows only specific items. |
Impossible.io rendering options
Pass these inside the impossible object to configure server-side rendering:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
impossible.projectId | String | Yes | — | Your project's UUID. |
impossible.movie | String | Yes | — | The name of the dynamic movie to render. |
impossible.params | Object | No | {} | Key-value pairs for the movie's dynamic variables. |
impossible.region | String | No | — | Render region (e.g. "eu-west-1", "us-east-1"). Defaults to the global endpoint. |
impossible.parallel | Number | No | 1 | Number of parallel render servers. Higher values render faster. |
impossible.routingKey | String | No | "default" | Routing key for dedicated render clusters. |
impossible.apiKey | String | No | — | API key for server-side authentication. Grants access to all projects — do not use in client-side code. |
impossible.isToken | String | No | — | Alternative authentication token (istoken). Use when your backend issues scoped session tokens. |
Methods
| Method | Description |
|---|---|
play() | Starts rendering (if needed) and playback. |
pause() | Pauses playback. |
stop() | Stops playback and resets to the beginning. |
seek(seconds) | Seeks to a specific time in seconds. |
setSrc(url, format?) | Sets a video source URL or render token. Auto-detects HLS vs MP4. Tokens (e.g. "v2:...") are resolved automatically. |
setMovie(projectId, movie) | Configures project and movie for rendering. |
setParams(params) | Sets dynamic rendering parameters. |
setToken(token) | Sets a pre-obtained render token directly. |
enterFullscreen() | Enters fullscreen mode. |
exitFullscreen() | Exits fullscreen mode. |
toggleFullscreen() | Toggles fullscreen mode. |
setPosterUrl(url) | Sets a custom poster image from a URL. Returns a promise. |
addSubtitleTrack(track) | Dynamically adds a subtitle track. See Subtitles. |
destroy() | Cleans up all resources and event listeners. |
Properties
| Property | Type | Description |
|---|---|---|
currentTime | Number | Current playback position in seconds (read-only). |
duration | Number | Total video duration in seconds (read-only). |
playing | Boolean | Whether the video is currently playing (read-only). |
volume | Number | Volume level from 0 to 1 (read/write). |
muted | Boolean | Whether the video is muted (read/write). |
fullscreen | Boolean | Whether the player is in fullscreen (read-only). |
Events
Listen to player events with on() and off():
player.on("ready", () => console.log("Video ready"));
player.on("timeupdate", ({ time, duration }) => {
console.log(`${time}s / ${duration}s`);
});
player.on("error", ({ message }) => console.error(message));
| Event | Data | Description |
|---|---|---|
ready | — | Video is ready to play. |
play | — | Playback started. |
pause | — | Playback paused. |
ended | — | Video finished. |
timeupdate | { time, duration } | Fires during playback with current position. |
volumechange | { volume, muted } | Volume or mute state changed. |
fullscreenchange | { fullscreen } | Fullscreen state changed. |
error | { message, code? } | A playback error occurred. |
Token-based playback
If you’ve already made a render request and have a token, pass it as src:
// HLS streaming (default)
const player = new FXPlayer("player", {
src: "v2:eu-west-1:abc123-def456-789",
controls: true,
});
// MP4 playback
const player = new FXPlayer("player", {
src: "v2:eu-west-1:abc123-def456-789",
format: "mp4",
controls: true,
});
You can also set the token programmatically:
const player = new FXPlayer("player", { controls: true });
player.setToken("v2:eu-west-1:abc123-def456-789");
player.play();
Dynamic configuration
You can also configure the project and parameters after creating the player:
const player = new FXPlayer("player");
player.setMovie("YOUR-PROJECT-ID", "welcome-video");
player.setParams({ name: "Alex", company: "Acme Inc" });
player.play();
CTA overlays
The player supports Call-To-Action overlays that appear during or after playback. Use them for promotions, subscribe prompts, or end screens.
player.addCTA("promo", {
content: '<div class="promo">50% off — use code SAVE50</div>',
startTime: 5,
endTime: 12,
position: "center",
dismissible: true,
});
CTA options
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
content | String | HTMLElement | Yes | — | HTML string or DOM element to display inside the overlay. |
startTime | Number | No | — | When to show the CTA (in seconds). Omit for an end-screen CTA that appears when the video finishes. |
endTime | Number | No | — | When to hide the CTA (in seconds). Omit to keep it visible until dismissed. |
position | String | No | "center" | Overlay position: "center", "top", or "bottom". |
dismissible | Boolean | No | true | Show a close button so the viewer can dismiss the CTA. |
pauseOnShow | Boolean | No | false | Pause the video when this CTA is shown. |
className | String | No | — | Custom CSS class to add to the overlay element. |
CTA methods
| Method | Description |
|---|---|
addCTA(id, options) | Registers a CTA overlay. |
removeCTA(id) | Removes a CTA by its ID. |
showCTA(id) | Manually shows a CTA. |
hideCTA(id) | Manually hides a CTA. |
CTA events
| Event | Data | Description |
|---|---|---|
cta:show | { id } | A CTA overlay became visible. |
cta:hide | { id } | A CTA overlay was hidden. |
cta:click | { id } | The viewer clicked a CTA. |
CTA patterns
Timed promo — visible between 5s and 12s:
player.addCTA("promo", {
content: '<div class="banner">Limited offer!</div>',
startTime: 5,
endTime: 12,
position: "center",
});
Persistent pill — appears at 3s, stays until dismissed:
player.addCTA("subscribe", {
content: '<div class="pill">Subscribe</div>',
startTime: 3,
position: "bottom",
dismissible: true,
});
End screen — shown when the video finishes, pauses playback:
player.addCTA("end-screen", {
content: '<div class="end">Thanks for watching! <button>Sign up</button></div>',
position: "center",
pauseOnShow: true,
dismissible: false,
});
Subtitles
New in 3.3.0The player supports WebVTT subtitles with a built-in settings panel for font size, color, background, and position. Settings are persisted in localStorage.
Basic usage
const player = new FXPlayer("player", {
src: "https://example.com/video.mp4",
subtitles: {
tracks: [
{ src: "english.vtt", srclang: "en", label: "English" },
{ src: "spanish.vtt", srclang: "es", label: "Español" },
],
},
});
Subtitle options
Pass these inside the subtitles object:
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
subtitles.tracks | SubtitleTrack[] | No | [] | Array of subtitle tracks. Each track has src (VTT URL), srclang (language code), and label (display name). |
subtitles.storageKey | String | No | "fxplayer" | Custom localStorage key prefix for persisting subtitle settings (font size, colors, position). |
SubtitleTrack
Each track object has:
| Property | Type | Description |
|---|---|---|
src | String | URL to a WebVTT (.vtt) file. |
srclang | String | Language code (e.g. “en”, “es”, “de”). |
label | String | Display name shown in the track selector (e.g. “English”). |
Adding tracks dynamically
player.addSubtitleTrack({
src: "https://example.com/french.vtt",
srclang: "fr",
label: "Français",
});
Settings panel
When subtitles are configured, the player shows a CC button and a settings (gear) button in the control bar. The settings panel lets viewers customize:
- Track — choose between available subtitle tracks or turn them off
- Font size — 50% to 200%
- Font color — White, Yellow, Cyan, Green
- Background — Black (75%, 50%, 25% opacity) or transparent
- Position — Bottom or Top
All settings are saved to localStorage and restored on next visit.
Impossible.io integration
When using the player with Impossible.io rendering, subtitle VTT files can be lazily loaded — the player fetches the VTT URL only when the viewer enables subtitles.
const player = new FXPlayer("player", {
impossible: {
projectId: "YOUR-PROJECT-ID",
movie: "welcome-video",
params: { name: "Alex" },
},
subtitles: {
tracks: [
{ src: "auto", srclang: "en", label: "English" },
],
},
});
Poster images
Set a custom poster image from any URL:
const player = new FXPlayer("player", {
src: "https://example.com/video.mp4",
});
// Set poster from a URL
await player.setPosterUrl("https://example.com/poster.jpg");
This works alongside the static poster option — use poster for a known URL at construction time, and setPosterUrl() when the poster URL is determined later.
Examples
Simple MP4 playback with controls and event logging.
HLS StreamingHLS stream with auto-format detection via hls.js.
Impossible.ioServer-side rendering integration with the Render API.
CTA OverlaysTimed promos, persistent pills, and end-screen CTAs.
Iframe EmbedEmbed via iframe with URL params, CTAs, and postMessage API.
SubtitlesWebVTT subtitles with CC toggle, settings panel, and dynamic tracks.
Google AnalyticsGA4 integration with play/pause/complete events and milestone tracking.
The player uses HLS streaming by default via hls.js. HLS is supported natively on Safari and iOS; on Chrome, Firefox, and Edge the bundled hls.js polyfill handles playback automatically.
JW Player
JW Player is a widely-used commercial video player with HLS support, analytics, and ad integrations.
Using a token-based URL:
<div id="player"></div>
<script src="https://cdn.jwplayer.com/libraries/YOUR-PLAYER-KEY.js"></script>
<script>
const token = "v2:eu-west-1:abc123-def456-789";
jwplayer("player").setup({
sources: [
{
file: `https://render.impossible.io/v2/render/${token}.m3u8`,
type: "hls",
},
],
width: 640,
height: 360,
});
</script>
You can also use a direct URL instead of tokens — just pass the v1 render URL as the file source.
JW Player has native HLS support, so use .m3u8 for adaptive bitrate streaming. For a simple setup, .mp4 works across all configurations.
Video.js
Video.js is the most popular open-source HTML5 video player, with a large plugin ecosystem and full HLS/DASH support.
Using a token-based URL:
<link href="https://vjs.zencdn.net/8.10.0/video-js.css" rel="stylesheet" />
<script src="https://vjs.zencdn.net/8.10.0/video.min.js"></script>
<video
id="my-video"
class="video-js"
controls
width="640"
height="360"
>
</video>
<script>
const token = "v2:eu-west-1:abc123-def456-789";
const player = videojs("my-video", {
sources: [
{
src: `https://render.impossible.io/v2/render/${token}.mp4`,
type: "video/mp4",
},
],
});
</script>
You can also use a direct URL as the source — just pass the v1 render URL in the <source> tag.
HLS streaming with Video.js:
Video.js includes HLS support via its built-in VHS (Video.js HTTP Streaming) library:
const token = "v2:eu-west-1:abc123-def456-789";
const player = videojs("my-video", {
sources: [
{
src: `https://render.impossible.io/v2/get/${token}.m3u8`,
type: "application/x-mpegURL",
},
],
});
Wistia
Wistia is a video platform focused on marketing and analytics. You can embed external video URLs using Wistia’s iframe embed or their Player API.
Using a token-based URL with an iframe:
<iframe
src="https://fast.wistia.net/embed/iframe/WISTIA-VIDEO-ID"
width="640"
height="360"
allowfullscreen
></iframe>
To use ImpossibleFX videos with Wistia, upload the rendered video via the Wistia Upload API:
# 1. Render the video
TOKEN=$(curl -s -X POST https://render.impossible.io/v2/render/YOUR-PROJECT-ID \
-H "Content-Type: application/json" \
-d '{"movie": "welcome-video", "params": {"name": "Alex"}}' \
| jq -r '.token')
# 2. Upload the render URL to Wistia
curl -X POST "https://upload.wistia.com/api/v1/medias" \
-H "Authorization: Bearer YOUR-WISTIA-API-TOKEN" \
-d "url=https://render.impossible.io/v2/render/${TOKEN}.mp4"
Wistia will fetch the video from the render URL and host it on their platform, giving you access to their analytics, heatmaps, and embed tools. You can also pass a direct URL instead of the token-based URL.
Vimeo
Vimeo is a professional video hosting platform. Since Vimeo’s player only plays Vimeo-hosted content, the integration involves uploading your rendered video to Vimeo, then embedding it with their player.
Upload via the Vimeo API:
# 1. Render the video
TOKEN=$(curl -s -X POST https://render.impossible.io/v2/render/YOUR-PROJECT-ID \
-H "Content-Type: application/json" \
-d '{"movie": "welcome-video", "params": {"name": "Alex"}}' \
| jq -r '.token')
# 2. Create a video on Vimeo with a pull URL
curl -X POST "https://api.vimeo.com/me/videos" \
-H "Authorization: Bearer YOUR-VIMEO-ACCESS-TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"upload\": {
\"approach\": \"pull\",
\"link\": \"https://render.impossible.io/v2/render/${TOKEN}.mp4\"
},
\"name\": \"Personalized Video for Alex\"
}"
Vimeo will pull the rendered video from the ImpossibleFX URL and host it. The response includes the Vimeo video URI which you can use to build an embed:
<iframe
src="https://player.vimeo.com/video/VIMEO-VIDEO-ID"
width="640"
height="360"
frameborder="0"
allowfullscreen
></iframe>
You can also pass a direct URL as the link value instead of the token-based URL.
For high-volume Vimeo uploads, consider using ImpossibleFX’s built-in upload destinations to send rendered videos directly to cloud storage, then pull from there into Vimeo.