Selected candidates will soon be able to give the suite a real world test run and provide their feedback and opinions!
DEVELOPMENT UPDATE
CRESA Web Runtime Takes a Major Step Forward
What started as a browser-runtime migration turned into four days of CEF, Twitch, authentication, playback and native bridge debugging. The result: CRESA can now detect and clear Twitch's "Start Watching" content gate automatically — and the path back to Raid Out and browser-scene migration is finally open.
This was supposed to be a browser migration
The original goal sounded relatively straightforward: move CRESA's browser-powered scenes and components away from OBS Browser and onto the new CRESA Web Runtime.
That work is important because CRESA is gradually becoming responsible for more of its own runtime. Rather than creating a collection of browser sources inside OBS and hoping they remain in the correct state, CRESA should be able to create, manage, monitor and recover its own web-powered components. That gives us much greater control over lifecycle, readiness, diagnostics, audio behaviour, scene integration and recovery.
For most browser content, that remains the direction. Then we got to Twitch, and Twitch had other ideas.
The problem with replacing OBS Browser
CRESA Web Runtime can handle the normal web content used throughout the suite, but Twitch playback introduced requirements that meant we couldn't simply remove OBS Browser from the equation.
OBS Browser already provides the Chromium/CEF environment needed for Twitch's playback requirements, including the media and codec path we need. It can also maintain the browser session used by Twitch. Instead of forcing everything through one browser system, the architecture became much clearer: CRESA Web Runtime handles CRESA's general browser content, while OBS Browser remains as a specialised backend for Twitch playback where required.
That means scenes, overlays, alerts, widgets and other web-powered presentation elements can move to CRESA Web Runtime, while Twitch playback can retain the parts of OBS Browser that it genuinely needs. CRESA still controls the overall experience; OBS Browser simply becomes one of the underlying tools available to it.
Then autoplay broke
Before we could even get back to Raid Out, Twitch playback itself had to become reliable. At one point CRESA could initialise the Twitch player correctly, but automatic playback stopped behaving consistently.
The eventual cause was almost comically small compared with the amount of debugging around it: CRESA had an "Enable Twitch audio" control positioned over part of the embedded Twitch player. Twitch has visibility requirements around embedded playback, and our own UI was physically overlapping the player.
Moving that control fixed the problem. The Twitch path could once again reach READY → ONLINE → PLAY → PLAYING automatically, muted playback worked, and no OBS Interact window was required.
That should have been the end of the Twitch problem.
It wasn't.
"Just one second..."
Certain Twitch streams can display a Content Classification interstitial before allowing the viewer to actually see the stream:
> Just one second... > Start Watching
For a normal viewer this isn't particularly interesting. Click the button and continue watching. For an automated Raid Out system, however, it is a serious problem.
CRESA could load the Twitch player. Twitch could report that the player was ready. Playback could even report that it was running, with audio underneath it, while the actual video remained covered by Start Watching.
That meant Raid Out could technically believe everything was healthy while the audience would be staring at a Twitch warning. For CRESA, that wasn't acceptable. Raid Out needs to work without somebody opening OBS Interact and clicking a button manually.
So the warning had to become something CRESA could detect and handle itself.
Down the rabbit hole
The obvious question was whether Twitch could simply tell us when this warning was being displayed. Unfortunately, not in a useful way.
Twitch exposes Content Classification Labels through its supported API surfaces, but knowing that a broadcaster has a classification label is not the same thing as knowing whether a particular viewer session is currently looking at the Start Watching interstitial. A viewer may already have acknowledged that particular label, meaning the channel can still carry the classification while no blocker is displayed.
So CRESA could not safely assume:
Classification label exists → Start Watching is present
We needed to know whether the control actually existed for the current browser session.
That led directly into another problem: cross-origin browser security. Our local CRESA page hosts the Twitch player inside an iframe, but Twitch lives on a different origin. Normal browser security correctly prevents the parent CRESA page from simply reaching inside Twitch's iframe and inspecting its DOM.
We weren't going to disable browser security. We weren't going to use OCR or image matching. We weren't going to blindly click screen coordinates and hope Twitch hadn't moved something. We also weren't going to build anything that bypassed Twitch authentication or exposed browser credentials.
We needed a controlled native solution.
Building the native Twitch bridge
OBS Browser is built on CEF, and internally CEF knows about the actual Twitch frame. That gave us a path forward.
We built a deliberately narrow bridge into our isolated OBS Browser development build. It does not expose arbitrary JavaScript execution or give CRESA unrestricted access to somebody's browser. Instead, it provides two tightly controlled Twitch-specific capabilities:
- twitch_gate_probe — determine whether the known Twitch Start Watching control is present in the expected Twitch frame.
- twitch_gate_ack — acknowledge that control, but only after the current navigation has positively confirmed that it exists.
No cookies, credentials, Twitch tokens or browser storage are exposed to CRESA. The bridge exists purely to solve this specific Twitch playback problem.
And then, naturally, we couldn't find the frame.
We found the Twitch frame... and still somehow lost it
The first native bridge attempts failed with frame_state=NOT_FOUND. That opened another investigation: was Twitch hiding the frame? Could CEF see the cross-origin iframe? Was our channel matching wrong?
We added bounded native frame-discovery diagnostics and eventually got one of the most frustrating results of the entire journey.
CEF reported that the Twitch frame existed. `player.twitch.tv` was found. The expected channel matched. The frame was selected.
Then the exact same probe returned:
frame_state=NOT_FOUND
That contradiction finally exposed the real problem. The browser process was correctly selecting Twitch's out-of-process iframe, but the old bridge handoff wasn't preserving that selected frame. The request was effectively handed back through the main browser frame, where another process-local lookup took place.
In other words:
Find Twitch frame → select Twitch frame → lose Twitch frame → report NOT_FOUND
Once that process boundary was understood, the handoff could be corrected. The browser process retained the result of the actual Twitch frame selection and used a tightly scoped relay through the already-connected CRESA frame.
The next test finally produced frame_state=FOUND.
We could now execute a bounded native probe against the actual Twitch player frame.
Surely that was the end.
Of course it wasn't.
FOUND... ABSENT
Our known test channel had previously displayed the Start Watching warning. The native bridge was now reaching the correct Twitch frame, but the result came back:
FOUND / ABSENT
At first that suggested the warning simply wasn't being presented anymore. Maybe the browser session had already acknowledged that particular classification, or perhaps we'd lost our useful test case.
Then the warning appeared on screen.
After the probe had already finished.
That was the moment the final major contradiction made sense. The bridge wasn't wrong. The selector wasn't wrong. The frame wasn't wrong.
We were faster than Twitch.
The Twitch player could reach its normal readiness state before the Content Classification interface had finished appearing. Our sequence was effectively:
Player READY → CRESA probes → gate isn't in the DOM yet → ABSENT → Twitch renders Start Watching
A screenshot from the exact same session showed the warning sitting over the player even though the earlier native probe had correctly returned ABSENT at the instant it ran.
The final problem wasn't detection anymore. It was timing.
CRESA learned to wait
Rather than turning the bridge into an endless polling system, we implemented a bounded observation window.
Once the Twitch player reaches readiness, CRESA performs its initial native probe. If the result is FOUND / ABSENT, CRESA doesn't immediately assume everything is clear. For a short, strictly limited period it continues observing the same source and navigation.
The current implementation waits 750ms between normal re-probes, observes for no more than six seconds, allows no more than eight total probes and handles explicit bridge rate limiting with a bounded backoff. If the source, channel or navigation changes, the observation is cancelled. Most importantly, CRESA will never acknowledge the gate unless a fresh native probe positively reports PRESENT.
No infinite polling. No blind clicking. No stale acknowledgement from an old Twitch page. If the evidence isn't there, it fails closed.
And then it finally happened
On the final isolated Recovery OBS run, CRESA loaded the Twitch test channel and began the new observation sequence.
The first probe returned:
FOUND / ABSENT
The warning hadn't appeared yet.
A second attempt encountered the bridge's rate limiter, so CRESA backed off rather than hammering it. Then, approximately 1.3 seconds into the observation window, the next native probe returned:
# FOUND / PRESENT
For the first time, CRESA itself had positively detected the real Twitch Content Classification gate inside the correct Twitch frame.
No screenshot inference. No classification metadata assumption. No OBS Interact. No human click.
That native PRESENT result authorised exactly one acknowledgement. CRESA issued twitch_gate_ack, the bridge accepted it, and CRESA then performed a completely fresh verification probe.
The result:
# FOUND / ABSENT
The gate was gone.
The verdict
After four days, the complete certified sequence finally became:
Initial probe → ABSENT → delayed Twitch gate appears → PRESENT → exactly one native acknowledgement → fresh verification probe → ABSENT
And it happened automatically.
There was no OBS Interact, no manual Start Watching click, no coordinate automation, no OCR, no browser security bypass and no Twitch credentials exposed to CRESA. The Twitch player remained inside the browser environment capable of handling it, while CRESA dealt with the blocker through the narrow native bridge.
The Twitch Content Classification blocker passed engineering certification.
What four days of pain actually gave us
From the outside, this could easily be reduced to: "You spent four days making it click Start Watching?"
In the most technically insulting interpretation possible... yes.
But solving it forced us to understand and harden considerably more of CRESA's browser architecture. Along the way we established reliable Twitch autoplay behaviour, player and iframe readiness telemetry, native OBS Browser capability detection, controlled CRESA-to-OBS Browser communication, CEF frame discovery, cross-process Twitch frame handling, generation-safe navigation tracking, bounded gate observation, acknowledgement safety rules, post-action verification, rate-limit handling and substantially better diagnostics.
More importantly, the work clarified exactly where OBS Browser belongs in CRESA's architecture.
It doesn't need to own everything.
The architecture going forward
The goal remains to move CRESA's general browser-powered components onto CRESA Web Runtime. Browser scenes, overlays, alerts, widgets and other web-driven presentation elements belong there.
OBS Browser can remain as a specialised backend for the parts of Twitch playback where we genuinely require its capabilities, including the media/codec environment and browser session used by Twitch.
The separation becomes much cleaner:
CRESA Web Runtime → CRESA's general browser content OBS Browser → specialised Twitch playback/session backend CRESA → controls the complete experience
That lets us continue moving away from unnecessary OBS Browser dependencies without pretending Twitch doesn't have requirements of its own.
Where we are now
The certified implementation was deliberately developed and tested inside an isolated portable Recovery OBS environment. That protected the normal streaming installation while low-level browser components were modified, rebuilt and repeatedly abused in the name of science.
Now that the behaviour has passed isolated certification, the next job is to carefully promote the known-good runtime into the normal OBS installation. That means inventorying the exact changed components, backing up the existing OBS binaries, preserving scenes, profiles and the existing OBS Browser session, installing only the minimum required runtime delta, then validating CRESA and Twitch playback before and after a clean restart.
We are not replacing the normal OBS configuration with the development environment. We're taking the components that passed and moving only those components across.
Then we're going back to Raid
Finally.
Raid Out was the reason this Twitch work became a blocker in the first place. A substantial amount of the Raid system already exists, so the next objective isn't to blindly rebuild Raid from scratch.
Once the certified Twitch stack is confirmed inside normal OBS, we'll return to the existing Raid implementation and move the remaining non-Twitch browser elements involved in that workflow onto CRESA Web Runtime. The Twitch player itself can remain on the specialised OBS Browser backend where required.
Then we can test the workflow that actually matters:
Start Raid Out → load target Twitch stream → reach readiness → automatically handle Start Watching if required → continue Raid Out → run the Raid presentation → complete and clean up
Only then will we know what, if anything, the Raid implementation itself still needs.
Four days for one blocker
This was not supposed to consume four days.
There were several points where fixing one problem simply exposed another underneath it: autoplay, CEF, TLS, runtime isolation, OBS connectivity, frame discovery, cross-process frame ownership, readiness, gate detection and finally a race condition measured in little more than a second.
There were moments where starting over sounded easier. But starting over would have thrown away everything we'd learned.
Instead, every failure narrowed the problem until the behaviour could finally be explained, reproduced and fixed.
And that's the important part.
We didn't get the result because the warning happened to disappear once. We got a native sequence proving:
> PRESENT → ACK → ABSENT
That's the difference between "it seemed to work" and something we can actually build Raid Out on top of.
Verdict: breakthrough achieved
The Twitch Content Classification blocker is now considered engineering-certified in the isolated runtime.
The next verdict comes from normal OBS and, ultimately, the real Raid Out workflow. If it behaves there the way it behaved during certification, one of the most stubborn blockers standing between us and the wider CRESA Web Runtime migration is finally behind us.
After four days, we'll happily take that win.
Next up: normal OBS validation → Raid Out → Raid presentation → back to browser-scene migration.