npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

growthbook-roku

v1.3.1

Published

Official GrowthBook SDK for Roku/BrightScript applications

Readme

GrowthBook Roku SDK

License: MIT BrightScript Roku Version Production Ready

Official GrowthBook SDK for Roku/BrightScript applications. Add feature flags and A/B testing to your Roku channels with a simple, lightweight SDK.

Current Version: v1.3.1 — View Changelog | Integration Guide | Quick Start

Features

  • 🚀 Lightweight - Core SDK is ~50KB, minimal memory footprint
  • Fast - Feature evaluation in <1ms, optimized for Roku devices
  • 🎯 Full Spec Alignment - Supports v1.3.0 logic (filters, namespaces, ranges, and truthiness)
  • 🧪 A/B Testing - Run experiments with accurate traffic splits and holdout groups
  • 🔄 Consistent Bucketing - Same user always sees same variation (cross-platform)
  • 📊 Analytics Ready - Detailed experiment tracking and feature usage callbacks
  • 🔒 Secure - Support for encrypted feature payloads
  • 📦 Ropm Support - Easy dependency management via ropm
  • 🎨 No Dependencies - Pure BrightScript, works on all Roku devices (Roku 3+, OS 9.0+)

Quick Start

1. Installation

Manual Installation

Copy GrowthBook.brs from the source/ directory to your channel's source/ folder:

your-roku-channel/
├── source/
│   ├── main.brs
│   └── GrowthBook.brs  ← Add this file
└── manifest

Installation with ropm (Recommended)

If you are using ropm for dependency management:

ropm install growthbook-roku

2. Initialize

' In your main.brs or scene component
function initGrowthBook() as object
    config = {
        apiHost: "https://cdn.growthbook.io",
        clientKey: "sdk-abc123",  ' Your GrowthBook client key
        attributes: {
            id: "user-123",
            deviceType: "roku",
            premium: true
        }
    }
    
    gb = GrowthBook(config)
    
    ' Load features from GrowthBook API
    if gb.init()
        print "GrowthBook ready!"
    end if
    
    return gb
end function

3. Use Feature Flags

' Boolean feature flag
if gb.isOn("new-player-ui") then
    showNewPlayer()
else
    showLegacyPlayer()
end if

' Get feature value with fallback
buttonColor = gb.getFeatureValue("cta-color", "#0000FF")
maxVideos = gb.getFeatureValue("videos-per-page", 12)

' JSON feature configuration
playerConfig = gb.getFeatureValue("player-settings", {
    autoplay: false,
    quality: "HD"
})

Important: Instance Management (SINGLETON PATTERN IS SUGGESTED)

Create one instance and resuse it.

' Initialize ONCE when app starts
m.global.addFields({ gb: invalid })

function InitApp()
    m.global.gb = GrowthBook({
        clientKey: "sdk_YOUR_KEY",
        attributes: { id: GetUserId() }
    })
    m.global.gb.init()
end function

' Reuse the same instance throughout your app
function ShowFeature()
    if m.global.gb.isOn("feature-key") then
        ' Feature logic
    end if
end function

' Update attributes when needed (e.g., user login)
function OnUserLogin(newUserId)
    m.global.gb.setAttributes({ id: newUserId })
    ' Features are now re-evaluated with new user ID
end function

Why This Matters

Creating a new instance for each feature check causes:

| Problem | Impact | |---------|--------| | Redundant API calls | Every init() call fetches features again (expensive) | | Memory waste | Each instance consumes ~150KB of memory | | Inconsistent variations | Different hash seeds mean same user gets different variations | | Poor performance | Network latency on every feature evaluation | | Broken experiments | Inconsistent variation assignment for A/B tests |

Best Practice: Singleton at App Level

' In your app's global initialization
function Main()
    ' Create ONCE
    globalNode = CreateObject("roSGNode", "GlobalNode")
    
    ' Initialize GrowthBook once
    gb = GrowthBook({
        clientKey: "sdk_YOUR_KEY",
        attributes: { id: "user123" }
    })
    if gb.init()
        ' Store globally to reuse everywhere
        globalNode.addFields({ growthBook: gb })
    end if
    
    ' Now use it throughout your app
    ' Access via: globalNode.growthBook
end function

Updating Attributes vs. Recreating Instance

' ✅ DO THIS: Update attributes without recreating instance
m.global.gb.setAttributes({
    id: newUserId,
    subscription: "premium",
    country: "US"
})

