Controlling the frame
Every frame serves its own web app and HTTP API on port 8787. Whether it's standalone, self-hosted or cloud-managed - control it from anything that speaks HTTP.
Every frame runs its own web server on port 8787. This is the heart of FrameOS's design:
viewing, controlling, and even administering a frame works directly against the device, from
any browser or script on your network. The backend can be off. The internet can be down. It is
also how a standalone frame is managed at all, and the escape hatch on a
cloud-managed one.
On a microcontroller
ESP32 frames serve the same kind of API on port 80 - the image, the scene
state, POST /event/<name>, logs, metrics and assets - but not the browser UI below. Manage
those from the backend. Pico and virtual frames have no
on-device server at all.
The frame's web app
Open http://<frame-ip>:8787/ for the frame's own web UI:
/- view the current image, live (updates over a WebSocket)./c- the control page: switch scenes and edit the active scene's state fields (the yellow nodes you added in the scene editor)./admin- the on-device admin panel: settings, scenes, schedule, logs, metrics, assets and updates. A full management UI served by the frame itself.
By default the frame also renders its control URL as a QR code on the display, so anyone standing in front of the frame can scan and control it.

The admin panel
http://<frame-ip>:8787/admin is the frame's own management UI. It is the same interface the
backend shows for a frame, just served from the device: sign in with the admin
password you set during setup (or in the backend under Settings → Admin) and you get:

- Scenes - what is installed and what is active. Install scenes from the sample and gallery repositories or the scene store, upload a scene ZIP, or open the node-graph scene editor right here. Interpreted scenes save and go live without a compile step.
- Settings - everything from Useful settings: name, display driver and rotation, time zone, access mode and the QR code, the color palette, GPIO buttons, the HTTP API key, HTTPS, and which backend or cloud manages the frame. This is also where you connect a standalone frame to a backend with an adoption code, link it to FrameOS Cloud, or disconnect it again.
- Preview - the current image, live over a WebSocket.
- Schedule - render intervals and cron-like schedules, plus the maintenance restart and reboot timers.
- Logs and Metrics - runtime output and health charts (CPU, memory, temperature, render times), straight from the device. Logs only leave the frame if you switch on Send logs to backend.
- Assets - files on the frame's SD card: fonts, images, downloaded scenes.
- Debug - on-device diagnostics. (Ping and Terminal exist only in the backend.)
- Settings → FrameOS upgrade - the frame downloads a new release itself, verifies the signature, and swaps it in on the next reboot. Cloud and backend only nudge it; the binary never passes through them.
The admin panel is how a standalone frame is managed at all, and it stays available on frames that a backend or the cloud manages - which is why the frame can always be moved between the three without a reflash. ESP32 frames do not serve it: they answer the HTTP API and a small setup page, and are managed from the backend or cloud.
Access modes
Under the frame's settings you choose who can do what without a key:
- Private (default) - a key is required to view and to control.
- Protected - anyone on the network can view the image; the key is required to change anything.
- Public - no key needed for anything. Makes for the smallest QR codes.
The access key travels as a ?k=... query parameter (or a cookie after the first visit).
Traffic is plain HTTP on your local network. If you need real security - frames on a shared network, remote access - put the frames on a Tailscale tailnet or enable the HTTPS options in the frame's settings.
The HTTP API
Everything the control page does is a plain HTTP call. With KEY being your access key:
# the current image as PNG
curl "http://frame:8787/image?k=KEY" -o now.png
# the active scene and its state
curl "http://frame:8787/state?k=KEY"
# all scenes and their states
curl "http://frame:8787/states?k=KEY"
# change state fields on the active scene and re-render
curl -X POST "http://frame:8787/event/setSceneState?k=KEY" \
-H "Content-Type: application/json" \
-d '{"render": true, "state": {"message": "Dinner at 7!"}}'
# switch to another scene
curl -X POST "http://frame:8787/event/setCurrentScene?k=KEY" \
-H "Content-Type: application/json" \
-d '{"sceneId": "<scene-uuid>"}'
# trigger a render right now
curl -X POST "http://frame:8787/event/render?k=KEY" -d '{}'POST /event/<name> dispatches any event into the running scene graph - the same mechanism
buttons, schedules, and apps use internally. This makes integrations trivial: a Home Assistant
automation, a cron job, a shell script, or a doorbell webhook can all drive your frame.
There's also a full admin REST API (config, logs, metrics, restart, reboot) under
http://frame:8787/api/ behind the admin login, and a WebSocket at /ws that streams renders.
Scheduling
Frames have their own on-device scheduler: render intervals per scene, plus cron-like schedules
(minute/hour/weekday) that can switch scenes or fire events - for example, a calendar during the
day and art in the evening. Configure it under the frame's Schedule tab. The schedule runs on
the frame, in the frame's own time zone, so it keeps working with the
backend off. When in doubt about what the frame will do next, check Logs: on start and after
every schedule change it logs scheduler:loaded with its zone, local time and the next due entry
("Mon 01:02 reboot"), and every firing logs scheduler:fire.
The same panel has a Maintenance card that schedules a restart of the FrameOS
process or a full reboot of the device - the successor to the nightly-reboot setting, on both
Raspberry Pi and ESP32 frames.