BrightScript Engine Current Limitations

August 8, 2026 · View on GitHub

The BrightScript Engine implements the BrightScript language specification up to Roku OS 15.3. However, some features and components remain unsupported or only partially implemented. These limitations fall into three categories: features planned for future development, components that will remain as mock objects for compatibility purposes, and functionality considered outside the project's scope. The following sections detail each category and its current status.

In Scope (to be developed/fixed in future releases)

  • Roku SceneGraph framework support is implemented as an extension published separately as the brs-scenegraph NPM package. All SceneGraph nodes documented by Roku are available, however because the engine is a simulator and not a hardware emulator, expect rendering differences from a real Roku device, please open a GitHub issue if you find any problem or missing feature. These are the current known limitations:
    • The Task node is implemented but its behavior is limited:
      • For now only 30 concurrent task threads are supported per application
      • Only one port instance can be used on Task init() to observe fields
      • Rendezvous is implemented, but may not work properly for all edge cases.
      • Task nodes never spawn under the synchronous executeFile() execution model of the Node.js library — use executeApp() for Task support, see the Node.js library guide.
    • The focus change native animation on grids, lists and panel nodes is not implemented yet.
    • Some abstract base nodes are not implemented on their own and fall back to a plain Group (like the Standard Dialog framework's StdDlgAreaBase, StdDlgItemGroup and StdDlgGraphicItem).
    • Component Libraries are supported, both declared in XML (<ComponentLibrary id="..." uri="..."/>) and created at runtime via CreateObject("roSGNode", "ComponentLibrary") with the uri assigned in code. The component namespace is the library manifest's sg_component_libs_provided value (falling back to the node id when not declared), so its components are referenced as LibraryName:ComponentName. The uri may point to a local volume (pkg:/, tmp:/, ext1:/) or a remote http(s):// package (the configured CORS proxy is honored). Current limitations:
      • Libraries are fetched and compiled synchronously (the BrightScript event loop never yields, so an asynchronous load could not complete mid-execution). The node reports loadStatus = "loading" immediately and the "ready"/"failed" transition is emitted on the next render frame, so observeField("loadStatus", ...) callbacks are notified as on a real device — provided the app is pumping its screen message loop (wait(...)).
      • Inside a library, pkg:/ still resolves to the host app's package (not the library's own); reference a library's bundled scripts with relative uri paths in its component XML.
      • Library source/ global functions and intra-library component inheritance (a library component extending another library component) are not supported yet.
      • Encrypted/signed libraries are not supported (plain .zip only).
  • Audio playback is implemented via roAudioResources, roAudioPlayer components and SoundEffect, Audio nodes, but with some limitations:
    • Only one instance of roAudioPlayer component or Audio node should be used, if more are created those will share the content playlist.
    • If the roAudioPlayer component or Audio node instance is destroyed the audio keeps playing, make sure to stop the playback before discarding the object.
    • No Timed Metadata support.
  • The component roAudioMetadata only supports MP3 (for now).
  • The component roImageMetadata only supports JPEG images (for now).
  • Video playback is implemented via roVideoPlayer component and Video node, but with some limitations:
    • If the instance of roVideoPlayer or Video is destroyed the video keeps playing, make sure to stop before discarding the object.
    • The following roVideoPlayer methods are not supported, implemented as mock: setCGMS, setMaxVideoDecodeResolution, setTimedMetadataForKeys, getCaptionRenderer
    • Check what formats (container and codec) can be used on each browser, using roDeviceInfo.canDecodeVideo(), to make sure your video can be played.
    • Under Node.js/CLI there is no audio or video output: autoPlayEnabled is forced off and media events are only simulated, so apps behave as if playback never starts.
    • Subtitles are only supported for HLS streams and only in Video node, the roVideoPlayer does not support subtitles until roCaptionRenderer is implemented.
    • DASH streams and BIF (Base Index Frames) thumbnails are not yet supported.
  • The component roUrlTransfer is implemented with basic functionality but with the following limitations:
    • To make a web app access urls from domains other than the one it is hosted, the Cross-Origin Resource Sharing (CORS) browser policy requires the server called to respond with the header Access-Control-Allow-Origin, read more.
      • A simple way to overcome this limitation is to use a CORS proxy like the cors-anywhere, see customization documentation to learn how to configure brs-engine to use it.
      • If you are using a Chromium based browser (Chrome, Edge, Brave, etc) you can install this extension to bypass CORS.
    • The async methods are actually synchronous and evaluated when WaitMessage or GetMessage are called.
    • Custom/Self-Signed SSL certificates are not supported, the engine will use default browser client certificate database.
    • As custom certificates are not supported these methods are just mocked and do nothing: EnablePeerVerification, EnableHostVerification, SetCertificatesDepth.
    • Cookies are only partially supported, if EnableCookies is called and EnableFreshConnection is set to false, then Cookies from previous calls will be preserved.
    • The other Cookies related methods are just mocked and do nothing: GetCookies, AddCookies, ClearCookies.
    • The following methods are also only mocked but do nothing: EnableResume, SetHttpVersion and SetMinimumTransferRate.
  • The roStreamSocket (TCP) component performs real network I/O, on the Node.js/CLI package only.
    • Supports listening, connecting, accepting connections, and sending/receiving stream data, including roSocketEvent delivery via NotifyReadable()/Wait(), backed by a small helper process per listener/connection.
    • Connect() uses a blocking request with an 8-second timeout rather than modeling Roku's async-per-message-port connect semantics.
    • SetLinger/SetMaxSeg (ifSocketConnectionOption) are accepted and stored but not enforced — Node's net module exposes no public SO_LINGER/TCP_MAXSEG hook.
    • On the browser package roStreamSocket remains mocked, as browsers cannot open raw sockets.
  • The roDataGramSocket (UDP) component performs real network I/O, on the Node.js/CLI package only.
    • Supports binding, broadcast, send and receive, including roSocketEvent delivery via NotifyReadable()/Wait(), backed by a small per-socket helper process.
    • Real multicast group membership (JoinGroup/DropGroup) is not implemented on either platform.
    • On the browser package roDataGramSocket remains mocked, as browsers cannot open raw sockets.
  • The complete Roku OS file system is available and shared among threads.
    • Supports all volumes: pkg:, common:, tmp:, cachefs: and ext1:.
    • By default, the external volume (ext1:) and the writeable volumes (tmp: cachefs:) are limited to 32 MB each.
    • The writable volumes tmp: and cachefs: limits are configurable, see customization documentation;
    • The ext1: limit is fixed.
  • The roInput deep link events are supported, but the events related to Voice Commands are not available yet.
  • The component roAppMemoryMonitor will only return measured data in Node.JS and Chromium browsers. For browsers the memory heap info only accounts for the main thread, as WebWorkers do not have support for performance.memory API. The roAppMemoryMonitorEvent is not yet implemented.
  • The global functions Eval(), GetLastRunCompileError() and GetLastRunRuntimeError() are not available.
  • The string mod cannot be used as variable or function parameter name, because it conflicts with remainder operator Mod (Roku devices allows that).
  • Screensaver functionality is not yet implemented.
  • SDK 1.0 deprecated components are not supported, but may be implemented in the future as a new extension, allowing legacy apps to be executed.

Mocked Components and Libraries

  • RAF (Roku Ads Framework) library that exposes Roku_Ads object is mocked with the most common methods available returning static values.
  • RED (Roku Event Dispatcher) and Google IMA3 libraries are also mocked.
  • Channel Store components (ChannelStore, roChannelStore and roChannelStoreEvent) are mocked with support for the fakeServer() feature.
  • The Text to Speech components (roAudioGuide, roTextToSpeech and roMicrophone) and the Signing Algorithm components (roDSA and roRSA) are mocked: they expose their documented methods returning static values, but perform no real speech, recording or cryptographic signing.
  • Several components have their methods and events mocked, they return constant values to prevent crash. Those are mostly related to device behaviors that are not possible to replicate in a browser environment or simply not applicable to the engine.

Out of Scope

  • Roku OS User Interface.
  • Roku Channel Store features.
  • Video playback with Ads.