Skip to main content

webATEM. Browser control for ATEM switchers. Run the server, open a tab, cut the show.

Browser control for ATEM switchers. Run the server on a Mac, a Windows PC or a Linux box, and control your ATEMs in a browser from any device on the network: desktop, laptop, tablet or phone. WebATEM finds the switchers for you and gives you the whole control surface: switching, keyers, audio, media pool, macros. Run the server, open a tab, cut the show.

Latest release On PyPI Tests MIT licensed

Download for Windows   Download for macOS, Apple silicon   Download for macOS, Intel   Download for Linux

Download it, open it, and your switchers appear.  ·  Windows installs per-user, no admin needed.  ·  The Linux build runs on desktop and headless servers.

Three ways to run it, all simple:

  • Just you, right now → download it for your Mac or Windows machine and open it. It lives in your menu bar / system tray (with a Start at login switch), opens your browser, and your switchers appear. Like ATEM Software Control, but in your browser, and on every device on the network.
  • Your whole team, always on → host it on a Linux box, VM or Raspberry Pi with one Docker command; everyone opens a URL. No installs, no accounts.
  • Already have Python? → pipx install "webatem[desktop]", then webatem.

Every way of running it auto-discovers the ATEMs on your network (Bonjour/mDNS) and lists them by name, exactly like ATEM Software Control. Open the page and they are already there:

WebATEM connect page: recent connections and the switchers found on the network, each listed by name and address

WebATEM control surface: program/preview buses, T-bar, keyers, transitions and macros

What you get

  • Full switcher control. Program/preview buses per M/E, cut/auto, transition styles (mix, dip, wipe, DVE, stinger) with per-style settings, fade to black, color generators, aux routing, source renaming that follows live switcher labels.
  • Upstream and downstream keyers. USK 1 to 4 (luma, chroma, pattern, DVE with fly keyframes, masks), DSKs with tie/rate/clip/gain, on-air countdowns.
  • Fairlight audio. Per-strip faders, EQ, dynamics, master bus, and live audio meters streamed over WebSocket (tab-gated so idle pages cost nothing).
  • Media pool. Live thumbnails of every still slot, and drag-and-drop image upload straight onto a slot (validated and resized to 1080p server-side).
  • Macros. Browse and run switcher macros.
  • Save and restore switcher state. Full switcher profile export/import as XML, compatible with ATEM Software Control’s “Save Switcher State”, including media pool images and macro bytecode.
  • HyperDeck transport. Clip browser and play/pause/stop/loop for decks bound to the ATEM (TCP/9993), from the same page.
  • Multi-operator by design. One pooled connection per switcher shared by all operators; state fan-out over WebSockets with adaptive polling (30 ms during transitions, relaxed when idle).

Deep per-switcher settings live in the Settings panel: color generators, each upstream keyer (luma / chroma / pattern / DVE), downstream keyers, transitions, video mode, input labels, outputs and HyperDecks:

WebATEM settings panel: color generators and an upstream keyer expanded, showing HSL controls, fill-source dropdown and wipe patterns

Full Fairlight audio, a per-strip mixer with input gain, EQ and dynamics curves, faders, pan and live meters, and a writable 6-band parametric EQ behind each strip:

WebATEM Fairlight audio mixer: 14 strips with input, EQ, dynamics, faders, pan and meters

---

Which switchers has it run on?

Every hour it has ever run has been against ATEM 1 M/E Constellation HD switchers, where it drives a working broadcast operation daily. That is one model out of a large family, and it is the honest answer to "will it work with mine".

It should work more widely than that. The protocol is the same across the range, the control surface is built from what each switcher reports about itself rather than from a hardcoded model list, and features a switcher does not have simply do not appear. But should is not the same as does, and nobody has checked.

So if you have a Mini, a Mini Pro, a Television Studio, a 2 M/E or 4 M/E, or anything else with an ATEM badge on it, trying this and telling me what happened is the single most useful thing you can do for the project. It does not matter whether it works. An issue saying which model you have and what went wrong is worth more than one that says it went fine.

