
This may be the client story I am most proud of, to-date. I completed this project independently, from scratch, in a domain I in which I had no prior experience. More importantly, I got to work with a wonderful business serving an underrepresented segment of the population.
Fortunately, this project did not require an NDA, so I can discuss it in great detail.
The situation
The Disorder Collection , a project of the Rare Outreach Coalition, has a curated library of over 250 films and video productions, produced by and for people living with rare diseases. This content used to be available on the popular Roku platform. However, on January 18th, 2024, Roku’s “Direct Publisher” service, which made channel creation as easy as copy-pasting a URL to a JSON feed, was officially sunset. Roku would now require all channels to build and publish their own app using the provided SDKs.
Unfortunately, this meant that the Disorder Collection was taken offline until an app could be developed to replace the old one. Given that Roku boasts over 81 million active users as of Q1 2024, the founders of the organization really wanted to restore their Roku channel and make their content available on the platform once again.
A former collegue and good friend of mine referred the founders to me.
The pitch
I offered to build a new Roku channel app and publish it to the app store. I would preserve any existing features the old app offered, and ensure that the app passed all of Roku’s required certification criteria for public channel apps.
As part of my initial discovery, I read a lot of official Roku developer documentation. This was critical to providing a more accurate time estimate, since I’d never done anything like this before. I ended up noting several sections of the certification criteria that appeared particularly relevant to include in the Scope of Work:
- Performance (part 3)
- Channel operation (part 4)
- Deep linking (part 5)
- UI and graphics (part 6)
Other criteria, such as requirements for advertisements, did not apply.
As always, I called out several risks in the proposal document, which I’ve copied here verbatim:
Trust: Partnering with a new developer for the first time is inherently risky due to the high degree of unknown. To mitigate this, I include a hassle-free cancellation policy in case you decide I’m not the right person for the job. Additionally, I strive to demonstrate regular, tangible progress towards the project objective in each of my work reports.
Lack of expertise: While I am a fast learner, Roku TV development is not one of my areas of specialization. The pace of my work will be slower than usual, and the complexity of the work I can deliver will be lower. To compensate, I will apply a discount to the total project price to reflect my reduced efficiency, and I will stretch my estimated timeline to incorporate my learning curve.
Security: Working with a third-party developer requires granting them access to sensitive information, and authorizing them to make changes to your accounts. To protect your organization, I advocate for a “zero-trust” security policy. I will research the Roku platform and walk you through the steps needed to grant me the minimal access necessary for me to deliver the completed project to you.
Project highlights
Client ownership
It’s important from a security and legal standpoint to ensure that clients maintain control of their own IP. For that reason, I walked the client’s point of contact (one of the founders) through the process of setting up a Roku developer account, creating the channel profile, and inviting me to collaborate as a user with limited privileges. This took slightly longer than me setting everything up myself, but by following the principle of least privilege, the client maintained ultimate control and responsibility over the channel.
Learning a new stack
Roku channel apps are built using two proprietary languages. SceneGraph is a flavor of XML used to declaratively create and compose UI components; BrightScript is a scripting language that seems to borrow from several other languages, and integrates heavily with SceneGraph.
One challenge I hadn’t foreseen was the relative lack of sample code and tutorials I would find while developing with Roku’s special purpose tools. This was a stark contrast to the open, general purpose, widely adopted programming languages I typically use. Roku’s official documentation had a lot of the information I needed, but it is not particularly easy to navigate. There were some official example apps published on Roku’s GitHub, but many of these lacked explanations for why things worked. And the developer’s forum was something of a ghost town - many recent questions were simply left unanswered.
To compensate, I made heavy use of intelligent trial and error. When something wasn’t working, I would strategically disable blocks of code one at a time, in a process of elimination that usually allowed me to determine which piece of logic was causing a problem. I also made good use of the built-in debugging endpoint on my development Roku device, which I could access from my laptop via the arcane telnet
command.
Iterative development
My inexperience with Roku development meant that even relatively mundane tasks, such as loading the feed and rendering a grid of shows, felt insurmountable at first. On a web app, these would be a breeze for me to whip up. In this brand new environment, often times I felt stuck not knowing where to start.
To remedy this situation, I broke up each feature into the smallest set of tasks I could. For example, loading a grid of shows and browsing was implemented like this:
- Fetch the feed via HTTP.
- Render one show title to the screen.
- Render one show thumbnail to the screen.
- Render one row of title + thumbnail elements.
- Validate the successful capture of any remote control button press.
- Bring focus to the grid and test left-right button presses to scroll the row.
- Fix the layout and styling of the titles and thumbnails.
I would commit to version control any time I got a task working. This helped me focus on learning one app function at a time, and avoid being overwhelmed.
Once users could select a movie and start playback, I took advantage of the “Beta app” feature of the Roku developer dashboard. This lets devs upload a bundled app and then share a private code to others so they can test the app out. I started publishing new versions of the Beta app each time I completed a feature, so that my point of contact could try it on their own device. This was especially important because I could not easily produce screen capture videos like I can for the web apps I build on my laptop.
UI design

