Camera resource (CPU state you can read
and drive headless), while the rendered output — and the ScreenshotChannel that captures it — is the
GPU edge. The headless host boots a Camera resource but no renderer and no ScreenshotChannel, so pose
endpoints work and /screenshot is host-only. This page drives every camera and screenshot endpoint against
a real local server and shows the exact response. Every command is real; the output below is
captured from a headless host.
Point the camera, frame an entity, capture a frame
1. Read the camera.GET /camera returns the camera’s eye, target, fov_y, and projection. The
headless host carries a Camera resource (here positioned by the served project), so you get the pose back:
/screenshot below.
2. Set the camera. POST /camera takes optional eye and/or target as [x, y, z], and pins a
CameraOverride so an editor’s mouse-orbit won’t snap your shot back:
GET /camera and eye/target reflect your write. On a render
host the rendered view moves with it; headless the pose changes but nothing is drawn.
3. Apply a view preset. POST /camera/view snaps to a named preset — one of top, front, back,
right, left, perspective:
isometric) returns
{"ok":false,"message":"Unknown view: isometric. Use: top, front, back, right, left, perspective"}.
4. Frame an entity. POST /camera/focus targets an entity and pulls the camera to a clamped 5–20 unit
distance along the current view direction. Spawn something, then focus it:
entity_id returns "Missing entity_id"; a nonexistent id returns "Entity 999 not found".)
5. Capture a frame. POST /screenshot re-renders the viewport offscreen, reads it back, and writes a
PNG. It needs a ScreenshotChannel resource wired to a live renderer — the one piece the headless host
doesn’t have:
ScreenshotChannel, so there is nothing to
capture. On a render host the same call returns 200 with
{"ok":true,"path":"/tmp/euca_screenshot_<ts>.png","size_bytes":<n>}. This is the one endpoint that is
honestly host-only: do not expect a PNG from the headless host.
Behavior and gotchas
The things you only find out by running it:- The camera pose works headless; only rendering doesn’t.
GET /camerareturns the pose, and every camera write (/camera,/camera/view,/camera/focus) drives that pose resource. What’s absent headless is the GPU edge — nothing is drawn from the pose, and/screenshotcan’t capture it. POST /camerareportsok:trueand actually moves the pose. Confirm withGET /camera; the neweye/targetare reflected. On a render host the rendered view follows; headless it’s pose-only.- View presets and
/camera/focussucceed headless. They reframe the pose around the named preset or the target entity’s transform; an unknown preset name or a missing entity still returns the matchingok:false. /screenshotis 503 headless, not a problem with your request. It needs a live renderer. Capture from the editor or a windowed build.- Setting the camera also pins
CameraOverrideso an interactive editor’s mouse-orbit won’t fight your framing. This matters only on a render host where an orbit camera is running.
Endpoints
Material, post-process, and fog endpoints live on Materials & post-processing;
they share the same render-host-vs-headless split. Keeping appearance out of the authoritative world state is
deliberate — see Determinism.
Status
- Shipped — camera get/set, view presets, entity framing, and offscreen screenshot capture, all backed by the real PBR forward renderer.
- Host-only output — every rendered result on this page requires a GPU render host (the editor or a
windowed build). The headless host carries the
Camerapose resource (so camera reads/writes work) but no renderer and noScreenshotChannel, so nothing is drawn and/screenshotreturns 503. This is by design: the headless host runs the simulation and holds the pose, not the renderer.
Camera & render endpoints
Every camera and capture endpoint, with request and response schemas.