← all articles

A phone as a console: why it helps and why the server lives inside the app

Checking light from the house without running back to the laptop, why SSE was enough instead of websockets, and what happens if you skip the PIN on an open venue Wi-Fi.

The lighting operator's main problem fits in one sentence: they are not sitting where the audience looks from.

At the desk you have your own angle, your own eye level, your own reflections off the monitor. Backlight that looks fine from the desk hits the audience in row six straight in the eye. A wash that looks even on the visualiser gathers into patches on stage. To see that, you have to get up and walk away — and at that moment there is nobody at the desk to press GO.

Hence the web remote: a phone on the same network opens a page and does the things you would otherwise walk back to the laptop for.

The scenarios people actually set this up for

Checking from the house. You walk the rows, running cues as you go, and watch how it looks from the seats. Without this, setting angles and levels becomes a relay race: five steps there, five back, forty times over.

Focusing from the stage. You stand under the fixture, select it on the phone, push the intensity — and see the beam where you are standing rather than through a camera feed.

A second person at the run. An assistant runs GO from the script while you fix levels. Or the other way round.

Get-in. Blackout from the phone when the stage must go dark right now and you are up a ladder.

The console stays with the operator. The phone is not a replacement, it's a second hand.

Inside: why SSE and not websockets

An engineering decision worth explaining, because it is counterintuitive.

Data flows both ways here: down to the phone goes state (active cue, engaged presets, master position), up go commands. Reflex says: bidirectional exchange, take a WebSocket.

We took Server-Sent Events downward and plain POST upward, and here is why.

Upward go finger presses — a handful per second at their most energetic. For that traffic a POST is no worse than a websocket frame, and it can be debugged with curl.

Downward flows state, and it flows downward only. SSE is literally an HTTP response that never ends: the server holds the connection and appends lines to it. No framing, no handshake, no ping-pong to work out whether the client is still alive.

The price difference: a WebSocket in Node means either an external dependency or your own implementation of RFC 6455 with framing and masking. SSE is res.write() in a loop. For the feature "show the phone which cue is active", the second one is more honest.

A side benefit: SSE reconnects by itself. The phone slept, the Wi-Fi blinked — the browser restores the stream without a single line of our code. With a websocket we would have written that by hand, and written it with bugs.

State is sent with 120 ms throttling and de-duplication: while the operator drags a fader, snapshots don't fly out on every frame.

Commands take the same road as MIDI and OSC

This is a matter of principle. The phone does not get its own path into the application's stores: its command goes through the same dispatcher as a pad press on a MIDI controller or an OSC message from a tablet.

The motive is pragmatic: three parallel routes to one action means three sets of bugs and three places where somebody forgot to update the logic. One route, one set.

The HTTP server itself lives in the application's main process, next to DMX and OSC. The interface window opens no ports at all.

On security: the PIN is not decorative

The server listens on every network interface — otherwise the phone couldn't reach it. Which means the whole venue network can see it.

Picture what that means at a concert: a guest Wi-Fi with three hundred people on it, and an open page with a blackout button on it. No malice is required — curiosity and an accidentally guessed address like 192.168.1.42:7331 will do.

Therefore:

  • Set a PIN. An empty PIN means "let everyone on this network in", and the settings say so outright. With a PIN the phone gets a session cookie (HttpOnly, 12 hours), so you don't retype it constantly.
  • Brute force is cut off: more than ten attempts from one address per minute and it stops answering. A four-digit PIN takes ten thousand attempts, i.e. seventeen hours at that rate rather than a minute.
  • Changing the PIN logs everyone out. The assistant left with their own phone — change the PIN and their session dies with the rest.

The ideal option is of course a separate show network that outsiders can't physically reach. But since we know that in reality there will be one switch for everything, the PIN is on by default for a reason.

Connecting, and the small thing that ruins it

The settings show the address and a QR code — you point the phone's camera instead of typing an IP with your fingers in a half-dark hall. Saves about thirty seconds and one typo.

If the page won't open, it is almost always client isolation: guest Wi-Fi is frequently configured so devices can't see each other. Checked instantly — the phone and the laptop should ping each other.

And the most annoying thing, worth doing in advance: turn off the screen lock. The phone goes dark exactly when GO needs pressing, and you unlock it one-handed while standing on a ladder.

Read next