Frame-perfect multi-camera synchronization for USB webcams 💀📸
SkellyCam turns a handful of cheap USB webcams into a synchronized multi-camera system. It guarantees frame-perfect synchronization — every camera produces the exact same number of frames, and each "multi-frame" event delivers one image from every camera captured at the same moment. This makes it suitable for applications like markerless motion capture, 3D reconstruction, and multi-view computer vision where frame-number identity across cameras is non-negotiable.
SkellyCam is the camera backend for the FreeMoCap markerless motion capture project.
- Synchronized recording — all cameras produce videos with precisely the same frame count, with high-resolution
perf_counter_nstimestamps for post-hoc analysis - Live streaming — WebSocket protocol delivers synchronized multi-frame payloads (one image per camera per frame event) in real time
- Frame-locked playback — recorded videos always display the exact same frame number across all cameras, with frame-stepping, variable-speed, and keyboard shortcuts
- Audio capture — optional microphone recording alongside video
- Recording browser — browse saved sessions with metadata (camera count, file size, duration, FPS, frame count), sorted newest-first
- Internationalization — UI available in 36+ languages (help us translate!)
The easiest way to get started is to download the installer for your platform:
Download SkellyCam | GitHub Releases
The installer bundles everything you need — no Python or Node.js required.
For developers who want to run from the source code:
# Clone and install the Python server
git clone https://github.com/freemocap/skellycam
cd skellycam
uv venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
uv sync
# Start the server
python -m skellycam
# or just: skellycam
# In another terminal — install and start the UI
cd skellycam-ui
npm install
npm run devServer starts on http://localhost:53117. Swagger docs at http://localhost:53117/docs.
See the Development guide for full setup instructions including prerequisites and troubleshooting.
Audio recording requires additional system packages:
sudo apt update && sudo apt install clang portaudio19-devEach camera runs in its own OS process to avoid the GIL. The CameraOrchestrator enforces synchronization through a two-phase capture protocol:
- Grab — all cameras call
cv2.VideoCapture.grab()simultaneously, latching sensor images in driver buffers without transferring pixel data - Retrieve — after all cameras have grabbed, each calls
cv2.VideoCapture.retrieve()to decode the latched frame
The orchestrator assembles one frame from each camera into a single multi-frame payload — the atomic unit of data throughout the system. Consumers (WebSocket stream, video recorder, frontend) always see exactly one frame per camera per event.
For the full architecture (process model, IPC, data flow, playback sync), see the Architecture docs.
| Page | Description |
|---|---|
| Quick Start | Installation, first run, and basic workflow |
| Beginner Tutorial | Camera selection, configuration, and recording |
| Advanced Tutorial | Data model, folder structure, server configuration |
| Architecture | Synchronization protocol, process model, data flow |
| API Reference | HTTP and WebSocket endpoint documentation |
| WebSocket Protocol | Binary frame format, JSON messages, backpressure |
| Development | Running from source, testing, linting, CI, and contributing |
| Method | Path | Description |
|---|---|---|
GET |
/health |
Health check |
GET |
/shutdown |
Graceful shutdown |
POST |
/skellycam/camera/detect |
Detect available cameras |
GET |
/skellycam/camera/microphone/detect |
Detect available microphones |
POST |
/skellycam/camera/group/apply |
Create or update camera group |
POST |
/skellycam/camera/group/all/record/start |
Start recording |
GET |
/skellycam/camera/group/all/record/stop |
Stop recording |
DELETE |
/skellycam/camera/group/close/all |
Close all camera groups |
GET |
/skellycam/camera/group/all/pause_unpause |
Toggle pause |
GET |
/skellycam/playback/recordings |
List recordings with stats |
POST |
/skellycam/playback/load |
Load a recording for playback |
GET |
/skellycam/playback/videos |
List loaded videos |
GET |
/skellycam/playback/video/{video_id} |
Stream a video file |
GET |
/skellycam/playback/timestamps |
Get timestamps for all loaded videos |
GET |
/skellycam/playback/timestamps/{video_id} |
Get timestamp metadata for a video |
WS |
/skellycam/websocket/connect |
Real-time frames and logs |
Full details in the API Reference.
uv sync --group dev # Install dev dependencies
uv run pytest skellycam/tests/ -v # Run tests
uv run ruff check skellycam/ # Lint
uv run poe test # Via task runnerSee the Development guide for the full setup.
AGPL-3.0-or-later — see LICENSE. Contact the FreeMoCap team for alternative licensing.
See CONTRIBUTING.md. Translations welcome — see TRANSLATING.md.
