Iframe Stages
From this document, you’ll know:
The different iframe connection stages and their purposes.
How you can customize the UI for each stage.
At which stage you can start sending messages from the webpage to the Unreal app.
Stage 1. In Queue (stage1_inqueued
)
Triggered when the user enters the queue to connect to the app.
case "stage1_inqueued":
//add your code here
break;
Purpose: Show the first loading screen.
You can customize: Queue waiting screen (spinner, estimated wait time, branding).
Stage 2. De-Queued (stage2_deQueued
)
Triggered when the user leaves the queue and is about to start app preparation.
case "stage2_deQueued":
// loading screen 1 hides
break;
Purpose: End the queue loading UI.
You can customize: Transition animation from queue to loading phase.
Stage 3. Slot Occupied (stage3_slotOccupied
)
Triggered when the server has reserved a slot for the user’s app session.
case "stage3_slotOccupied":
// add your code here
break;
Purpose: Hide queue UI and show app loading UI.
You can customize: Loading animation, progress bar, status messages.
Stage 4. Play Button Showed Up (stage4_playBtnShowedUp
)
Triggered when the play button is available to start the Pixel Streaming session.
case "stage4_playBtnShowedUp":
loaderStep2.style.visibility = "hidden";
iframeElem.style.visibility = "visible";
let playButton = document.getElementById("playButtonParent");
playButton.click();
onPlayBtnPressed();
break;
Purpose: Hide loading, show iframe, automatically click play.
You can customize: Play screen, confirmation message before starting.
Stage 5. Play Button Pressed (stage5_playBtnPressed
)
Triggered after the play button is pressed — the app is now running and interactive.
case "stage5_playBtnPressed":
sidebar.style.visibility = "visible";
loaderStep2.style.display = "none";
iframeElem.style.visibility = "visible";
$('#iframe_1').focus();
break;
Purpose: Show sidebar and make iframe interactive.
Important: Only after this stage, you can start sending data from the webpage to the Unreal Engine app.
Other Events
ResponseFromUE4
→ Receives messages from Unreal Engine. (Triggered whenever UE app sends data to webpage.)QueueNumberUpdated
→Shows updated queue position. (E.g., from position 4 → 3 → 2 → 1.)stage3_1_AppAcquiringProgress
→ Shows download progress (only triggered if the app is not yet downloaded on the streamer machine).stage3_2_AppPreparationProgress
→ Shows preparation progress (only triggered if the app is being extracted on the streamer machine).isIframe
→ Sends confirmation that this is inside an iframe.shortCuts
→ Logs key press shortcuts.Error_Redirect
→ Handles errors and focuses iframe.
Full Code Example:
const messageHandler = (event) => {
const loaderStep1 = document.getElementById("loaderStep1");
const loaderStep2 = document.getElementById("loaderStep2");
const loaderStep3 = document.getElementById("loaderStep3");
const iframeElem = document.getElementById("iframe_1");
const sidebar = document.getElementById("sidebar");
console.log("received data event type " + event.data.type);
switch (event.data.type) {
case "ResponseFromUE4":
console.log("UE4->iframe: " + event.data.descriptor);
myHandleResponseFunction(event.data.descriptor);
break;
case "stage1_inqueued":
loaderStep1.style.visibility = "visible";
break;
case "stage2_deQueued":
// loading screen 1 hides
break;
case "stage3_slotOccupied":
loaderStep1.style.display = "none";
loaderStep2.style.visibility = "visible";
break;
case "stage4_playBtnShowedUp":
loaderStep2.style.visibility = "hidden";
iframeElem.style.visibility = "visible";
let playButton = document.getElementById("playButtonParent");
playButton.click();
onPlayBtnPressed();
break;
case "stage5_playBtnPressed":
sidebar.style.visibility = "visible";
loaderStep2.style.display = "none";
iframeElem.style.visibility = "visible";
$('#iframe_1').focus();
break;
case "_focus":
document.getElementById("iframe_1").focus();
break;
case "isIframe":
let obj = { cmd: 'isIframe', value: true };
document.getElementById("iframe_1").focus();
document.getElementById("iframe_1").contentWindow.postMessage(JSON.stringify(obj), "*");
break;
case "QueueNumberUpdated":
console.log("QueueNumberUpdated. New queuePosition: " + event.data.queuePosition);
break;
case "stage3_1_AppAcquiringProgress":
console.log("stage3_1_AppAcquiringProgress percent: " + JSON.stringify(event.data.percent));
break;
case "stage3_2_AppPreparationProgress":
console.log("stage3_2_AppPreparationProgress percent: " + JSON.stringify(event.data.percent));
break;
case "shortCuts":
console.log("Key pressed");
break;
case "Error_Redirect":
loaderStep2.style.display = "none";
iframeElem.style.visibility = "visible";
$('#iframe_1').focus();
break;
default:
console.error("Unhandled message data type");
break;
}
};
Need help?
🛠️ Contact our Support Team
💬 Join the Community on Discord
Follow us on:
Facebook | GitHub | LinkedIn | YouTube