Skip to main content

Building and packaging

FreeMoCap ships to users as an Electron desktop app with a bundled, frozen Python backend. The repository is also set up as an installable Python package (its pyproject.toml defines a freemocap console script and cuda/cpu extras for pip users), but the user-facing release artifacts are these installers:

freemocap_<version>_<label>.<ext> e.g. freemocap_2.0.0-alpha.25_windows-x64-cpu.exe
│ (NSIS .exe on Windows; .dmg and .zip on Apple Silicon macOS,
│ .AppImage and .deb on Linux)
├── FreeMoCap(.exe) Electron launcher
└── resources/
├── app.asar Electron + React frontend (dist/, dist-electron/)
└── app.asar.unpacked/
├── freemocap_server/ PyInstaller onedir bundle: the
│ freemocap_server(.exe) launcher beside its
│ _internal/ tree of DLLs and data files
└── freemocap-logo.png

Model weights are not in the installer. RTMPose and YOLOX ONNX models and MediaPipe .task files are downloaded at first use and cached under the home directory (~/.cache/skellytracker/models and ~/.freemocap/skellytracker-models, respectively).

PyInstaller for the backend​

The Python backend is frozen into a standalone executable via PyInstaller, configured by freemocap.spec at the repo root:

pyinstaller freemocap.spec

The entry point is freemocap/__main__.py; the bundle carries OpenCV, SciPy, pandas, MediaPipe, ONNX Runtime (both GPU and CPU execution providers), the NVIDIA CUDA libraries it needs, and the other Skelly packages (SkellyCam, SkellyTracker, SkellyForge) as real dependencies rather than expecting them preinstalled. CUDA collection is gated on FREEMOCAP_BUILD_VARIANT=cuda (the default); CPU-variant builds skip the NVIDIA packages entirely. Development-only tooling (pytest, tkinter, IPython) and unused packages (numba, scikit-learn, MediaPipe's own dev tools) are explicitly excluded to keep the bundle smaller. Output is a onedir bundle named freemocap_server (freemocap_server.exe on Windows, equivalent unsuffixed launchers on macOS and Linux): a launcher executable beside an _internal/ tree of DLLs and data files, not a single self-contained file. The spec also adds run_blender_export.py and the freemocap_blender_addon package source to the bundle as loose data files, because Blender executes them with its own Python interpreter, which cannot load them from PyInstaller's embedded archive.

Electron Builder for the frontend​

The React frontend is packaged with electron-builder (freemocap-ui/electron-builder.json):

cd freemocap-ui

npm run dev # development: Vite + Electron together
npm run build # production build
npm run preview # preview the production build

Key packaged dependencies: react 19, Redux Toolkit, Three.js (via @react-three/fiber and @react-three/drei), the Monaco editor, D3, and i18next for the frontend's 41 locales.

CI/CD​

Build and deploy workflows live in .github/workflows/:

WorkflowPurpose
build-installers-pyinstaller.ymlBuilds the platform-specific installers (CPU and CUDA variants) on pushes to development and on v* tags, then assembles a draft GitHub Release on tag pushes. Release assets are renamed to freemocap_<version>_<label>.<ext>, with a matching freemocap_server_*.zip per platform. CUDA builds are too large for GitHub and are uploaded to Cloudflare R2 instead
deploy-docs.ymlBuilds and deploys the repository's own docs site (freemocap-docs/) to GitHub Pages, not the FreeMoCap.github.io site this page belongs to
test.ymlbackend tests and a frontend typecheck on pull requests to main and development
test-bucket.ymlManual smoke test that uploads a file to the Cloudflare R2 release bucket

Versioning​

Both halves report the same version, for example v2.0.0-alpha.25 in freemocap/__init__.py (backend) and 2.0.0-alpha.25 in freemocap-ui/package.json (frontend). There is one canonical version string, current_version under [tool.bumpver] in pyproject.toml, and a bump (for example uv run poe bump-alpha) rewrites the Python and Electron version strings together in a single commit and tag, so the two move in lockstep across releases; they can only diverge if someone edits one of the files by hand. electron-updater handles auto-updates for installed releases.

← Architecture overview