How Owl20 Works

A technical look at how Owl20 bridges D&D Beyond dice rolls into Owlbear Rodeo using browser extension APIs, cross-origin messaging, and the Owlbear Rodeo extension system. Written by the developers who built it — DM UberDragon and DM Sparks.

The Problem Being Solved

D&D Beyond and Owlbear Rodeo run on completely different domains: dndbeyond.com and owlbear.rodeo. Browser security — specifically the same-origin policy — prevents JavaScript in one domain from directly reading data from or sending data to a different domain.

Beyond20 already solves one part of this problem: it reads dice roll data from D&D Beyond's character sheets and makes it available to VTT platforms. But Beyond20 can't natively communicate with Owlbear Rodeo's extension system, which uses an iframe-based architecture.

Owl20's job is to be the intermediary: receive data from Beyond20 on the Owlbear Rodeo page, and forward it securely across the iframe boundary into the Owl20 OBR extension running inside Owlbear Rodeo.

System Overview

Owl20 has two components that work together:

Owl20 Browser Extension

A Manifest V3 browser extension installed by players (and DMs if they roll). Runs as a content script on owlbear.rodeo pages. Receives Beyond20 events and forwards them into the OBR extension iframe.

View source on GitHub

Owl20 OBR Extension

An Owlbear Rodeo extension installed by the DM. Runs inside an iframe embedded in the Owlbear Rodeo room. Receives the forwarded roll data and renders it for all participants.

View source on GitHub

Full data flow, step by step

  1. Player clicks a rollable element on their D&D Beyond character sheet
  2. Beyond20 processes the roll and fires a Beyond20_Roll CustomEvent on the Owlbear Rodeo page (because Owlbear Rodeo is in Beyond20's custom domains list)
  3. The Owl20 browser extension's content script, running on the Owlbear Rodeo page, catches this event
  4. Owl20 uses the postMessage API to forward the roll data across the iframe boundary into the Owl20 OBR extension
  5. The OBR extension receives the message, plays a dice sound, and enriches the roll data with the player's OBR name and color
  6. The OBR extension broadcasts the enriched roll to all players in the room simultaneously — everyone's panel updates at once

Step 1: CustomEvent Listeners — Receiving Beyond20 Roll Data

When Beyond20 is configured to load on Owlbear Rodeo pages (via the custom domain setting), it dispatches a CustomEvent named Beyond20_Roll on the page's document object whenever a player makes a roll.

This event's detail property contains the full roll payload: the roll type, dice results, character name, ability name, modifiers, and any other context Beyond20 collected from the character sheet.

Owl20's content script registers a listener for this specific event type:

document.addEventListener("Beyond20_Roll", function(event) {
    // event.detail contains the full roll data from Beyond20
    forwardRollToOBRExtension(event.detail);
});

This is the standard mechanism Beyond20 uses to communicate with third-party integrations. The requirement to add https://www.owlbear.rodeo/* to Beyond20's custom domains list tells Beyond20 to inject itself — and fire these events — on Owlbear Rodeo pages.

Step 2: MutationObserver — Finding the OBR Extension iframe

Owlbear Rodeo loads extensions inside iframes embedded in the main room page. The challenge is that these iframes are injected dynamically — they don't exist in the initial HTML, they're added by Owlbear Rodeo's JavaScript after the page loads.

Owl20 uses a MutationObserver to watch the DOM for the appearance of the Owl20 OBR extension iframe:

const observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        mutation.addedNodes.forEach(function(node) {
            if (isOwl20OBRFrame(node)) {
                targetFrame = node;
                observer.disconnect(); // stop watching once found
            }
        });
    });
});

observer.observe(document.body, { childList: true, subtree: true });

Once the iframe is detected, Owl20 stores a reference to it and uses that as the target for all subsequent postMessage calls. The MutationObserver also handles cases where the iframe is removed and re-added (e.g., if the DM disables and re-enables the extension mid-session) by re-establishing the reference automatically.

Step 3: postMessage API — Crossing the iframe Boundary

The core challenge of the entire system is that the Owl20 OBR extension runs in an iframe from a different origin than the Owlbear Rodeo page. Standard DOM access across cross-origin iframes is blocked by the browser's same-origin policy.

The postMessage API is the browser's approved mechanism for secure cross-origin communication between a page and an iframe. Owl20 uses it to forward the roll data:

function forwardRollToOBRExtension(rollData) {
    if (targetFrame && targetFrame.contentWindow) {
        targetFrame.contentWindow.postMessage(
            { type: "Beyond20_Roll", data: rollData },
            "https://owl20.friendlymimic.com" // explicit target origin
        );
    }
}

