Webcam Bridge for Nikon: use your Nikon USB webcam in Zoom, Teams, or OBS as a virtual camera, with HTTP control for focus and exposure. Windows first, open source.
Table of Contents
Windows-first bridge that will consolidate Nikon USB webcam (UVC) access with PTP-style control in one logical service, then fan out video through a virtual camera (Media Foundation) while exposing a separate HTTP control API for focus, exposure, and related commands.
This repository is a pnpm workspace: shared core types, a headless HTTP API package, a video adapter package (MF virtual camera, planned), an Electron desktop shell, and a SolidStart marketing site. Each workspace has its own README with a focused diagram where it helps; this file ties the story together.
| Path | Role |
|---|---|
| packages/core | Types and small state helpers (no Electron, no MF). |
| packages/api | HTTP JSON control surface (/health, stub /v1/command, …). |
| packages/video | MF virtual camera registration and frame pump (planned). |
| apps/desktop | Electron UI and IPC stubs for local control. |
| apps/site | SolidStart static site; published to GitHub Pages (see below). |
The apps/site app builds to static HTML (prerendered / and /about). In Settings → Pages, set Build and deployment source to GitHub Actions. Pushes to main that touch apps/site/**, the Pages workflow, or the root lockfile run .github/workflows/pages.yml and publish to:
https://AMDphreak.github.io/nikon-camera-bridge/
Production-style preview (matches the Pages base path):
$env:VITE_BASE_PATH="/nikon-camera-bridge/"
pnpm run build:site
pnpm dlx serve apps/site/.output/publicLocal dev (Vite base /):
pnpm run dev:siteOne core owns USB and coordinates two outward adapters: video into MF, control into your HTTP API. Media Foundation is the Microsoft library you use to implement the virtual camera; it is not a replacement for your own control API.
MF is a Windows user-mode API used to register and feed a virtual camera. Consuming apps talk to Windows; your process registers the device and pushes frames. Your control API does not need to go through MF.
Requirements: Node 20+, pnpm 9+.
pnpm install
pnpm devprepare runs pnpm run build:packages after pnpm install so every library has a dist/ output before the desktop typecheck or build. The desktop package sets build.electronVersion so electron-builder can resolve Electron under pnpm's hoisted layout.
HTTP API (stub server):
pnpm run dev:apiTypecheck entire workspace:
pnpm typecheckProduction build (all packages + Electron bundle):
pnpm buildWindows desktop (x64 + arm64 .msi, .msix, and portable .zip; unsigned):
pnpm run build:winLinux desktop (x64 + arm64 .deb, .AppImage, and .flatpak bundle; requires Flatpak tooling for the Flatpak target):
pnpm run build:linuxmacOS desktop (x64 + arm64 .dmg, unsigned):
pnpm run build:macPack npm tarballs for the three libraries (written to dist-pack/). Locally:
pnpm run pack:packagesOn GitHub Actions, library .tgz files are produced on the Windows desktop matrix leg (pnpm run pack:packages) so packing matches local developer machines; the Ubuntu job still compiles all packages for Linux CI coverage.
Electron writes each OS/arch combination under apps/desktop/release/<staging-folder>/ so x64 and arm64 builds never share the same win-unpacked tree (avoids file locks on Windows).
- CI (
.github/workflows/ci.yml): Ubuntu buildspackages/*and the static marketing site (pnpm run build:sitewith the GitHub Pages base path). A desktop matrix on Windows, Ubuntu, and macOS typechecks the workspace, runspnpm build, then packages x64 + arm64 artifacts per OS (Windows MSI + MSIX + zip, Linux deb + AppImage + flatpak, macOS DMG). The Windows leg also runspnpm run pack:packagesand uploadsbridge-windows(MSI, MSIX, zips, library.tgz). Linux and macOS legs uploadbridge-linuxandbridge-macos. - Pages (
.github/workflows/pages.yml): On pushes tomain, buildsapps/siteand deploys the prerendered bundle to GitHub Pages (enable Pages → GitHub Actions in repo settings first). - Release (
.github/workflows/release.yml): Onv*tags, merges allbridge-*artifacts, runsscripts/render-winget.mjsandscripts/render-homebrew-cask.mjs, and publishes everything underrelease-assets/**(binaries, WinGet YAML, Homebrew cask Ruby) to the GitHub Release.
See CHANGELOG.md.
Pull requests and issues are welcome. By contributing, you agree your contributions are under the same license (see CONTRIBUTING.md).
For per-person profile links, prefer all-contributors.
This project is licensed under the GNU Affero General Public License v3.0 or later (SPDX: AGPL-3.0-or-later). See LICENSE for the full text.
Why AGPL? It is a strong copyleft license: if someone modifies this code and distributes it or runs it as a networked service for others, they generally must offer their source under the same license. That discourages proprietary "copycat" forks and scam repackagers who won't publish source, while still allowing anyone to study, improve, and redistribute the project and to charge for binaries as long as they comply with the license.
Ryan Johnson — @amdphreak
Project Link: https://github.com/AMDphreak/nikon-camera-bridge