A switcher report takes a minute and needs no code. If you go further and fix what you found, a patch for a switcher I do not own is welcome on your word plus a green test suite: see CONTRIBUTING.md.

One known limit while you are here: still upload and capture assume a 1080p switcher. Other video modes connect and control fine, but the media features are untested there.


Get started

Option A. Download it (Mac, Windows)

  1. Click your platform's Download button at the top (or the Releases page).
  2. Open it. The icon appears in the menu bar / tray and the launcher window comes up: pick the interface and port if the defaults are not right, then Launch GUI opens the control page in your browser with the ATEMs on your network listed. Pick one and you're controlling it.

That's the whole setup. Nothing to install alongside it, no Python, no Docker.

On a Mac, one line in Terminal does both steps and skips the first-launch security prompt described below (it downloads the right build for your chip, puts WebATEM in Applications and opens it):

curl -fsSL https://github.com/lucas-romanenko/webatem/releases/latest/download/install-mac.sh | sh

While it runs it is a small window plus an icon in the menu bar (Mac) / system tray (Windows), the way Bitfocus Companion works, with nothing in the Dock or on the taskbar, only the icon. The window shows Running and the address other devices use, and holds the settings: Interface (all of them, one adapter, or this computer only), Port, Start minimized, Run at login, with Launch GUI (opens the control page in your browser), Hide and Quit. Closing the window just hides it; the tray menu is three items: Show/Hide window, Launch GUI, Quit. Changing the interface or port restarts the server on the new address right there; handy on a laptop with Wi-Fi, Ethernet and a VPN at once. Launch GUI opens the address shown, the real interface and port, the same one other devices use (this computer also answers at 127.0.0.1 whichever interface is chosen). Nothing listens until you have picked an interface in the window: it opens on Not running and waits for your choice. Tick Run at login (or Start minimized) and it comes back up on that interface by itself from then on, including when the machine boots with no window and no second choice. The default port is 8880 (Bitfocus Companion has 8000); if the port is taken anyway, the next free one is used and the window says so.

