Roku Integration Skill
Integration conventions for the StreamLayer Roku SDK that keep an AI agent's diff small and correct — copy in the SLSDK module, place one SLView over your Video, initialize with apiKey + sdkUri, drive pause ads, observe visibility, hand over the remote via the focus field, and the minimal-diff rules for a BrightScript / SceneGraph channel.
StreamLayer Roku SDK Integration
How to wire StreamLayer into an existing Roku channel cleanly. Follow the
rules — they prevent the common over-integration mistakes on BrightScript /
SceneGraph.
This page is the human-readable form of the streamlayer-roku-integration agent
skill. Reference it from your AI agent alongside the streamlayer-docs MCP — see
Integrate with an AI Agent.
BrightScript & SceneGraphYou integrate through a small, self-contained
SLSDKmodule (SLView+
SLManager) that you copy into your channel. It hosts the StreamLayer UI
bundle — loaded dynamically at runtime — and exposes oneSLViewnode you
drive withcallFunc
and observable fields. You never reach into the SDK's internal components.
Pick the package
SLSDKmodule — the integration surface: a self-contained folder
(SLView+SLManager) you copy into your channel'scomponents/.SLView
is the node your app talks to;SLManagerloads the SDK and drives it. Copy it
as-is from the roku-sdk-sample.StreamLayerSDK.pkg— the StreamLayer UI bundle (the StreamLayer Element).
It is CDN-hosted and loaded dynamically at runtime from thesdkUriyou
pass toinitialize(via a SceneGraphComponentLibrary), not compiled in — so
sdkUriis a required init parameter, kept in settings/config rather than
hardcoded. Point it at the StreamLayer CDN URL in production; you can override it
with a locally served package for debugging.- Underlying node: internally
SLManagercreates aStreamLayerSDK:StreamLayer
node and callsinitSdkon it. You normally stay at theSLViewlayer; the
raw node's methods (initSdk,setFocus,showPauseAd, …) are documented in
the API Reference. - Requirements: Roku OS 11.5+, a SceneGraph channel, and a
Videonode that
exposescontrolandstate.
Rules (do this)
-
Copy in the
SLSDKmodule; place oneSLViewover yourVideo. Copy the
self-containedSLSDKfolder intocomponents/. In the SceneGraph component
that owns your<Video>, add<SLView id="SLView" visible="false" />as a
sibling rendered over the video. OneSLViewper player screen — not in the
main scene / app root, and not one per interactive feature.- ❌ Placing
SLViewinMainScene/ a global, away from the player. - ✅
<SLView id="SLView" visible="false" />beside<Video>in the player screen.
- ❌ Placing
-
Initialize once with
initialize, passingapiKey,sdkUriand your
Video. Callinitializea single time; it loads the UI bundle from
sdkUriand starts the SDK. Then bind the event withsetEvent(eventId), and
re-callsetEventwhenever playback switches content. KeepapiUrl/sdkUri
in your settings or globals, not inline literals.- ❌ Omitting
sdkUri(nothing loads); callinginitializemore than once perSLView. - ✅
initialize({ apiKey, sdkUri, playerRef: m.player, … })→setEvent("SL_EVENT_ID_HERE").
m.slView = m.top.findNode("SLView") m.slView.callFunc("initialize", { apiKey: "SL_SDK_KEY_HERE" sdkUri: getGlobal("sdkUri") playerRef: m.player apiUrl: getGlobal("apiUrl") isLoggingEnabled: true isAnalyticsEnabled: true isVastModeEnabled: false }) m.slView.callFunc("setEvent", "SL_EVENT_ID_HERE") - ❌ Omitting
-
Drive ads and playback through
SLView; observe state, don't poll.
CallsetPauseState(true/false)when the host player pauses/resumes. Close a
pause ad withclosePauseAd(), and clear all surfaces withcloseOverlay()
(for example on the Back key). ObserveisResumeRequestedand resume
your ownVideowhen it flipstrue— the SDK signals resume, it does not
control your player. ObserveisPromoVisible/isNotificationVisible/
isPauseAdVisibleto coordinate host UI. UseobserveField, never a polling
loop.m.slView.observeField("isResumeRequested", "onResumeRequested") sub onResumeRequested(event as object) if event.getData() = true then m.player.control = "resume" end subThere are two entry points, one per lifecycle, and which one you call
decides who owns the stream:showPauseAd(params)showAd(params)Stream already stopped by your app still playing Who resumes it your app, when the viewer leaves the ad card, by itself Shapes PauseVastFullBleed,PauseVastAd,PauseAdSidebar11,PauseAdSidebar21SideBar21,LBar21,SideBarImageOnly,LBarImageOnly,SideBySideClosed with closePauseAd()closeOverlay()Both take
{ vastUrl, type, isNotificationEnabled }: the tag to load, the shape
to draw, and whether to tease the ad with a notification the viewer opens it
from. Omittingtypeleaves the choice to the SDK, which picks a shape from the
creative. In full modeshowPauseAd()takes nothing and the SDK serves its own
pause ad.- ❌
showPauseAd(vastUrl)— the bare-URL form is gone; pass an object. - ❌ Asking for a standard shape through
showPauseAd, or a pause shape throughshowAd.
- ❌
-
Wait for
isEventReadybefore asking for an ad. Resolving the event takes a
network round trip, sosetEventreturns long before the SDK can serve
anything.showAdcalled earlier is ignored — observeisEventReadyand
ask from there.- ❌ Calling
showAdon the line aftersetEvent. - ✅
m.slView.observeField("isEventReady", "onEventReady"), then ask inside it.
m.slView.observeField("isEventReady", "onEventReady") sub onEventReady() m.slView.callFunc("showAd", { vastUrl: getGlobal("vastUrl") type: "LBar21" }) end sub - ❌ Calling
-
Hand over the remote through the
focusfield, gated on visibility. Give
the overlay the remote withm.slView.focus = true, and take it back with
m.slView.focus = false— but only give focus whilem.slView.visibleis
true, otherwise the remote strands on a hidden node. On Back with an
overlay up, callcloseOverlay()and return focus to your own UI. Route focus
throughSLView; don't callsetFocuson the SDK's internal nodes.- ❌
m.slView.focus = truewhile it's hidden; trapping the remote while a surface is up. - ✅
if m.slView.visible then m.slView.focus = m.top.focus.
- ❌
-
Auth is automatic; keep credentials and the diff simple. On Roku there is
no separate login call and no token to forward — pass theapiKeyto
initializeand the SDK authenticates anonymously itself. The SDK API Key is a
public, client-side value from StreamLayer Studio
— amanifestor settings value is fine; don't add secret scaffolding or a
login screen. Keep the diff minimal: drop inSLSDK, add oneSLView, and
wire only thecallFuncmethods and observers your features use — don't
restructure the channel or wrap the player screen. Tearing the SDK down with
disposeSdk()makes it inactive; callinitialize()again before returning to
a StreamLayer-enabled screen.
Gotchas
- Nothing renders / blank overlay. The UI bundle is loaded from
sdkUriat
runtime — a wrong or missingsdkUri(orapiKey) means nothing loads. Also
check thatsetEventran and theVideonode exposescontrolandstate. - A
callFuncthat does nothing is not a crash, and may not even be logged.
A method that exists but is unavailable in the current mode (or before
initialize) is a logged no-op — look for the reason in the console. But a
callFuncnaming a function the component does not declare is silent: Roku
neither throws nor logs, the call simply does nothing. If a call has no effect
and no log line, check the spelling and that the function is declared on the
node's interface before suspecting your logic. - Ads asked for too early are dropped.
showAdbeforeisEventReadyis
ignored — the SDK has no event to attach the ad to yet. - Pause-ad mode matters. In full mode
showPauseAd()takes nothing and the SDK
serves its own pause ad; in VAST mode it takes{ vastUrl, type, isNotificationEnabled }.setVastModeEnabledswitches modes at runtime but only
affects the next request. - Left-paused stream. You own playback — resume your
Videowhen
isResumeRequestedflipstrue. The SDK signals; it won't resume for you. - Focus stranded on a hidden node. Only hand
SLViewfocus while it's
visible; guard everym.slView.focus = truewith anm.slView.visiblecheck. - Stay at the
SLViewlayer. Talk toSLView(or, if you skip the wrapper,
theStreamLayernode'scallFunc); don't reach intoPromoPresenter/
NotificationPresenter/PauseAdPresenteror the SDK's managers — they're
internals. - Authoritative API details: the
streamlayer-docsMCP (search/fetch) and
the API Reference.
Related
- Integrate with an AI Agent — Connect the MCP and prompt your agent to run this integration
- Roku SDK Overview & Prerequisites — Requirements, SDK API Key, and prerequisites
- Roku SceneGraph Integration Guide — Full setup: SLSDK module, initialization, pause ads, visibility, and focus
- API Reference — Every public
callFuncmethod and observable field on the underlying node - Custom Pause Ads (VAST) — Host-supplied VAST pause ads
Updated 15 days ago