' ❌ DON'T DO THIS: Creating new instance to change attributes
m.global.gb = GrowthBook({ ... })  ' Wrong!
m.global.gb.init()                 ' Wrong!

Detailed Examples

Example 1: Basic Configuration & Instantiation

'
' Complete example showing SDK configuration and initialization
'

function InitializeGrowthBook() as object
    ' Step 1: Create configuration object
    config = {
        apiHost: "https://cdn.growthbook.io",
        clientKey: "sdk_YOUR_CLIENT_KEY",
        
        ' Set user attributes for targeting
        attributes: {
            id: "user-" + GetUserId(),           ' Unique user ID
            email: GetUserEmail(),                ' User email
            subscription: GetSubscriptionTier(), ' free, basic, premium, enterprise
            country: "US",                        ' User location
            isPremium: (GetSubscriptionTier() = "premium")
        },
        
        ' Enable debug logging during development
        enableDevMode: false
    }
    
    ' Step 2: Create GrowthBook instance
    gb = GrowthBook(config)
    
    ' Step 3: Initialize - load features from API
    if gb.init()
        print "✓ GrowthBook initialized successfully"
        print "  Features loaded: " + Str(gb.features.Count())
    else
        print "✗ Failed to initialize GrowthBook"
        ' Optionally continue with defaults or cached features
        return invalid
    end if
    
    return gb
end function

function GetUserId() as string
    ' Get from your app's user data
    return "12345"
end function

function GetUserEmail() as string
    return "[email protected]"
end function

function GetSubscriptionTier() as string
    ' Return: "free", "basic", "premium", or "enterprise"
    return "premium"
end function

' Usage in your main application
sub Main()
    gb = InitializeGrowthBook()
    
    if gb <> invalid
        ' Now ready to use feature flags
        if gb.isOn("app-feature")
            print "Feature is enabled!"
        end if
    end if
end sub

Example 2: Setting Attributes & Evaluating Features

'
' Comprehensive example showing attribute management and feature evaluation
'

sub ManageUserAttributesAndFeatures()
    ' Initialize with basic attributes
    gb = GrowthBook({
        clientKey: "sdk_YOUR_KEY",
        attributes: {
            id: "user-123",
            country: "US"
        }
    })
    gb.init()
    
    ' =========================================================
    ' SECTION 1: Initial Feature Evaluation
    ' =========================================================
    
    print "--- Initial State ---"
    if gb.isOn("enable-new-ui")
        print "✓ New UI is enabled"
        LoadNewUserInterface()
    else
        print "✗ New UI is disabled, using legacy"
        LoadLegacyUserInterface()
    end if
    
    
    ' =========================================================
    ' SECTION 2: User Logs In - Update Attributes
    ' =========================================================
    
    ' When user logs in, update their attributes
    gb.setAttributes({
        id: "user-456",                    ' Now we know their real ID
        email: "[email protected]",         ' Email address
        subscription: "premium",           ' Premium subscriber
        country: "US",
        accountAgeInDays: 365,             ' Account created a year ago
        watchTimeMinutes: 50000            ' User has watched a lot
    })
    
    print ""
    print "--- After User Login ---"
    
    
    ' =========================================================
    ' SECTION 3: Evaluate Features with New Attributes
    ' =========================================================
    
    ' Feature 1: Premium feature (only for premium users)
    if gb.isOn("premium-features")
        print "✓ Premium features available"
        ShowPremiumContent()
    else
        print "✗ Premium features not available"
    end if
    
    ' Feature 2: Get specific value with fallback
    maxQuality = gb.getFeatureValue("max-video-quality", "1080p")
    print "  Max quality: " + maxQuality
    
    ' Feature 3: Complex object configuration
    playerSettings = gb.getFeatureValue("player-config", {
        autoplay: false,
        quality: "HD",
        subtitles: "on",
        dolbyVision: false
    })
    
    print "  Player autoplay: " + Str(playerSettings.autoplay)
    print "  Player quality: " + playerSettings.quality
    
    
    ' =========================================================
    ' SECTION 4: Detailed Feature Evaluation
    ' =========================================================
    
    ' Get detailed evaluation including source and targeting info
    result = gb.evalFeature("advanced-search")
    
    print ""
    print "--- Feature Evaluation Details ---"
    print "Feature: " + result.key
    print "Enabled: " + Str(result.on)
    print "Value: " + Str(result.value)
    print "Source: " + result.source  ' defaultValue, force, experiment, or unknownFeature
    
    if result.source = "experiment"
        print "Experiment: " + result.experimentId
        print "Variation: " + Str(result.variationId)
    end if
    
    
    ' =========================================================
    ' SECTION 5: Attribute Updates Over Time
    ' =========================================================
    
    ' User upgrades subscription
    print ""
    print "--- User Upgrades Subscription ---"
    gb.setAttributes({
        id: "user-456",
        email: "[email protected]",
        subscription: "enterprise",        ' Now enterprise user!
        country: "US",
        accountAgeInDays: 365,
        watchTimeMinutes: 50000
    })
    
    ' Re-evaluate premium features
    if gb.isOn("premium-features")
        print "✓ Enterprise features now available"
    end if
    
    ' Get enterprise-specific feature
    apiLimit = gb.getFeatureValue("api-rate-limit", 1000)
    print "  API rate limit: " + Str(apiLimit) + " requests/day"
    