I am not a UX designer. I make that clear to every client I contract with. That said, since I build a lot of user interfaces, some amount of design work is inevitable.
In this case, I didn’t have any access to the prior app to draw from, nor did I have any experience building interfaces for television apps. The pre-built components in the SDKs did a lot of heavy lifting, thankfully. But there were still layout and interaction decisions I needed to make. My approach? Mimicry.
I booted up my smart TV, and explored several of the popular streaming apps we have installed in our house. I took some notes on how they approached grid views and show summary screens, and search pages, since those were the major features I was developing. The result is, in my opinion, nothing that will win any awards, but good enough to not feel too jarring when users jump from a more popular app into this one.
I think I’m most proud of the show info screen. It took a lot of experimentation to find the spacing and font sizes that felt the most natural and readable. Plus, I had fun adding the gradient effect!
Solving trick play thumbnails
The biggest hurdle I faced in terms of achieving certification was implementing something called “trick play thumbnails.” These are Roku’s name for the small preview images that appear above the playback controls when you seek forward or backward. What initially seems like a simple feature turned out to require a lot of research and problem solving.
A primer on streaming video
In order to understand the challenge here, we need some background knowledge about how video files work. This was all knowledge I had to teach myself while working on this project.
Raw video files are very large, because they are essentially a giant collection of images. This is especially true when you get to higher resolutions such as 4K. In order to shrink the size of the file, we typically compress all the still images in a video file for storage, and then decompress them during playback. The algorithm used for compression and decompression is called a “codec”. H.264, also known as Advanced Video Coding (AVC), is one of the most popular video codecs.
Of course, a video file typically contains more than just an image stream. Most videos also include audio streams, and many also contain metadata, such as the movie title, publication date, genre, and more. A video “container” format specifies a way to store compressed video and audio together with metadata. The ubiquitous “MP4” file format that most people know is in fact a video container format.
A single large file with a fixed resolution may be fine for videos saved to play offline on your device. But what about videos streamed from a server to many different devices? In that case, we need more flexibility. An old smartphone on a shaky mobile connection will require a lower-resolution stream than a brand-new 4K smart TV with a Gigabit WiFi connection. To enable adaptive streaming, we use streaming protocols such as HTTP Live-Streaming (HLS). HLS helps devices gather the information they need to stream, by defining two new file types:
- A “Master Playlist” file, which contains URLs pointing to multiple variants of a video, typically at different resolutions. The device consuming the stream can choose a variant from this list and follow the link to download…
- A “Playlist” file, which contains URLs pointing to all of the “media segment” files for a specific variant of the video, arranged in order.
Each segment file contains the compressed images for a small portion of the full video. The streaming device reads the Playlist file to fetch each segment, one after the other, and plays them in sequence. The advantage of using tiny segment files is that the streaming device can start playing the first segments without waiting for the entire video to download.
Implementation

Remember, trick play thumbnails are small preview images that we show when a user is navigating to a new point in a show. Roku gives us two options for adding these preview images to our streaming content:
- Generate standard thumbnail files and upload them to a file host. Then, update the HLS master playlists for each video to include the download URL from the host. The Roku device will automatically discover this URL and download the trick play thumbnails.
- Generate thumbnails in a format called “BIF” and upload them to a file host. Then, add some code to our Roku app so that it knows how to fetch the trick play thumbnails for each video.
The first problem was a big one; the HLS Master Playlist files for each show on the Disorder Channel were being generated and hosted on Vimeo. I had no way to modify these files, so option (1) was immediately eliminated. Option (2) it was!
The second problem was that the executable tool Roku provided for generating BIF tools on linux had not been updated in six years! This wasn’t a problem on its own, but the executable depended on a dynamic link to an old version of a shared library that was no longer available on any modern Linux distribution. After banging my head on my keyboard for a couple days, I had an idea; I could pull a container image for an older version of my linux distribution, one that still had the old library version, and that should allow me to execute the outdated BIF generator. With just a couple quick hacks, this worked like a charm. I ended up open-sourcing the Containerfile and Readme for this BIF Container , which you are welcome to view yourself.
The third problem called for some DevOps. The client did not have any infrastructure set up, which meant nowhere to upload and host the trick play thumbnail files. I ended up creating a team for them on Digital Ocean, and uploading the files to a Spaced bucket there. I made this decision after running a cost comparison of several different S3-compatible hosting providers, including AWS and Google Cloud.
You can be sure that I was thrilled when I finally got this solution working end-to-end!
Area of improvement: Keep the ball rolling
My client was, understandably, very busy. This meant that sometimes weeks would pass before I would hear back from them. As someone who thrives in an environment of fast-paced iteration and feedback loops, it was frustrating to feel my momentum stalling.
Later into the project, I started reaching out to offer possible paths forward that did not require their input, with an explanation of the trade-offs compared to having them more engaged. One example was setting up the S3 bucket; originally, I had asked them for a bunch of viewer metrics so that I could estimate bandwidth use as a factor in my cost analysis; it turned out that they preferred me to simply make a best guess and continue towards the goal of publishing the app, even if it meant they might pay a bit extra in hosting costs.
In the future, I’m going to proactively prompt my busier clients to make conscious choices about whether they want to engage or let me use my own judgment.
Final takeaway
With this project, I really proved to myself that I am achieving my freelance aspirations; managing projects and clients independently, teaching myself entirely new skill-sets on the job, and choosing to work with clients whose missions I believe in.
The Disorder Channel is available on the Roku app store. If you have a Roku device, please give it a look!