Back to DevLog

Fixing Invisible Text and Adding Watermarks to StreamStack Video Rendering

3 min read

Another fun debugging session in the world of headless video rendering! Today I tackled two issues with StreamStack that were driving me nuts.

The Invisible Text Mystery

First up: text was rendering perfectly in development but completely disappearing when deployed to Railway. After some detective work, I discovered the culprit was Railway's headless Linux environment.

The problem? I was using CSS @import url(...) to load Google Fonts, which fires async network requests. In a headless environment, Remotion was rendering frames before these fonts could load. Even worse, chromium-headless-shell ships with literally zero system fonts, so even my Courier New fallback was failing. Result: invisible zero-width glyphs everywhere.

The fix was surprisingly clean. I swapped out the CSS imports for @remotion/google-fonts using their loadFont() function. This bundles fonts directly into webpack at build time—no network requests during render. I also added proper Linux font fallbacks (DejaVu Sans Mono, Liberation Mono) across all components.

Funny thing is, @remotion/google-fonts was already sitting in my package.json as a dead dependency. Sometimes the solution is already there waiting for you!

Adding Watermarks the Right Way

Next, I added URL watermarks to the bottom-right corner of video scenes. Instead of hardcoding "EPSTEINSCAN.ORG", I'm pulling from config.websiteUrl so it works across different channels.

The tricky part was getting the styling right. My initial attempt was way too subtle—opacity 0.35 and 13px text looked fine in the browser but completely vanished after H.264 compression. I'm currently testing with debug styling (bright red, 24px, white background) just to verify the element renders at all.

Once I confirm it's visible, I'll dial it back to something more professional but with better contrast than my first attempt.

Multi-Agent Development Flow

I experimented with a Manager/Builder/Reviewer pattern using agent-bridge MCP. Having different AI agents handle coordination, implementation, and validation was pretty smooth. The Builder agent read through the codebase and made targeted changes while the Reviewer validated everything made sense.

What's Next

I need to check that debug watermark render and finalize the styling. If it's visible, I'll bump the opacity to 0.55, increase font size to 15px, and remove that garish red background. Then it's time for a full production test with real content.

Headless rendering always has these fun edge cases, but each one you solve makes the system more robust. The font loading issue especially was one of those "works on my machine" problems that only surfaces in production environments.

Share this post