CEF (Chromium Embedded Framework) browser operator for Vivid.
Renders HTML/CSS/JavaScript/WebGL content as GPU textures. Use it for dashboards, data visualizations, WebGL effects, HTML overlays, and more.
In Vivid's package browser, search for vivid-cef and click Install. Or from the CLI:
vivid packages install https://github.com/seethroughlab/vivid-cef
Note: First install downloads ~100MB of CEF binaries and compiles the wrapper library, which takes a few minutes.
| Parameter | Type | Default | Range | Description |
|---|---|---|---|---|
| url | File | URL or local HTML file path | ||
| stream_id | Text | Shared ID for BrowserAudioIn pairing | ||
| zoom | Float | 1.0 | 0.25 – 4.0 | Browser zoom level |
| transparent | Bool | false | Transparent background (for overlays) | |
| audio_capture | Bool | true | Enable CEF audio capture for stream_id | |
| frame_rate | Int | 60 | 1 – 120 | CEF rendering frame rate |
- texture (GPU_TEXTURE) — rendered web content as a GPU texture
| Parameter | Type | Default | Range | Description |
|---|---|---|---|---|
| stream_id | Text | Must match Browser stream_id | ||
| gain | Float | 1.0 | 0.0 – 2.0 | Output gain |
| sync_strength | Float | 1.0 | 0.0 – 1.0 | Drift correction aggressiveness |
| max_drift_ms | Float | 80.0 | 5.0 – 500.0 | Max drift window before skip/dup correction |
| dropout_behavior | Enum | Silence | Underrun policy (v1: Silence) |
- left (AUDIO_FLOAT) — left channel
- right (AUDIO_FLOAT) — right channel
HTML pages:
examples/hello-world.html— basic HTML/CSS renderingexamples/hello.html— minimal hello pageexamples/webgl-cube.html— rotating WebGL cube (won't render — see Known Issues)examples/dashboard.html— animated data dashboardexamples/transparent-overlay.html— transparent overlay with live clockexamples/interactive.html— mouse/keyboard interaction test (cursor tracking, click dots, scroll resize, key display)examples/audio-tone.html— WebAudio oscillator page for BrowserAudioIn testingexamples/audio-element.html— HTMLAudioElement (<audio>) playback test page
Graphs:
graphs/browser_hello.json— loadshello.htmlinto a Browser operator and pipes to video outputgraphs/browser_webgl.json— loadswebgl-cube.html(demonstrates the WebGL limitation)graphs/browser_audio.json— Browser + BrowserAudioIn routed to video_out + audio_outgraphs/browser_audio_element.json— Browser + BrowserAudioIn using HTMLAudioElement source
String params (like url) go inside the "params" object alongside numeric params — the Vivid graph parser separates strings from numbers by type. There is no separate "string_params" key at the node level.
- CEF renders web content offscreen to a CPU pixel buffer (OnPaint callback)
- Pixels are uploaded to a WebGPU staging texture via
wgpuQueueWriteTexture - A fullscreen blit pass copies the staging texture to the operator's output
CEF lifecycle is reference-counted for acquisition: the first Browser operator initializes CEF, and subsequent operators share the same CEF process. In plugin mode, CEF is kept initialized for the host process lifetime (it is not torn down/reinitialized between operator instances).
BrowserOp now treats CefManager::acquire() as authoritative:
- If acquire fails in
process(), the operator logs once, outputs a deterministic clear frame, and skips browser creation/CEF calls for that frame. - The operator retries acquire on subsequent frames (no permanent lockout).
- On later success, normal browser creation/rendering resumes.
When the Vivid UI is hidden (tilde key), mouse and keyboard events are forwarded to Browser operators via the VividInputState API. Events are translated from GLFW to CEF format:
- Mouse move, click, scroll
- Key press/release with modifier support
- Character input (for text fields)
- CMake 3.16+
- macOS (arm64/x86_64), Windows, or Linux
- Internet connection for first build (downloads CEF from Spotify CDN)
Configure/build:
cmake -B build -S . \
-DVIVID_SRC_DIR=/path/to/vivid \
-DVIVID_BUILD_DIR=/path/to/vivid/build
cmake --build build --target test_browser_cef_gate test_browser_audio_bridgeRun:
ctest --test-dir build --output-on-failure -R vivid_cef_test_Current deterministic coverage:
vivid_cef_test_browser_cef_gate— acquire failure/retry contractvivid_cef_test_browser_audio_bridge— stream claim/release + silence/no-stale-audio behavior
The CI smoke workflow builds and runs the deterministic vivid_cef_test_* tests before demo-graph smoke checks:
--disable-gpu is required in single-process mode, which prevents GL context creation inside the browser. CSS/HTML/JS rendering works fine — only WebGL and WebGPU content won't render.
CEF runs with --single-process because Mach port rendezvous IPC fails when CEF is loaded as a dlopen'd plugin rather than the main executable. This means no GPU subprocess, no network service subprocess, and no renderer subprocess — everything runs on the main thread. The Cannot use V8 Proxy resolver in single process mode warning is benign.
CEF cannot be initialized, shut down, and re-initialized in the same process. Vivid's plugin probing (scan_deferred) opens each dylib, calls vivid_descriptor(), then dlcloses it. When the dylib was later reopened for real use, CefInitialize() would crash with SIGSEGV.
Fix: CefManager::acquire() was moved out of the BrowserOp constructor into process(), so CEF is only initialized when the operator actually runs — not during descriptor probing.
- CPU pixel path — CEF's OnAcceleratedPaint with shared textures is not available on macOS. Pixel upload is ~0.5-1ms for 720p on Apple Silicon.
- No WebGL/WebGPU — GPU is disabled in single-process mode, so GPU-accelerated web content won't render.
- Single consumer per stream_id — only one BrowserAudioIn can claim a stream ID at a time.
- Rich browser audio diagnostics (buffer depth + drift telemetry ports)
