🤖AI-generated documentation✓ curatedAI Generated
About content generation types
(e.g., docs generated from codebase analysis)
(e.g., livestream → blog post, meeting notes → docs)
(e.g., hand-written tutorial)
Development
The frontend and backend are independent processes. If you're only working on backend code, you can use the standard app installer for the frontend and set server auto-connect to false, then run your backend from source. Vice versa for frontend-only work — just run the installed backend server in a terminal and it will connect to a UI running from source. This saves setup time and lets you focus on the part you're changing.
Install from Source
This section is for developers and contributors who want to run SkellyCam from the source code. If you just want to use SkellyCam, download the installer from the Download page instead.
Prerequisites
- Python 3.11+ (3.12 recommended) — python.org/downloads
- uv — Fast Python package manager: astral.sh/uv
- Node.js 18+ — Required for the React/Electron UI: nodejs.org
- USB cameras or built-in webcams
Clone and Install
git clone https://github.com/freemocap/skellycam
cd skellycam
uv venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
uv sync --group dev
This installs runtime dependencies plus development tools: pytest, pytest-asyncio, ruff, poethepoet, and build tools.
Linux Audio Dependencies
Audio recording on Linux requires additional system packages:
sudo apt update
sudo apt install clang portaudio19-dev
Install the Frontend UI
cd skellycam-ui
npm install
Running in Development Mode
Start the Python server:
python -m skellycam
# or just: skellycam
The server starts on http://localhost:53117. Verify it's running at http://localhost:53117/health. Swagger API docs are at http://localhost:53117/docs.
Start the UI (in a separate terminal):
cd skellycam-ui
npm run dev
This launches the Vite dev server. If running inside Electron, it launches the Electron window; otherwise, open the URL printed in the terminal.
Verifying the Installation
Run the test suite to confirm everything is wired up correctly:
uv run pytest skellycam/tests/ -v
All tests should pass without requiring physical cameras — the test suite uses mocks and a lightweight FastAPI test client.
Troubleshooting
Port 53117 already in use — SkellyCam automatically kills any existing process on port 53117 at startup. If this fails, manually stop the conflicting process or change the port in skellycam/api/server_constants.py.
No cameras detected — Check that your USB cameras are plugged in and recognized by the OS. On Linux, ensure your user has permission to access /dev/video* devices (you may need to add your user to the video group: sudo usermod -aG video $USER).
Audio not working on Linux — Make sure you've installed the system packages: sudo apt install clang portaudio19-dev.
Setting Up the Development Environment
If you've already followed the "Install from Source" steps above, your development environment is ready. The uv sync --group dev command installs all development tools.
Running Tests
Backend (Python)
# Run all tests
uv run pytest skellycam/tests/ -v
# Run a specific test file
uv run pytest skellycam/tests/test_health.py -v
# Run with short tracebacks
uv run pytest skellycam/tests/ -v --tb=short
The test suite uses a lightweight FastAPI TestClient with mocked camera dependencies. No physical cameras are required.
asyncio_mode = "auto" is set in pyproject.toml, so async test functions do not need the @pytest.mark.asyncio decorator.
Test Structure
skellycam/tests/
├── conftest.py # Shared fixtures (mock app, client, mock managers)
├── mocks/
│ ├── camera_mock.py # MockVideoCapture (simulates cv2.VideoCapture)
│ └── test_camera_mock.py # Tests for the mock itself
├── test_camera_config.py # CameraConfig model logic
├── test_camera_config_extended.py # Extended config tests
├── test_camera_group_manager.py # CameraGroupManager creation and singleton
├── test_camera_orchestrator.py # Orchestrator synchronization tests
├── test_camera_router.py # Camera REST endpoint tests
├── test_frontend_payload_and_recording.py # Binary payload creation and recording tests
├── test_health.py # Health and root endpoint tests
├── test_playback.py # Playback endpoint tests
├── test_pubsub.py # IPC publish/subscribe tests
├── test_shared_memory.py # Shared memory ring buffer tests
├── test_shutdown.py # Shutdown endpoint tests
├── test_timestamps_and_framerate.py # Timestamp and framerate tracking tests
├── test_websocket.py # WebSocket connection and protocol tests
├── test_websocket_internals.py # WebSocket server internal logic tests
└── test_worker_lifecycle.py # Worker process lifecycle tests
Key Test Fixtures (conftest.py)
mock_camera_group_manager— A MagicMock standing in for CameraGroupManager with AsyncMock async methods. Patchesget_or_create_camera_group_managerat all import sites.app— A lightweight FastAPI app with the same routes but no heavy lifespan (no bytecode compilation, no logging setup).client— A synchronousTestClientwrapping the test app, suitable for both HTTP and WebSocket tests.
Frontend (TypeScript)
cd skellycam-ui
# Type checking
npx tsc --noEmit
# End-to-end tests (requires Electron)
npm run e2e
Linting
SkellyCam uses Ruff for linting, configured in pyproject.toml.
# Check for lint issues
uv run ruff check skellycam/
# Auto-fix lint issues
uv run ruff check --fix skellycam/
The primary lint rule enabled is TC (flake8-type-checking), which moves type-only imports behind if TYPE_CHECKING: blocks. This reduces import time for spawned child processes.
Suppressing False Positives
If Ruff tries to move an import that is needed at runtime (e.g., used in isinstance(), TypeAdapter, or Pydantic field types), add a # noqa: TC001/TC002/TC003 comment:
from skellycam.core.camera.config.camera_config import CameraConfig # noqa: TC003
Task Runner
poethepoet provides shorthand task commands:
uv run poe test # Run pytest
uv run poe lint # Run ruff check
uv run poe lint-fix # Auto-fix ruff violations
uv run poe tc-check # Preview TYPE_CHECKING import moves
uv run poe tc-fix # Apply TYPE_CHECKING import moves
uv run poe tc # Apply imports + run tests to verify
Continuous Integration
GitHub Actions runs on every push and pull request (.github/workflows/test.yml):
- Backend tests — Python 3.11 and 3.12 on Ubuntu, Windows, and macOS
- Linting — Ruff check on all platforms
- Frontend typecheck — TypeScript
tsc --noEmiton Ubuntu
Code Organization Conventions
Python
- Type hints everywhere — All function signatures, return types, and variables should have type annotations.
- New-style type hints — Use
str | Noneinstead ofOptional[str],dict[str, int]instead ofDict[str, int]. - Global imports only — No local imports inside functions or methods.
- Fail loudly — Raise exceptions on errors instead of printing warnings or returning defaults.
- Pydantic models — Used for all API request/response schemas and configuration objects.
- Logging — Uses skellylogs with custom levels like
logger.trace(),logger.success(), andlogger.api().
TypeScript (Frontend)
- React 19 functional components with hooks
- Redux Toolkit for state management with typed hooks
- Material UI for component styling
- OffscreenCanvas workers for live camera frame rendering
- Frame-locked playback — recorded videos use a leader-based synchronization strategy: the first video drives canonical time via its native
.play(), arequestAnimationFrameloop readsleader.currentTimeto derive the frame number, and follower videos are drift-corrected when they diverge beyond 2 frames. Overlays are updated via direct DOM refs to avoid React re-renders during playback.
Adding a New API Endpoint
- Create a new router file in the appropriate
skellycam/api/http/subdirectory. - Define your Pydantic request/response models.
- Add the router to
skellycam/api/routers.py:from skellycam.api.http.your_module.your_router import your_router
SKELLYCAM_ROUTERS = [..., your_router] - Write tests in
skellycam/tests/test_your_router.pyusing theclientfixture fromconftest.py.
Adding a New Test
- Create a test file in
skellycam/tests/test_*.py. - Use the
clientfixture for HTTP/WebSocket endpoint tests. - Use
mock_camera_group_managerfor tests that need to interact with camera management. - Async test functions work automatically — no decorator needed (
asyncio_mode = "auto").
Building Installers
Python Executable (PyInstaller)
pyinstaller skellycam.spec
The spec file at the repository root (skellycam.spec) defines the build configuration. This produces a standalone server executable.
Electron App
cd skellycam-ui
npm install && npm run build
See electron-builder.json for packaging configuration.