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.
This page covers how to get a render URL and use it with popular third-party players.
Getting a render URL
There are two ways to get a playable video URL from ImpossibleFX.
Token-based URL (recommended)
First, make a render request to get a token:
curl -X POST https://render.impossible.io/v2/render/YOUR-PROJECT-ID \
-H "Content-Type: application/json" \
-d '{"movie": "welcome-video", "params": {"name": "Alex"}}'
The API returns a token:
{ "token": "v2:eu-west-1:abc123-def456-789" }
Build the video URL using the token and your desired format:
| Format | URL |
|---|---|
| MP4 | https://render.impossible.io/v2/render/{token}.mp4 |
| HLS | https://render.impossible.io/v2/render/{token}.m3u8 |
| DASH | https://render.impossible.io/v2/render/{token}.mpd |
| WebM | https://render.impossible.io/v2/render/{token}.webm |
You can also use different retrieval modes — render (full render first), get (progressive download), or play (stream immediately).
Direct URL
For simpler use cases, render and retrieve in a single URL with no token step:
https://render.impossible.io/v1/render/YOUR-PROJECT-ID/welcome-video.mp4?name=Alex&company=Acme
Pass dynamic parameters as query strings. This is useful for email campaigns, CRM templates, or anywhere you need a single self-contained URL.
Token-based URLs give you more control — you can choose retrieval modes, use HLS/DASH streaming, and reuse tokens. Direct URLs are simpler and work anywhere a static URL is expected.
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.
Quick setup
Include the player CSS and script, then create a player with your project details:
<link rel="stylesheet" href="https://cdn.impossible.io/support/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/support/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. Not needed when using the impossible config. |
format | String | No | "auto" | Video format: "hls", "mp4", or "auto". Auto-detects from URL extension. |
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. |
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. |
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 directly. Auto-detects HLS vs MP4. |
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. |
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, you can skip the project/movie/params setup:
const player = new FXPlayer("player");
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,
});
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.
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>
Using a direct URL:
<script>
jwplayer("player").setup({
file: "https://render.impossible.io/v1/render/YOUR-PROJECT-ID/welcome-video.mp4?name=Alex",
width: 640,
height: 360,
});
</script>
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>
Using a direct URL:
<video
id="my-video"
class="video-js"
controls
width="640"
height="360"
>
<source
src="https://render.impossible.io/v1/render/YOUR-PROJECT-ID/welcome-video.mp4?name=Alex"
type="video/mp4"
/>
</video>
<script>
const player = videojs("my-video");
</script>
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.
Using a direct URL:
curl -X POST "https://upload.wistia.com/api/v1/medias" \
-H "Authorization: Bearer YOUR-WISTIA-API-TOKEN" \
-d "url=https://render.impossible.io/v1/render/YOUR-PROJECT-ID/welcome-video.mp4?name=Alex"
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>
Using a direct 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/v1/render/YOUR-PROJECT-ID/welcome-video.mp4?name=Alex"
},
"name": "Personalized Video for Alex"
}'
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.