STAGEYE / HOST — Host Setup & Configuration

Capture setup for the front-of-house computer

Open viewer

Host disconnected

Frame rate
~0.0 fps
Last frame
--:--:--

Connection details

Project URL

https://fxomeytrkhrzkpjkpfjt.supabase.co

Anon key

••••••••••••••••••••••••••••••••••••••••••••••••

Install dependencies

pip install mss pillow supabase pyautogui

Running the host script

  1. Install Python 3.10 or newer on the FOH computer.
  2. Install the dependencies with the command above.
  3. Paste the script below, filling in the project URL and anon key above.
  4. Run it: python stageye_host.py
  5. It uploads latest.jpg to the screen-frames bucket, broadcasts { timestamp } on frame-updates, and applies incoming events from control-events.
  6. Autostart on Windows: press Win + R, run shell:startup, and drop a shortcut to stageye_host.py (or a .bat that calls it) in that folder so the host script starts automatically on boot.
# stageye_host.py - run on the FOH computer
# pip install mss pillow supabase pyautogui
import io, time, asyncio
from datetime import datetime, timezone
from mss import mss
from PIL import Image
from supabase import create_client
import pyautogui

pyautogui.FAILSAFE = False

SUPABASE_URL = "<project url above>"
SUPABASE_KEY = "<anon key above>"
sb = create_client(SUPABASE_URL, SUPABASE_KEY)
BUTTONS = {0: "left", 1: "middle", 2: "right"}

async def main():
    frames = sb.realtime.channel("frame-updates")
    await frames.subscribe()

    control = sb.realtime.channel("control-events")

    def on_control(payload):
        e = payload.get("payload", payload)
        w, h = screen_size
        if e["type"] in ("mousemove", "mouseclick"):
            pyautogui.moveTo(int(e["x"] * w), int(e["y"] * h))
        if e["type"] == "mouseclick":
            pyautogui.click(button=BUTTONS.get(e.get("button", 0), "left"))
        elif e["type"] == "keydown":
            key = e["key"]
            pyautogui.press({"Enter": "enter", "Escape": "esc", " ": "space"}.get(key, key.lower()))


    control.on_broadcast("control", on_control)
    await control.subscribe()

    with mss() as sct:
        mon = sct.monitors[1]
        globals()["screen_size"] = (mon["width"], mon["height"])
        last_beat = 0.0
        try:
            while True:
                shot = sct.grab(mon)
                img = Image.frombytes("RGB", shot.size, shot.bgra, "raw", "BGRX")
                buf = io.BytesIO()
                img.save(buf, format="JPEG", quality=55)
                sb.storage.from_("screen-frames").upload(
                    "latest.jpg", buf.getvalue(),
                    {"content-type": "image/jpeg", "upsert": "true", "cache-control": "0"},
                )
                await frames.send_broadcast("frame", {"timestamp": int(time.time() * 1000)})

                # Heartbeat every 5 seconds so viewers see "Host connected"
                if time.time() - last_beat >= 5:
                    last_beat = time.time()
                    sb.table("host_status").update({
                        "is_connected": True,
                        "last_seen_at": datetime.now(timezone.utc).isoformat(),
                    }).eq("id", 1).execute()

                await asyncio.sleep(1)
        finally:
            sb.table("host_status").update({"is_connected": False}).eq("id", 1).execute()

asyncio.run(main())