How to watch, record and pull the thermal camera's data at Studio 45, from anywhere. Every recipe below was run on Sat 17 Oct 2026 (week 42), 04:1x–04:3x PDT, from a Mac outside the lab, while the phone streamed on the lab's Ethernet with app 1.5.4.
The short version: anyone with the link gets the live temperatures at once, and the video after a quick "are you a person" check. For a temperature record, press Record on the page, from anywhere. For full 256×192 frames at 25 a second, use the phone's own API from inside the lab network.
What the camera sends
A Thermal Master P3 (256×192 pixels, 25 frames a second) is clamped behind an ET-SoC-1 card in the AI Foundry lab. A phone drives it and publishes three things:
| Stream | What it is | Radiometric? | Rate |
|---|---|---|---|
| Video on the page | colour picture, 768×576 | No: auto-gained 8-bit, for looking | 25 fps |
| Telemetry on the page | min, max, mean, centre °C, the camera's own temperature, and an 80×60 grid of the scene | Yes, see the limits below | about 8 a second |
| Raw frames on the lab network | every pixel, 1/64 K counts | Yes: the full data | 25 a second |
Pick your path
| You want | Use | You need | Resolution |
|---|---|---|---|
| To watch it | the live page | a browser; the video starts after a person check, or a sign-in | the picture plus live °C readouts |
| A temperature record, from anywhere | the page's ● Record button | the page open while you record | every reading's summary; the 80×60 °C grid once a second |
| Full frames | the phone's HTTP API, http://p3-phone:8080 |
a login on aifoundry1–3, or a laptop on the Studio45 Wi-Fi | 256×192, 1/64 K, 25 a second |
1. Watch
Open spacesheep.dev/@yaroslavvb/thermal-master-p3-live-camera-stream. The temperatures and the chart are live at once, with no account. The video starts after spacesheep's quick "Verify you are human" check on the picture, or as soon as you sign in. Hover over or tap the image to read a spot's temperature.
The chip on the video says how fresh the picture is, e.g. "Delay 0.46 s · 25 fps": green under a second, amber up to five, red beyond that. A red banner covers the picture if the video stops moving for more than two seconds, so you never mistake a frozen frame for a live one. The buttons that change the camera work only for the page's editors.
2. Record temperatures from anywhere
In the Take data off the stream box, press ● Record, leave the tab open, then use the download buttons:
- Download CSV: one row per reading, about 8 a second. Each row has the time, sequence number, min / max / mean / centre / standard deviation °C, and the camera's own temperature.
- Download grids (JSONL): the 80×60 grid in °C, once a second.
It keeps up to 30 minutes, runs entirely in your browser, needs no key and uploads nothing. It records only while the tab is open.
3. Full frames from the lab network
The phone answers on the lab's private Ethernet as p3-phone (10.45.0.55, a fixed lease). From any of aifoundry1–3 that name already works. On the Studio45 Wi-Fi the phone is at 192.168.0.221 (a DHCP lease, so it can change). The read endpoints need no password inside the lab.
One frame in °C (192 lines of 256 comma-separated values, emissivity 0.96):
curl -s http://p3-phone:8080/frame.csv -o frame.csv
Is it alive?
curl -s http://p3-phone:8080/health
The live lossless stream (/stream.raw.z: about 7.7 Mbit/s, every frame), in plain Python:
import array, struct, urllib.request, zlib
r = urllib.request.urlopen("http://p3-phone:8080/stream.raw.z")
def read(n):
b = b""
while len(b) < n:
b += r.read(n - len(b))
return b
HDR = "<4sIqqqqqihhfBBH" # 64 bytes: magic "P3FZ", version, frame seq, mono ns, wall ns,
# cnt1, cnt2, cnt3, width, height, camera temp °C, gain, auto-FFC, spare
while True:
(n,) = struct.unpack("<I", read(4)) # each frame: a 4-byte length, then zlib data
raw = zlib.decompress(read(n)) # 99,392 bytes
h = struct.unpack(HDR, raw[:64])
seq, wall_ns, w, ht = h[2], h[4], h[8], h[9]
counts = array.array("H", raw[1088:1088 + w * ht * 2]) # uint16, row-major, 1/64 K
temps_c = [c / 64 - 273.15 for c in counts] # 256 x 192 values in °C
print(seq, wall_ns, min(temps_c), max(temps_c))
wall_ns is the phone's clock when the frame was captured, so time.time_ns() - wall_ns is how late the frame reached you.
Please:
- one streaming client at a time, and close it when you're done: each open stream holds a thread on the phone;
- use
/stream.raw.z, not/stream.raw(39.5 Mbit/s for the same data); - leave
/api/controland/api/probealone: they change the camera and are password-protected anyway.
What the numbers mean
- °C = raw / 64 − 273.15. The container step is 15.6 mK, but the sensor's real step is 62.5 mK.
- The 80×60 grid is rescaled to its own frame. Each value is a byte from 0 to 255 between that frame's
min_rawandmax_raw, so across a 6 °C scene one step is about 24 mK. Decode it asraw = min_raw + q/255 × (max_raw − min_raw), then the formula above. - Absolute accuracy is about ±1.5–2 °C; changes over time are far better. The camera sits at about 45 °C, and its own temperature moves every pixel.
- The video carries no temperatures. Use the telemetry or the raw frames for numbers.
Measured on Sat 17 Oct 2026
| Test, from outside the lab | Result |
|---|---|
| The page, signed out, in a fresh browser | temperatures live at once; the chip read "Delay 0.42–0.60 s · data only"; the video waits for the person check |
| Phone to screen, telemetry | 0.38–0.42 s |
| Record, then Download CSV | a real download, with grids decoded to each frame's own min and max (in a test browser at phone width) |
| Telemetry through spacesheep, 15 s | 115 values with none missing, 7.9 a second, each about 13 KB |
| The 80×60 grid, decoded | 38.98–45.16 °C, matching the reported min 38.975 and max 45.163 |
/frame.csv |
256×192 °C in 1.6 s |
/stream.raw.z, 4 s |
97 frames with none missing, 24 fps, 7.7 Mbit/s |
| Capture to arrival, raw frames | about 0.33 s, of which about 0.04 s is the network (72 ms round trip) |
p3-phone from inside the lab |
not run today: there was no lab login from outside. The same endpoints answered from outside the lab, through the same code path |