oembed-graphics
Docker-hosted oEmbed graphics for broadcast workflows. It is inspired by webrecorder/oembed.link, but runs as a normal container and exposes graphic URLs that can be loaded by CasparCG, OBS Browser Source, OGraf, or any HTML-capable character generator.
Run locally
npm install
npm start
Open http://localhost:3000.
Run with Docker
docker compose up --build
The service listens on http://localhost:3000.
Broadcast URL
Use /graphic with a source URL:
http://localhost:3000/graphic?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ&width=1920&height=1080&transparent=1
Useful query parameters:
url: required source URL to resolve through oEmbed.width: stage width, default1920.height: stage height, default1080.transparent:1for transparent output, default1.chroma: background color when transparent output is disabled, default#00ff00.fit:containorcover, defaultcontain.scale: graphic scale multiplier, default1.wait:1to keep the graphic hidden until embed media loads, default1.readyDelay: extra milliseconds to wait after media load before reveal, default1000.maxWait: safety timeout before reveal, default10000.autoplay:1to request autoplay for iframe and video embeds, default1.muted:1to mute embeds, default1. Most browsers require this for autoplay.maxwidth: sent to the oEmbed provider and capped at500, default500.maxheight: sent to the oEmbed provider, default480.
Autoplay is best-effort for social embeds. The graphic page keeps the original
social post HTML, adds provider autoplay parameters where possible, and runs a
small assist script that calls play() on any video element it can access.
Browsers still block scripts from controlling video inside cross-origin iframes,
which includes Twitter/X widget frames. For broadcast runtimes based on Chromium
or CEF, also allow autoplay at the browser layer when possible, for example with
--autoplay-policy=no-user-gesture-required.
The renderer caps every social card at 500px wide so different providers line
up more consistently on a broadcast canvas. It still uses the oEmbed response's
height when present.
The service also supports the oembed.link-style form:
http://localhost:3000/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdQw4w9WgXcQ
CasparCG template
Load /caspar as the browser template URL. It starts transparent and blank, then
renders when CasparCG sends UPDATE data.
Example template URL:
http://localhost:3000/caspar
Example JSON update payload:
{
"url": "https://bsky.app/profile/bsky.app/post/3k...",
"width": 1920,
"height": 1080,
"scale": 1,
"transparent": true
}
The template also accepts a raw URL string or standard CasparCG XML
templateData with component ids such as url, scale, fit, autoplay,
muted, readyDelay, and maxWait.
API
GET /api/oembed?url=...returns the matched provider and raw oEmbed data.GET /casparreturns the CasparCG update-driven HTML template.GET /providersreturns the loaded provider patterns.GET /healthzreturns a health check response.
Provider data is loaded from https://oembed.com/providers.json and cached in
memory. Override with PROVIDERS_URL, PROVIDERS_TTL_MS, and
OEMBED_TIMEOUT_MS environment variables.