The explicit target origin (https://owl20.friendlymimic.com) is an important security detail. By specifying the exact origin the OBR extension runs on rather than using *, Owl20 ensures the message can only be received by the legitimate Owl20 OBR extension — not by any other iframes that might be present on the page.

On the OBR extension side, the incoming message is validated by checking event.origin before processing it, providing defence-in-depth against cross-origin message spoofing.

Step 4: The OBR Extension — Receiving, Enriching, and Broadcasting

Once the postMessage arrives inside the Owl20 OBR extension iframe, the receiving side takes over. This is the component built by DM Sparks using the official Owlbear Rodeo extension SDK.

What happens when the message lands

The OBR extension receives the roll data and does three things in quick succession:

  1. Plays a dice sound — audio feedback fires immediately so players know a roll landed. The sound loads fresh each time so multiple rapid rolls (e.g. a player who triple-clicks) can play simultaneously without cutting each other off.
  2. Enriches the roll with player identity — the extension asks Owlbear Rodeo for the rolling player's display name and their assigned room color. This is what makes the colored player bubble appear next to each roll result.
  3. Broadcasts to the whole room — this is the key step. Rather than only showing the roll to the player who rolled, the OBR extension uses OBR's broadcast API to send the enriched roll to every participant in the room simultaneously. The DM and all other players see the result at the same moment.

How rolls are displayed

Each player's Owl20 panel receives the broadcast and renders the roll using the HTML that Beyond20 originally generated — the same styled roll cards Beyond20 shows in its own UI, including dice results, modifiers, and roll type labels. On top of that, Owl20 adds:

  • A colored dot matching the rolling player's Owlbear Rodeo color, so it's immediately clear who rolled
  • The character name (from D&D Beyond) alongside the player's OBR display name
  • Auto-scroll to the newest roll so nothing is missed
  • Smart grouping — if the same character rolls multiple times in a row, the header only appears once, keeping the panel tidy

Theme support

The panel respects Owlbear Rodeo's light and dark mode settings and updates automatically if the DM switches themes mid-session.

No server involved

The OBR broadcast API routes messages peer-to-peer within the active room session — no external server, no database, no account required. When the session ends, the roll history is gone.

Security Model

Owl20 was designed with a minimal footprint and no unnecessary data handling:

No data collection or storage

Owl20 does not log, store, or transmit any data to external servers. Roll data flows directly from the Beyond20 event on the page to the OBR extension iframe in memory — it never leaves the browser or touches any server under our control.

Minimal permissions

Owl20 requests only the permissions required to function:

  • Content script on owlbear.rodeo: Required to run on Owlbear Rodeo pages and listen for Beyond20 events
  • No access to D&D Beyond — Beyond20 handles that side entirely
  • No access to your browser history, bookmarks, or any other sites

Explicit origin validation

Both the postMessage send and receive use explicit origin checks. The browser extension sends only to the known OBR extension origin, and the OBR extension rejects any messages not originating from the expected Owlbear Rodeo domain.

Open source

The complete source code for both components is available on GitHub. Anyone can audit exactly what the extensions do:

Manifest V3 Architecture

Owl20 is built on Manifest V3 (MV3), the current browser extension standard required by Chrome and Edge since 2023. This was a deliberate choice, not just a requirement:

Content scripts only — no background service worker needed

Many extensions use background service workers for persistent processing. Owl20's design avoids this entirely. All the extension's logic — the CustomEvent listener, the MutationObserver, and the postMessage forwarder — runs as a content script injected directly into the Owlbear Rodeo page.

This means:

  • The extension only uses resources when an Owlbear Rodeo tab is actually open
  • There's no persistent background process consuming memory or CPU when you're not playing
  • The extension's full behaviour is transparent — it lives entirely in the page's context

Cross-browser compatibility

The same MV3 extension package works on Chrome and Edge without modification. Firefox uses its own variant of MV3 with some differences in the declarativeNetRequest API, but Owl20 doesn't use that API — so the Firefox build is functionally identical to the Chrome/Edge build with only minor manifest adjustments.

Why not Manifest V2?

Chrome deprecated MV2 support in 2023 and began enforcing the deprecation for Chrome Web Store extensions in 2024. Building on MV3 from the start means Owl20 will continue to work without requiring a major architectural rewrite as MV2 support is fully removed across browsers.

Requirements Summary

  • Player side: Chrome, Edge, or Firefox with both Beyond20 and Owl20 browser extensions installed. Beyond20 must be configured with https://www.owlbear.rodeo/* in its custom domains list.
  • DM side: Owl20 OBR extension installed in Owlbear Rodeo via the manifest URL, and enabled per room.
  • No server-side component. Everything runs locally in the browser. There are no accounts, no API keys, no backend infrastructure.

About the Authors

This page was written by DM UberDragon and DM Sparks — the developers of Owl20. DM UberDragon built the browser extension side with 25+ years of software engineering experience spanning full-stack web development, browser extension APIs (MV2 and MV3), and cross-origin communication architectures. DM Sparks built the Owlbear Rodeo integration that receives and displays the dice rolls.

The extension was built to solve a real problem: running D&D campaigns in Owlbear Rodeo without losing the convenience of D&D Beyond's digital character sheets and Beyond20's dice rolling. Every design decision described here — the MutationObserver approach, the explicit-origin postMessage, the content-script-only architecture — came from real experience debugging cross-origin messaging in browser extension environments.

Read more about the creators → | View the source code →