Photography — Interval Capture and StageView Console
Photography is the third generative output of the system. Unlike music (continuous) and visuals (continuous), photography is discrete: the system captures moments.
Full Public Reader
Photography — Interval Capture and StageView Console
The Photography System
Photography is the third generative output of the system. Unlike music (continuous)
and visuals (continuous), photography is discrete: the system captures moments.
The design goal is to make the photographer invisible. The dancer moves; stills
appear without anyone pressing a button. The iPhones are the cameras; the iPad
is the control console.
Camera Nodes
Each iPhone in the system is a self-contained camera node. It:
- Serves a live MJPEG stream at `:8081/stream` (30fps)
- Handles `POST /capture` to take a still photo (AVCapturePhotoOutput)
- Runs its own interval timer (2/3/5/8 second automatic capture)
- Advertises itself on Bonjour as `_mmcam._tcp`
- Sends SSE events (`GET /events`) for `still_captured`, `session_changed`, `status`
The iPhone can operate as a camera node while simultaneously running the full
SAN/audio pipeline (primary performance device) or in camera-only mode (no SAN,
no audio — just the camera service).
StageView: The Operator Console
StageView is an iPad app that discovers all camera nodes and gives the operator
a unified view of the entire camera rig.
Discovery:
- Bonjour: `NetServiceBrowser` listens for `_mmcam._tcp` announcements
- mDNS hostname prober: `getaddrinfo` probes `iPhone-*.local.` hostnames
- /24 subnet scanner: parallel HTTP probes of all IPs in the iPad's subnet
- Extra subnet scan: always scans `10.0.0.x`, `10.0.1.x`, `192.168.0.x`,
`192.168.2.x` to catch devices on different WiFi networks
- Manual IP entry: + button allows adding Tailscale or non-standard IPs
Live feeds:
Each discovered node streams its MJPEG feed to StageView. The UIView-based
`MJPEGFeedView` renders frames via `CATransaction.setDisableActions(true)` on
`layer.contents` — required to bypass iOS 26 beta's implicit animation regression.
Controls (per-camera or "all" fan-out):
- Capture: fire shutter on one or all cameras
- Interval: start/stop auto-shoot timer with selectable period
- Lens: switch between wide/main/tele (device-dependent)
- Zoom: set magnification factor
- Torch: flash mode
- Session: assign a session name/ID for organizing stills
The Contact Sheet
As stills are captured, each iPhone sends a `still_captured` SSE event with a
thumbnail (base64-encoded JPEG thumbnail, ~5KB). StageView's SSE client receives
these events and appends the thumbnails to a horizontal contact sheet strip at
the bottom of the operator view.
This gives the operator a live curation view — they can see what's being captured
in real time without picking up any phone.
Interval Timer Logic
The interval timer (`IntervalShootController.swift` on iPhone) fires
`PhotoCaptureService.capture()` at the configured interval. The default periods
match the standard photography/filmmaking intervals: 2s (action), 3s (standard),
5s (considered), 8s (slow/portrait).
All iPhones in a session run their own independent timers started simultaneously
from StageView. The timers are not synchronized by a clock signal — they are
started at approximately the same time by fan-out HTTP calls. For posed stills,
this ~100ms jitter between phones is acceptable.
(Frame-perfect synchronization would require a scheduled-timestamp capture API,
which is a noted future option if needed for action sequences.)
ShootView: Curation
ShootView (the second iPad, or same iPad in a different app) aggregates all stills
from all camera nodes and provides:
Gallery: All stills from all phones in one scrollable grid, sorted by timestamp.
Star-rating for selection. Filter by session, camera, time range.
Filter: Per-image CoreImage filters (crop, exposure, saturation, contrast,
grain). All on-device, no server required.
Reel: `ReelComposer` assembles stills into a video: `AVMutableComposition` +
`AVMutableVideoComposition`, configurable seconds-per-still (1.5–4.0),
transition styles (cut, dissolve, zoom), duration targets (15/30/45/60s).
Exports 1080×1920 H.264 to the Photos library.
Lookbook: `LookbookRenderer` (1188 lines) builds a PDF from a curated selection
of stills. Layout, typography, page count — configurable. Shares via AirDrop or
saves to Files.
The Laptop-Free Vision
The entire photography system — discovery, live feed monitoring, capture control,
curation, reel building, lookbook rendering — runs on iOS devices only. No laptop,
no Mac, no multicam-server required.
The multicam-server (the Rust server at `:9404`) was the predecessor: it ran on
Mac1 and coordinated all devices centrally. The current architecture distributes
that function: each iPhone owns its own state, StageView orchestrates them without
a server in the loop.
This makes outdoor shoots, location shoots, and portable setups feasible. Two iPhones
and two iPads on a shared WiFi (or a phone hotspot) are the entire rig.
Hardware in the Photography Rig
| Device | Role |
|---|---|
| iPhone 16 Plus (`880B4058`) | Primary camera node + SAN performer device |
| iPhone 16 Pro Max (`84109044`) | Secondary camera node |
| iPhone 14 Pro Max (`45896348`) | Third camera node (camera-only mode) |
| iPad A16 (`1938B9B3`) | StageView operator console |
| iPad A16 (`1DE6FABC`) | ShootView curation / second StageView |
Promotion Decision
Attach run IDs, datasets, metrics, and reproduction commands.
Source Anchor
computational-choreography/04-generative-output/photography.md
Detected Structure
Method · Evaluation · Figures · Code Anchors · Architecture