end sub

function LoadNewUserInterface()
    print "Loading new UI..."
end function

function LoadLegacyUserInterface()
    print "Loading legacy UI..."
end function

function ShowPremiumContent()
    print "Showing premium content..."
end function

Example 3: Advanced Targeting & Experiment Tracking

'
' Advanced example with complex targeting conditions and experiment tracking
'

sub AdvancedTargetingAndExperiments()
    ' Initialize with tracking callback
    config = {
        clientKey: "sdk_YOUR_KEY",
        attributes: {
            id: "user-789",
            subscription: "premium",
            country: "US",
            accountAge: 200,
            isTestUser: false
        },
        
        ' Callback fired when user enters an experiment (assigned to a variation)
        ' Use this to send data to your analytics platform (Firebase, Mixpanel, etc.)
        trackingCallback: sub(experiment, result)
            ' experiment: { key: string, variations: [] }
            ' result: { variationId: integer, value: dynamic, source: string, ruleId: string, experimentId: string }
            
            print "[EXPERIMENT TRACKED]"
            print "  Experiment ID: " + experiment.key
            print "  Variation ID: " + Str(result.variationId)
            
            ' Send to your analytics platform
            SendAnalyticsEvent({
                event: "experiment_exposed",
                experimentId: result.experimentId,
                variationId: result.variationId,
                ruleId: result.ruleId,
                value: result.value
            })
        end sub,

        ' Optional: Fired on every feature evaluation (not just experiments)
        onFeatureUsage: sub(featureKey, result)
            ' Use this for high-level usage tracking or debugging
            ' result: { value: dynamic, on: boolean, off: boolean, source: string, ... }
        end sub
    }
    
    gb = GrowthBook(config)
    gb.init()
    
    
    ' =========================================================
    ' TARGETING BY SUBSCRIPTION LEVEL
    ' =========================================================
    
    print "--- Premium Features (Subscription Targeting) ---"
    
    ' This feature only shows for premium/enterprise users
    if gb.isOn("advanced-analytics")
        print "✓ Advanced analytics available for premium users"
        ShowAdvancedAnalytics()
    end if
    
    
    ' =========================================================
    ' TARGETING BY COUNTRY
    ' =========================================================
    
    print ""
    print "--- Regional Features (Country Targeting) ---"
    
    ' Feature targeted to US & Canada only
    if gb.isOn("hdr-streaming")
        print "✓ HDR streaming enabled for North America"
    else
        print "✗ HDR streaming not available in your region"
    end if
    
    
    ' =========================================================
    ' A/B TESTING - BUTTON COLOR EXPERIMENT
    ' =========================================================
    
    print ""
    print "--- A/B Testing: Button Color ---"
    
    ' Get button color from experiment
    ' User will be consistently assigned to same variation
    buttonColorResult = gb.evalFeature("button-color-test")
    
    ' buttonColorResult will contain:
    ' - value: the assigned color
    ' - variationId: 0 or 1 (which variation they're in)
    ' - source: "experiment" if in test, "defaultValue" if not
    ' - experimentId: ID of the experiment
    
    buttonColor = buttonColorResult.value
    if buttonColorResult.source = "experiment"
        print "User in A/B test (Variation " + Str(buttonColorResult.variationId) + ")"
        print "Button color: " + Str(buttonColor)
    else
        print "Using default button color: " + Str(buttonColor)
    end if
    
    ' Store for later conversion tracking
    m.global.addFields({
        currentButtonColor: buttonColor,
        buttonExperimentId: buttonColorResult.experimentId
    })
    
    
    ' =========================================================
    ' A/B TESTING - PROGRESSIVE ROLLOUT
    ' =========================================================
    
    print ""
    print "--- Progressive Rollout: New Video Player ---"
    
    ' This feature gradually rolls out to percentage of users
    ' based on consistent hash of user ID
    useNewPlayer = gb.isOn("new-video-player-rollout")
    
    if useNewPlayer
        print "✓ Using new video player (progressive rollout)"
        m.player = CreateObject("roSGNode", "NewVideoPlayer")
    else
        print "✗ Using legacy video player"
        m.player = CreateObject("roSGNode", "LegacyVideoPlayer")
    end if
    
    
    ' =========================================================
    ' COMPLEX TARGETING - MULTI-CONDITION
    ' =========================================================
    
    print ""
    print "--- Complex Targeting: Premium US Users Only ---"
    
    ' This feature requires multiple conditions:
    ' - subscription = "premium" OR "enterprise"
    ' - country = "US"
    ' - accountAge > 30 days
    
    if gb.isOn("vip-support-exclusive")
        print "✓ VIP support available"
        ShowVIPSupport()
    else
        print "✗ VIP support not available for this user"
    end if
    
    
    ' =========================================================
    ' FEATURE VALUE WITH COMPLEX OBJECT
    ' =========================================================
    
    print ""
    print "--- Complex Feature Configuration ---"
    
    playerConfig = gb.getFeatureValue("player-configuration", {
        autoplay: false,
        quality: "1080p",
        bitrateLimit: 10000,
        enableSubtitles: true,
        enableDolbyVision: false,
        cacheSize: 500
    })
    
    print "Player settings:"
    print "  Autoplay: " + Str(playerConfig.autoplay)
    print "  Quality: " + playerConfig.quality
    print "  Bitrate limit: " + Str(playerConfig.bitrateLimit) + " Kbps"
    print "  Subtitles: " + Str(playerConfig.enableSubtitles)
    print "  Dolby Vision: " + Str(playerConfig.enableDolbyVision)
    print "  Cache size: " + Str(playerConfig.cacheSize) + " MB"
    
    ' Apply to player
    m.player.autoplay = playerConfig.autoplay
    m.player.quality = playerConfig.quality
    m.player.bitrateLimit = playerConfig.bitrateLimit
    
    
    ' =========================================================
    ' CONVERSION TRACKING (Manual)
    ' =========================================================
    
    ' When user completes an action (like purchase):
    ' You track it manually with the experiment context
    
