Infrastructure for capturing paired screencast and keyboard/mouse input data.
Download the installer for your platform and follow the instructions in the setup wizard.
- macOS: open
CrowdCast.dmgand grant permissions by following the setup wizard. - Windows: run
crowd-cast-setup.exe. The agent runs from a single executable; OBS is fetched automatically on first launch and no special permissions are required. If SmartScreen shows "Windows protected your PC", click More info, then Run anyway.
- Single binary: No external OBS installation required (libobs is embedded)
- Privacy-aware capture: Only records when selected applications are in the foreground
- Full control: Start or stop recording at any time, and delete the last 10 minutes of recording
- Automatic updates: Sparkle framework keeps the app up to date in the background
- Idle detection: Automatically pauses recording when you step away, resumes on return
- Hardware acceleration: Uses native encoding (VideoToolbox on macOS)
- Efficient uploads: Streaming uploads via pre-signed S3 URLs with retry/backoff
- Easy setup: Wizard handles permissions and application selection
crowd-cast is a single-binary agent that embeds libobs for screen capture and recording, eliminating the need to install OBS Studio separately.
Key components:
- Embedded libobs - Screen/window capture with hardware encoding (via libobs-rs)
- Sync Engine - Coordinates recording with input capture, filters by frontmost app
- Input Capture - Cross-platform keyboard/mouse capture (rdev/evdev)
- System Tray - Control recording from the menu bar
┌─────────────────────────────────────────────────────────────────┐
│ crowd-cast Agent (Rust) │
│ ┌──────────┐ ┌────────────────┐ ┌─────────────────────────┐ │
│ │ Tray UI │ │ Embedded libobs│ │ Sync Engine │ │
│ │ │ │ (libobs-rs) │ │ - Frontmost app detect │ │
│ └──────────┘ │ │ │ - Input filtering │ │
│ │ ┌───────────┐ │ │ - Event buffering │ │
│ │ │mac-capture│ │ └───────────┬─────────────┘ │
│ │ │obs-x264 │ │ │ │
│ │ │obs-ffmpeg │ │ ┌─────┴─────┐ │
│ │ └───────────┘ │ │ rdev/evdev│ │
│ └────────────────┘ └───────────┘ │
│ │ │ │
│ Video Output Input Events │
│ │ │ │
│ └───────────┬───────────┘ │
│ │ │
│ ┌─────┴─────┐ │
│ │ Uploader │ │
│ └─────┬─────┘ │
└────────────────────────────────────┼────────────────────────────┘
│
▼
┌─────────────┐
│ Lambda + S3 │
└─────────────┘
- Single binary: No external OBS installation required (libobs is embedded)
- Privacy-aware capture: Only records when selected applications are in the foreground
- Automatic updates: signed background updates on macOS, Windows, and Linux
- Idle detection: Automatically pauses recording when you step away, resumes on return
- Hardware acceleration: Uses native encoding (VideoToolbox on macOS)
- Efficient uploads: Streaming uploads via pre-signed S3 URLs with retry/backoff
- Easy setup: Wizard handles permissions and application selection
Download the installer for your platform from the latest release:
- macOS:
CrowdCast.dmg - Windows:
crowd-cast-setup.exe - Linux:
curl -fsSL https://github.com/p-doom/crowd-cast/releases/latest/download/install-linux.sh | bash
Linux support is limited to GNOME on Wayland and sway. GNOME supports per-app capture; sway currently supports full-screen capture.
# Clone the repository
git clone https://github.com/p-doom/crowd-cast.git
cd crowd-cast
# Build (endpoint required at build time)
CROWD_CAST_API_GATEWAY_URL="https://your-api-gateway.execute-api.region.amazonaws.com/prod/presign" \
cargo build --release
# Run the setup wizard
./target/release/crowd-cast-agent --setupBuild speed:
cargo build --releaseis tuned for fast incremental rebuilds (~seconds, not minutes) — LTO is off because it costs minutes and buys nothing for this libobs-backed app. For an even quicker loop usecargo run(debug).
On macOS, build.rs automatically installs OBS binaries via cargo-obs-build during
cargo build. Set CROWD_CAST_SKIP_OBS_INSTALL=1 to skip this behavior.
On Linux there is no automatic OBS install, so the linker has to be told where
libobs lives — otherwise the build fails at link time with
rust-lld: error: unable to find library -lobs. Set LIBOBS_PATH to a directory
that contains libobs.so (the unversioned linker symlink), built for the OBS ABI in
CROWD_CAST_OBS_ABI (default 32.0.2):
# Linux build
LIBOBS_PATH=/path/to/obs/usr/lib \
CROWD_CAST_API_GATEWAY_URL="https://your-api-gateway.execute-api.region.amazonaws.com/prod/presign" \
cargo build --releasecargo check does not link, so it will not catch a missing or wrong LIBOBS_PATH —
only cargo build/run will.
- Grant Accessibility permission to the agent (System Settings → Privacy & Security → Accessibility)
First-time setup on a release machine:
scripts/setup-macos-signing.sh \
--p12 /path/to/developer-id.p12For a full release (build, sign, notarize, publish to GitHub Releases + upload appcast to S3):
scripts/build-and-publish-macos.sh \
--github-repo p-doom/crowd-cast \
--s3-bucket crowd-cast-bucket \
--identity "Developer ID Application: Your Name (TEAMID)" \
--notarize \
--version 1.0.0 \
--build-number 1055 \
--sparkle-public-ed-key "YOUR_PUBLIC_KEY" \
--sparkle-private-ed-key-file /path/to/private-key.txtAuto-updates are delivered via Sparkle using an appcast hosted on S3.
Linux installs into user space under ~/.local and downloads the matching libobs bundle during installation. The production installer is published with each release:
curl -fsSL https://github.com/p-doom/crowd-cast/releases/latest/download/install-linux.sh | bashSupported Linux sessions are GNOME on Wayland and sway. Other desktop sessions are blocked during setup so the agent does not run in an unvalidated capture mode. Linux auto-updates use a signed appcast hosted on S3 and install silently when recording and uploads are idle.
Download crowd-cast-setup.exe from the latest release and run the setup wizard. Windows auto-updates are delivered through the signed Windows appcast.
Most settings are managed through the setup wizard and the tray menu. The configuration file is at:
- macOS:
~/Library/Application Support/dev.crowd-cast.agent/config.toml - Linux:
~/.config/agent/config.toml - Windows:
%APPDATA%\agent\config.toml
Key settings:
[capture]
target_apps = ["org.mozilla.firefox", "com.apple.Terminal"]
capture_all = false
idle_timeout_secs = 120 # Pause after 2 min of inactivity
single_active_app_capture = true # One app captured at a time (multi-scene)
[recording]
autostart_on_launch = true
notify_on_start_stop = true
segment_duration_secs = 300 # 5-minute recording segments
[upload]
delete_after_upload = trueUpload endpoint is set at build time via CROWD_CAST_API_GATEWAY_URL.
Input logs are stored in MessagePack format. Each file contains an array of [timestamp_us, [event_type, event_data]] tuples:
[0, ["ContextChanged", ["com.apple.Terminal"]]]
[1234000, ["KeyPress", [0, "KeyA"]]]
[1334000, ["KeyRelease", [0, "KeyA"]]]
[1500000, ["MouseMove", [12.5, -3.2]]]
[2000000, ["MousePress", ["Left", 540.0, 320.0]]]
[2100000, ["MouseRelease", ["Left", 540.0, 320.0]]]
[2500000, ["MouseScroll", [0.0, -3.0, 540.0, 320.0]]]
[3999000, ["ContextChanged", ["UNCAPTURED"]]]
Event types:
ContextChanged: app switch (bundle ID orUNCAPTUREDfor untracked apps)KeyPress/KeyRelease:[key_code, key_name]MouseMove:[delta_x, delta_y]MousePress/MouseRelease:[button, x, y]MouseScroll:[delta_x, delta_y, x, y]
Timestamps are microseconds relative to the segment start. Video and input files share the same session/segment IDs for alignment.
This section is for contributors who want to modify crowd-cast.
crowd-cast-agent --setupThis runs the interactive setup wizard that guides you through configuration.
crowd-cast-agentThe agent will:
- Bootstrap OBS libraries (downloads if needed)
- Initialize embedded libobs for capture
- Show in your system tray
- Capture input when selected apps are in foreground
crowd-cast-agent [OPTIONS]
OPTIONS:
-h, --help Print help message
-s, --setup Run the setup wizard (re-select apps, etc.)
ENVIRONMENT:
RUST_LOG Set log level (e.g., debug, info, warn)
CROWD_CAST_LOG_PATH
Override log directory (default: ~/Library/Logs/crowd-cast on macOS)
CROWD_CAST_API_GATEWAY_URL
Lambda endpoint for pre-signed S3 URLs (set at build time)
macOS (Apple Silicon):
brew install simde # Required for ARM builds
brew install create-dmg # Required for release DMG packaging# 1. Clone with submodules
git clone --recursive https://github.com/p-doom/crowd-cast.git
cd crowd-cast
# 2. Build the agent (macOS auto-installs OBS binaries in build.rs)
cargo build
# 3. Run tests
cargo testThe agent uses libobs-rs to embed OBS functionality. Key crates:
libobs- Raw FFI bindings to libobslibobs-wrapper- Safe Rust wrapperlibobs-bootstrapper- Downloads OBS binaries at runtime (macOS)cargo-obs-build- Downloads OBS binaries at build time
The fork at libobs-rs/ includes macOS support from PR #53.
To add support for new capture types, implement them in src/capture/sources.rs:
pub fn new_window_capture(ctx: &ObsContext, window_name: &str) -> Result<ObsSourceRef> {
// Use libobs-wrapper to create window capture source
}First-time setup on a release machine:
scripts/setup-macos-signing.sh \
--p12 /path/to/developer-id.p12For a full release (build, sign, notarize, publish to GitHub Releases + upload appcast to S3):
scripts/build-and-publish-macos.sh \
--github-repo p-doom/crowd-cast \
--s3-bucket crowd-cast-bucket \
--identity "Developer ID Application: Your Name (TEAMID)" \
--notarize \
--version 1.0.0 \
--build-number 1055 \
--sparkle-public-ed-key "YOUR_PUBLIC_KEY" \
--sparkle-private-ed-key-file /path/to/private-key.txtAuto-updates are delivered via Sparkle using an appcast hosted on S3.
Build the per-user installer (no admin / UAC required) with Inno Setup:
# One-time: install the Inno Setup compiler
winget install JRSoftware.InnoSetup
# Build the release binary + installer
$env:CROWD_CAST_API_GATEWAY_URL = "https://.../prod/presign"
pwsh scripts/build-windows-installer.ps1
# -> dist/crowd-cast-setup-<version>.exeThe installer (installer/windows/crowd-cast.iss) installs the agent and its
obs.dll loader under %LOCALAPPDATA%\Programs\crowd-cast, creates a Start Menu
shortcut tagged with the app's AppUserModelID (so toast notifications are branded
"crowd-cast"), and registers an uninstaller that stops the agent and removes the
autostart entry and install directory. Autostart itself is managed in-app via the
setup wizard's "start at login" option.
On first launch the agent downloads the rest of the OBS runtime (codecs, plugins) into the install folder and relaunches itself automatically, a one-time step that needs network access.
Support coming soon...
The agent expects a Lambda endpoint that returns pre-signed S3 URLs. Example Lambda handler:
import boto3
import json
s3 = boto3.client('s3')
BUCKET = 'your-bucket'
def handler(event, context):
body = json.loads(event['body'])
file_name = body['fileName']
version = body['version']
user_id = body['userId']
key = f"uploads/{version}/{user_id}/{file_name}"
content_type = (
"application/msgpack" if file_name.endswith(".msgpack") else "video/mp4"
)
upload_url = s3.generate_presigned_url(
'put_object',
Params={'Bucket': BUCKET, 'Key': key, 'ContentType': content_type},
ExpiresIn=3600
)
return {
'statusCode': 200,
'body': json.dumps({
'uploadUrl': upload_url,
'key': key,
'contentType': content_type,
})
}Overlay keylogs on top of a screen capture:
python scripts/overlay_keylogs.py --video capture.mp4 --input input.msgpack --output capture_with_keys.mp4To just generate subtitles (ASS):
python scripts/overlay_keylogs.py --input input.msgpack --ass-out keylogs.assContributions welcome! Please open an issue first to discuss proposed changes.
MIT License, see LICENSE.md