First-launch security prompt (the app isn't code-signed yet)

Because the downloads aren't signed with a paid developer certificate, the OS warns you the first time. One-time, then it opens normally:

  • macOS: double-click, let it get blocked, then System Settings → Privacy & Security → “Open Anyway”. (On older macOS: right-click → Open.) Or skip it entirely with the Terminal line above: a browser marks what it downloads for that check, and curl does not.
  • Windows: More info → Run anyway on the SmartScreen prompt for the installer, once. (Windows warns about any unsigned program the first time.)

The macOS build signs and notarizes itself as soon as the repository holds an Apple Developer ID (the build workflow says which secrets); from that release on the download opens with no warning. Windows signing is a separate paid certificate, not done.

Option B. Host it (a whole team; always on)

Run it on a Linux box, VM or Raspberry Pi on the same network as your ATEMs (the ATEM-Software-Control-on-a-server model). The published image is built for amd64 and arm64:

docker run -d --name webatem --network host --restart unless-stopped \
  -v webatem-data:/app/data ghcr.io/lucas-romanenko/webatem:latest

Open http://<that-box>:8880 from any device on the network. It comes back automatically on reboot. No .env file needed: every setting has a working default (SQLite DB + a generated secret key live in the webatem-data volume); see .env.example for knobs like PORT, TIME_ZONE, ALLOWED_HOSTS. (In Docker the address and port come from that environment.) Prefer Compose? The repository's compose.yml runs the same image: docker compose up -d.

Without Docker: the webatem-linux-x64 binary from the Releases page runs headless too: on a machine with no display it skips the browser and tray and prints the address to open from another device:

WebATEM is running.
  On this machine:      http://127.0.0.1:8880/atem/
  From another device:  http://192.168.1.50:8880/atem/

Keep it up with a systemd unit, tmux or nohup.

Why Linux for hosting? Discovery needs the app to see your LAN directly. That works natively on Linux (the container uses host networking). Docker Desktop on Mac/Windows runs containers in a VM that can’t see LAN discovery traffic, so on a Mac or PC use the download (Option A) or pipx (Option C), not Docker.

Option C. pipx (you already have Python 3.10+)

pipx install "webatem[desktop]"      # or: uvx --from "webatem[desktop]" webatem, with no install at all
webatem

Same behaviour as the download: a native process on your real network, the tray icon and the launcher window appear where there is a desktop. Prebuilt wheels for every platform mean no compiler is needed. Upgrade with pipx upgrade webatem.


Finding your switchers

Three ways, in order of magic:

  1. Automatic (Bonjour/mDNS). On the same network as your ATEMs, they show up under “On Your Network” with their names, no typing. This is what ATEM Software Control does.
  2. Scan subnet. For ATEMs that don’t advertise over Bonjour, type your subnet (e.g. 192.168.1) and hit Scan, which finds them by IP.
  3. Manual IP. Type an address and connect. Recent connections are remembered for one-click reconnect.

Automatic discovery is same-network only: Bonjour/mDNS is link-local and doesn’t cross a router or VPN (this is true of ASC too). Reaching your ATEMs over a VPN? Auto-discovery won’t see them, but Scan and manual IP work fine over the tunnel.


WebATEM vs. ATEM Software Control

ATEM Software Control (ASC) is Blackmagic’s own free control app, and it’s excellent, and WebATEM isn’t trying to replace all of it. The difference is access: ASC is a desktop app tied to one machine; WebATEM is a control surface any device on your network can open (and you can still run it locally like ASC if you want).

WebATEM ATEM Software Control
Runs on Any browser: Windows, macOS, Linux, ChromeOS, iPad, phone Windows & macOS desktop only
Client install None, just open a URL Installed per machine
Access from Any device on the network The machine it’s installed on
Multiple operators One shared switcher session, many operators Each machine opens its own session
Phones / tablets ✓ Responsive ✗
How to run Download it, host one instance for everyone (Docker), or pipx install "webatem[desktop]" Install on each machine
Price Free, open source Free (proprietary)
Feature breadth Core: switching, keyers, Fairlight audio, media pool, macros, profiles, HyperDeck Everything, incl. camera control (CCU), streaming & recording, SuperSource
Support Community / self-hosted Official Blackmagic

Reach for ASC when you need the full feature set on one operator’s machine camera control, streaming/recording, SuperSource, recording macros.

Reach for WebATEM when you want the core control surface available to anyone on the network, on any device or OS: a second operator on an iPad, a Linux box in the rack room, a phone at the camera position.

Running it for real

  • There is no login, by design. Like the hardware panel, anyone who can reach the page can control your switchers. Keep it on the studio network. If you must expose it further, put your reverse proxy’s auth (basic auth, SSO) and TLS in front, and set CSRF_TRUSTED_ORIGINS=https://your.host.
  • Web pages can’t drive it cross-origin. The control WebSocket only accepts browser connections from the app’s own pages (same-origin), so a malicious website open in an operator’s browser can’t reach the switchers. If your reverse proxy rewrites Host, list the browser-facing host in WEBSOCKET_ALLOWED_ORIGINS.
  • It talks raw UDP to switchers on port 9910 (the ATEM protocol has no authentication, which is the hardware and not this app), so it needs to be on a network that can reach them.
  • Single instance, by design. The switcher connection pool, media-pool watcher, and channel layer are process-local; one instance handles many switchers and operators. Don’t run several against the same switchers.
  • Stills upload/capture assume 1080p switchers (all production use so far). Non-1080p ATEMs connect and control fine; media features are untested there.

Architecture

Browser (Alpine.js + WebSockets)
   │  ws/atem/ · HTTP
   ▼
Django + Channels (single ASGI worker, uvicorn)
   │  pooled UDP session per switcher (atemwire)
   ▼
ATEM switchers (UDP 9910) · HyperDecks (TCP 9993 / FTP)
  • atemwire is the ATEM protocol library, a substantially modified fork of Martijn Braam's pyatem: declarative wire-format DSL, hardened UDP transport (in-order delivery, retransmit serving, clean session close), ref-counted connection pooling, native interleaved bulk transfers, macro bytecode transfer, and ASC-compatible profile save/restore. A small C extension does YCbCr↔RGB conversion. hyperdeckwire drives the HyperDecks. Both ship in the one bmdwire distribution, which pyproject.toml asks for as a range and requirements.txt pins; a library change is a release there and a pin bump here.
  • atem_control/ is the Django app: the WebSocket consumer, a declarative command dispatch table, the media-pool watcher, LAN discovery, and the UI.
  • webatem/ is the project package: settings, the ASGI entry, the WebSocket origin guard, and launcher.py, the webatem command that runs the same web app locally and lives in the tray with its launcher window. The downloads are that launcher frozen per OS by PyInstaller (build/desktop/); the PyPI package is the same code installed by pip.
  • One tag, three deliverables. A v* tag publishes the Mac / Windows / Linux downloads to the GitHub release, the Docker image to GHCR (amd64 + arm64) and the webatem package to PyPI.
  • SQLite for persistence (connection history). No external database.

Development

git clone https://github.com/lucas-romanenko/webatem.git
cd webatem
npm install && npm run build:css      # the stylesheet; without it the app renders unstyled
pip install -e ".[test]"              # Python 3.10+; the device libraries install as wheels
python -m webatem                     # the launcher: server + tray + window
python -m pytest tests/ -q            # the suite (no hardware needed)

Or entirely in Docker: docker compose up -d --build runs the app and docker compose exec webatem pytest tests/ -q runs the suite in it. The suite (the command dispatch table, the T-bar, the consumer's state-change and session-cleanup rules, upload policy, tally safety, profile dialogs, the media-pool thumbnail cache, LAN discovery identity, WebSocket origin checks) runs against fake protocol layers. CI runs it on every push. The protocol libraries carry their own suites in bmdwire.

python manage.py runserver works too for plain Django development. The per-OS downloads are built by the Desktop builds workflow (pyinstaller build/desktop/webatem.spec).

Updating and removing it

Updates are one click. The launcher looks for a new version when it starts and twice a day, and the launcher window and the tray menu then offer Update to x.y.z. That downloads it, checks it against the checksum GitHub publishes, installs it over the old one and starts it again on the same interface. Untick Check for updates automatically to stop the checks (Check now still works). Offline, it finds nothing and says so quietly. From a terminal: webatem --update. With pipx it's pipx upgrade webatem; with Docker, docker compose pull && docker compose up -d.

Updating keeps everything: your interface and port, the connection history, start at login. Installing a new download over the old one does the same; don't uninstall first.

Uninstalling removes everything WebATEM put on the machine: the program, your settings and connection history, start at login, and on a Mac the launcher window's own data.

  • Any desktop install: the launcher window's Uninstall… button, bottom right. It lists exactly what will go before anything does, then WebATEM closes and removes itself. On Windows it hands over to Windows' own uninstaller.
  • Windows: or Settings → Installed apps → WebATEM → Uninstall, even while WebATEM is running in the notification area.
  • macOS, app already in the Trash: the Terminal installer cleans up what the Trash left behind:
    curl -fsSL https://github.com/lucas-romanenko/webatem/releases/latest/download/install-mac.sh | sh -s -- --uninstall
    
  • From a terminal: webatem --uninstall (it asks first; --yes skips the question). That's the Linux binary itself, or on a Mac /Applications/WebATEM.app/Contents/MacOS/webatem --uninstall.
  • pipx: webatem --uninstall removes your data and start at login, then pipx uninstall webatem removes the program.
  • Docker: docker compose down -v. The -v removes the webatem-data volume, which holds the settings, history and key.

If you point DATA_DIR somewhere of your own, uninstalling removes only the files WebATEM writes there, never the rest of that folder.

License

  • Application code: MIT.
  • The ATEM protocol library, atemwire, is a separate package (a fork of the OpenAtem pyatem library) and is LGPL-3.0-only; WebATEM uses it as an installed dependency, unmodified. hyperdeckwire is MIT.
  • Bundled frontend assets (Alpine.js and its collapse plugin, Bootstrap Icons, Tailwind/daisyUI) are MIT and the Archivo and IBM Plex Mono fonts are OFL, with notices in THIRD_PARTY_LICENSES.md.

The downloads are a Combined Work under the LGPL. The Windows, macOS and Linux builds bundle atemwire and pystray, both LGPL-3.0, rather than installing them alongside. Their full licence texts travel inside the bundle, and because every line of WebATEM is public and MIT, you can install a modified atemwire and rebuild: pip install -e ".[desktop]" then pyinstaller build/desktop/webatem.spec. The macOS and Windows builds are one-directory, so the library also sits replaceable in _internal/; the Linux download is a single file, so rebuilding is the route there.

Security posture and how to report vulnerabilities: SECURITY.md.

Not affiliated with or endorsed by Blackmagic Design. ATEM and HyperDeck are trademarks of Blackmagic Design Pty Ltd. Use against production hardware at your own risk.


Independent project. Not affiliated with or endorsed by the switcher manufacturer. The red and green are broadcast convention, sampled to their own values; the brand lives in docs/brand/.

Hosting the control app inside another Django project

atem_control is a normal Django app (label webatem_atem), and everything it needs from its surroundings goes through one class, atem_control.hooks.Hooks: who may open the page and drive the socket, where operator actions and connect/disconnect events are recorded, what a switcher is called, the switcher list for the suggestions, HyperDeck names, sightings (video mode, deck model, binding differences), where a dropped still goes, and extra template context. Every method has a working default, because the standalone app is the defaults, so a host subclasses it and overrides what it has:

# settings.py
INSTALLED_APPS += ['atem_control']
WEBATEM_HOOKS = 'myplatform.webatem_hooks.PlatformHooks'
# myplatform/webatem_hooks.py
from atem_control.hooks import Hooks

class PlatformHooks(Hooks):
    def check_access(self, request):        # None = allowed, else the response
        ...
    def can_use(self, user):                # the WebSocket gate
        ...
    def record_activity(self, **event):     # the audit log
        ...
    def name_for_ip(self, ip):              # the inventory's name
        ...

Templates are overridden the Django way (a base.html, a control/_header.html in the host's own template directory), and control.html carries a control_extra block for the host's panels and scripts. Discovery is off with discovery_enabled returning False.

Metadata

Release files for webatem 1.7.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for webatem 1.7.1
File Size Uploaded
webatem-1.7.1.tar.gz 1.1 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for webatem 1.7.1
File Interpreter ABI Platform
webatem-1.7.1-py3-none-any.whl Python 3 none any Details

Total release size: 2.3 MB

Release files / webatem-1.7.1.tar.gz

Download URL webatem-1.7.1.tar.gz
Size 1.1 MB
Tags Source
SHA-256 checksum
How to use checksums
0dd54ef7a39888fe46d0385a96bd5be1a25240829b3fb87517eb29b45d2db532
BLAKE2b-256 checksum
How to use checksums
ec5c5310361fe751aa5b9736d18480775cafde035ebe9ee687297285e6e11205
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release files / webatem-1.7.1-py3-none-any.whl

Download URL webatem-1.7.1-py3-none-any.whl
Size 1.2 MB
Tags Python 3
SHA-256 checksum
How to use checksums
e9826e0874313c3348a55c113c85cc32b1c4c4e185e67983a891ee051c2c93fe
BLAKE2b-256 checksum
How to use checksums
ad6cfd8519e4ca69c707bcb2d2475027bf0413596cadcbaaed6098bd5d29e735
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.

Transparency log

Release history Release notifications | RSS feed

2.1.4

2 release files

2.1.3

2 release files

2.1.2

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.4

2 release files

2.0.3

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.7.8

2 release files

1.7.7

2 release files

1.7.6

2 release files

1.7.5

2 release files

1.7.4

2 release files

1.7.3

2 release files

1.7.2

2 release files

This release

1.7.1 This release

2 release files

1.7.0

2 release files

1.5.0

2 release files

1.4.4

2 release files

1.4.3

2 release files

1.4.2

2 release files

1.4.0

2 release files

1.3.3

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page