end sub

function ShowAdvancedAnalytics()
    print "Showing advanced analytics dashboard..."
end function

function ShowVIPSupport()
    print "Showing VIP support options..."
end function

sub SendAnalyticsEvent(event as object)
    ' Send event to your analytics service
    ' Example: send to Roku analytics, Firebase, Mixpanel, etc.
    print "[ANALYTICS] Event: " + event.event
end sub

Installation

Option 1: Manual Installation (Recommended)

  1. Download GrowthBook.brs
  2. Copy to your channel's source/ directory
  3. Initialize in your main scene or component

Option 2: Git Submodule

cd your-roku-channel
git submodule add https://github.com/growthbook/growthbook-roku.git lib/growthbook

Then reference the SDK:

' In your main.brs
' Roku will automatically include all .brs files from source/

Documentation

Configuration Options

| Option | Type | Description | |--------|------|-------------| | apiHost | string | GrowthBook API host (default: https://cdn.growthbook.io) | | clientKey | string | Required - Your SDK client key from GrowthBook | | decryptionKey | string | Optional - Decrypt encrypted feature payloads | | attributes | object | User attributes for targeting (e.g., {id: "user-123", premium: true}) | | trackingCallback | function | Callback fired when user is placed in an experiment | | onFeatureUsage | function | Callback fired on every feature evaluation | | enableDevMode | boolean | Enable verbose logging for debugging |

Core Methods

init() as boolean

Load features from GrowthBook API. Returns true on success.

if gb.init()
    print "Features loaded successfully"
end if

isOn(featureKey as string) as boolean

Check if a boolean feature flag is enabled.

if gb.isOn("dark-mode") then
    applyDarkTheme()
end if

getFeatureValue(featureKey as string, fallback as dynamic) as dynamic

Get the value of a feature flag with a fallback.

theme = gb.getFeatureValue("theme-color", "#0000FF")
maxItems = gb.getFeatureValue("max-items", 20)

setAttributes(attributes as object)

Update user attributes for targeting.

gb.setAttributes({
    id: userId,
    subscription: "premium",
    country: "US"
})

evalFeature(featureKey as string) as object

Get detailed feature evaluation result.

result = gb.evalFeature("pricing-test")
print result.value       ' The feature value
print result.source      ' Where it came from: "defaultValue", "force", "experiment"
print result.on          ' Boolean: is feature "on"
print result.ruleId      ' ID of the matching rule

Advanced Targeting

The SDK version 1.3.1+ supports all GrowthBook advanced targeting rules:

  • Filters: Complex user segmentation using hash-based filtering.
  • Ranges (Intervals): Precise traffic control for experiments and rollouts (v1.1.0+).
  • Namespaces: Mutually exclusive experiments to prevent overlap.
  • Prerequisites: Dependent feature flags (flag A requires flag B).
  • Forced Variations: Global variation overrides for testing.

These are handled automatically by the SDK based on your GrowthBook configuration.

Examples

See the examples/ directory for complete working examples:

  • simple_flag.brs - Basic feature flag usage
  • experiments.brs - A/B testing with tracking
  • targeting.brs - Advanced audience targeting
  • coverage_rollout.brs - Progressive feature rollouts
  • array_targeting.brs - Tag-based targeting with arrays
  • version_targeting.brs - Semantic version targeting
  • weighted_experiments.brs - Custom traffic splits
  • consistent_hashing.brs - User bucketing consistency

Testing

Spec Compliance Tests

Run the logic validator (JavaScript shim) and native BrightScript tests to ensure 100% parity with the GrowthBook specification:

npm test

You can also run them individually:

  • npm run test:validator - Run the logic validator (fast, algorithm validation)
  • npm run test:native - Run the native BrightScript tests (validates actual .brs execution)

Validate BrightScript Syntax

Check for syntax errors and linting issues:

npm install -g @rokucommunity/bsc
npm run lint

Deploy to Roku Device

export ROKU_DEV_TARGET=192.168.1.100
export ROKU_DEV_PASSWORD=your-password

npm run deploy

See TESTING.md for comprehensive testing guide.

Performance

Benchmarks on Roku Ultra (2023):

| Operation | Time | Notes | |-----------|------|-------| | SDK Initialization | ~50ms | Without network call | | Feature Load (API) | 500-2000ms | First load from network | | Feature Evaluation | <1ms | Cached, in-memory | | Experiment Evaluation | <2ms | With hashing & targeting |

Memory Usage: ~150KB total (SDK + cached features)

Browser Support

Works on all Roku devices:

  • ✅ Roku Ultra
  • ✅ Roku Streaming Stick
  • ✅ Roku Express
  • ✅ Roku TV
  • ✅ Roku Premiere
  • ✅ Legacy Roku devices (Roku 2/3)

Minimum Roku OS: 9.0+

Limitations

  • No Server-Sent Events (SSE) streaming support (Roku limitation)
  • No Visual Editor experiments (SceneGraph only)
  • AES decryption requires roEVPCipher component (Roku OS 9.2+)
  • Network requests are asynchronous only

Contributing

We welcome contributions! Please see CONTRIBUTING.md for guidelines.

Development Setup

  1. Fork and clone the repo
  2. Make changes to source/GrowthBook.brs
  3. Test with npm test
  4. Deploy to Roku device for integration testing
  5. Submit a pull request

Running Tests

# Install dependencies
npm install

# Run unit tests
npm test

# Validate syntax
npm run lint

# Deploy to test device
npm run deploy

FAQ

Q: Does this work with SceneGraph?
A: Yes! Initialize GrowthBook in your Scene component and access it throughout your SceneGraph tree.

Q: Can I use this offline?
A: Yes, features are cached after the first load. Pass features directly to skip network calls:

gb = GrowthBook({features: myFeaturesObject})

Q: How do I target by device type?
A: Set device info as attributes:

deviceInfo = CreateObject("roDeviceInfo")
gb.setAttributes({
    id: userId,
    deviceType: deviceInfo.GetModel(),
    osVersion: deviceInfo.GetVersion()
})

Q: Does this support remote evaluation?
A: No, Roku SDK uses local evaluation only. Features are evaluated on the device.

Related SDKs

Resources

License

MIT License - see LICENSE file for details.

Support


Made with ❤️ by the GrowthBook team