Camera Streaming¶
Monitor your prints visually with live camera streaming directly from your Bambu Lab printer.
Live Streaming¶
BamDude provides MJPEG video streaming from your printer's built-in camera, or from an external network camera.
Opening the Camera¶
- Click the camera icon on any printer card
- Choose between embedded overlay or separate window (configurable in Settings)
- Stream starts automatically
Stream Controls¶
| Button | Action |
|---|---|
| Live | Real-time MJPEG video stream |
| Snapshot | Single still image (lower bandwidth) |
| Restart the stream | |
| Enter fullscreen mode |
Camera Wall¶
The Printers page has two layouts, switched with the Cards / Cam wall toggle in the page header. Cam wall replaces the printer cards with a responsive grid of camera tiles — one glance at every camera in the farm.
To conserve bandwidth and ffmpeg processes, the wall streams intelligently rather than opening every camera at once:
- Only on-screen tiles go live. An
IntersectionObservermarks a tile "visible" once ≥40% of it is on screen — the 40% floor stops a scroll-boundary sliver from spinning up a stream. - Live is capped by the browser transport. The saved max live preference defaults to 4. HTTP/1.x or an unknown protocol permits at most 2 live streams per tab, shared with the floating camera. Confirmed HTTP/2 or HTTP/3 permits the chosen maximum (up to 16). Extra visible tiles show snapshots; off-screen tiles pause. The UI explains a lower effective limit without changing your saved preference.
- Snapshots for the rest. Over-cap tiles refresh a still frame every 8 seconds by default (the snapshot interval setting).
- Off-screen tiles pause. Scroll a tile out of view and it stops all network activity until it returns. Disconnected printers also render paused — no live slot is burned on a camera that has nothing to stream.
The protocol comes from completed browser API requests, including the browser-facing hop through a reverse proxy. HTTPS alone does not prove HTTP/2. Missing browser timing data keeps the two-stream cap. Separate tabs/windows do not share this frontend budget, so it is not a browser-wide connection guarantee.
Snapshots share a two-request queue, cancel on exit and keep the last decoded image while a replacement arrives. The bottom-right time is when this tab last successfully updated the snapshot, not the printer's capture time. Failed refreshes retain the previous frame and retry. Live streams keep the LIVE badge.
Per-tile¶
Each tile shows:
- an offline chip when the printer isn't connected;
- an optional status overlay — off, a compact state chip, or full with progress %, layer count, and time remaining on printing/paused tiles;
- an HMS-error badge when the printer has active (non-noise) HMS errors;
- click opens that camera in your preferred viewer — embedded overlay or separate window, per your Camera settings. In the signed-in wall's full overlay, the job label falls back from the printer's subtask to its current print title and then its uploaded filename, so a firmware that omits one field does not leave a running tile anonymous.
Wall settings¶
A gear button on the wall opens per-browser settings: max live (1–16), snapshot interval (2–60 s), and status overlay mode (off / compact / full). All three persist in the browser's local storage — they're per-device, not synced to the account, since a Pi 4 install caps the live count lower than a NUC.
Permission
The Cam wall toggle needs the camera:view permission — the same one the camera viewer uses. Without it the toggle is disabled.
Shared streams, no more frozen tiles
Every viewer of a printer — a cam-wall tile, the embedded overlay, a popup window — subscribes to one shared fan-out stream. Closing one viewer no longer freezes another: the stream only tears down when the last viewer disconnects.
External Cameras¶
Connect external network cameras to replace the built-in printer camera. Useful for better angles, higher resolution, or printers in enclosures where the built-in camera is partially blocked.
Supported types¶
| Type | Example URL/path |
|---|---|
| MJPEG | http://192.168.1.50/mjpeg |
| RTSP | rtsp://192.168.1.50:554/stream |
| Snapshot | http://192.168.1.50/snapshot.jpg |
| USB (V4L2) | /dev/video0 |
Configuration¶
- Settings → General → Camera.
- Find your printer in the External Cameras section.
- Toggle the switch to enable.
- Enter the camera URL.
- Select the camera Type.
- Click Test — BamDude opens the stream once, confirms a frame, then disconnects.
RTSP authentication
Embed credentials in the URL: rtsp://user:[email protected]:554/stream.
go2rtc and IP cameras: warm-up-frame skip + Snapshot URL override
Many MJPEG sources — go2rtc most notably, plus several IP cameras — emit a "warm-up" / often-black frame on the byte that follows connection accept (the encoder's last keyframe before it catches up to live content). Since 0.4.4 BamDude reads past the first frame and returns the second on every single-frame capture path (notification thumbnails, finish photo, layer-timelapse, plate detection, Obico inference). Slow / single-frame streams that don't deliver a second frame within the timeout fall back to the first so callers always get something. No configuration needed.
Optional: Snapshot URL override. For MJPEG, RTSP, and USB types you can also fill in a separate Snapshot URL below the live-stream URL. When set, BamDude fetches single-frame captures (notification thumbnails, finish photos, layer timelapse, plate detection, Obico inference) from this URL via plain HTTP GET — bypassing the warm-up dance entirely. Useful for go2rtc setups (http://<host>:1984/api/frame.jpeg?src=<name> is a dedicated single-frame endpoint that never returns the encoder's stale keyframe) or IP cams with a /snapshot.jpg-style endpoint. Click Test next to the Snapshot URL to verify it returns a valid frame. The live-view stream always uses the main URL; the override only changes single-frame captures, since polling a snapshot endpoint at 1 fps for live view would be a regression for everyone who doesn't have this problem. Hidden when camera type is Snapshot — the live URL is already a single-frame endpoint, so an override would be redundant. Leave blank to use the warm-up-frame skip on the live stream.
USB / V4L2 setup¶
USB webcams work via the V4L2 path on Linux hosts:
# Install device-listing tools
sudo apt install v4l-utils
# Enumerate available video devices
v4l2-ctl --list-devices
BamDude reads /dev/video0 by default. If your camera is at a different node (e.g. /dev/video2), enter the path directly into the External Camera URL field.
For Docker, pass the device through:
Layer-Based Timelapse (external cameras only)¶
When an external camera is enabled and the printer publishes per-layer-change MQTT events, BamDude automatically:
- Captures a frame each time the print's layer counter advances.
- Stores frames in a temporary directory during printing.
- Stitches a video with ffmpeg when the print completes.
- Attaches the resulting timelapse to the print archive.
External cameras only
Layer-based timelapse only works with external cameras (MJPEG, RTSP, Snapshot, or USB). Built-in printer cameras use the printer's own timelapse feature instead — the printer does the stitching itself and BamDude attaches the resulting MP4/AVI.
ffmpeg required
Layer timelapse requires ffmpeg to be installed (included in the BamDude Docker image, install via apt install ffmpeg on bare metal).
This produces dramatically higher-quality timelapses than fixed-interval capture because each frame corresponds to a clean state of the print (head parked off the part, between layers).
Camera rotation¶
A per-printer Rotation setting (0 / 90 / 180 / 270, on the printer's camera settings) for a camera that is physically mounted turned.
It applies to everything BamDude produces from that camera:
| Output | Rotated |
|---|---|
| Notification snapshot | |
| Layer-timelapse frames — rotated as captured, before the video is assembled | |
| Finish photo, from any of its five sources: pre-captured frame, external camera, an already-open live view, a fresh grab, or the still recovered from the printer's own timelapse | |
| The printer's own timelapse video |
Why the printer's timelapse video is left alone
It is left exactly as the printer recorded it — turning a finished video means re-encoding it. Only the still extracted from it is straightened, so the finish photo matches every other image while the video stays untouched.
A frame that was already rotated on capture is never rotated a second time.
One camera, one reader¶
A Bambu printer's firmware permits exactly one camera connection, and a USB camera permits exactly one V4L2 handle. A capture that races another reader does not degrade — it fails.
BamDude enforces that on two axes:
- Background capture vs a viewer. While anyone is watching the live view, every background consumer — layer timelapse, finish photo, AI failure detection, the plate check, notification snapshots — reuses the live view's frame instead of opening its own connection. This has always been true for the built-in printer camera; external cameras now publish their frames the same way. Before that, watching an external camera during a print meant the layer timelapse recorded almost nothing and the finish photo arrived empty.
- Background capture vs background capture. With nobody watching, each consumer correctly concluded it was competing with no one — and then opened its own connection at the same moment as the next one. Two captures 207 ms apart were enough to knock over a running camera-wall stream. Simultaneous captures now share one connection: the first opens it, the rest wait for the same frame.
It coalesces; it does not cache
A capture that arrives after the shared one finished still takes a fresh frame. Plate detection and the finish photo decide things about a running print from these images, and a stale frame is worse than a slow one — a finish photo showing the bed already lowered is the failure this avoids.
Experimental isolated camera process¶
The default inline runtime keeps camera transport in the BamDude server
process. Advanced operators can set this environment variable before starting
the service:
It starts one supervised local child process for camera work. One-shot captures,
built-in Bambu chamber/RTSPS live view, and external MJPEG, RTSP and snapshot
live views use an authenticated local JPEG relay; browser URLs, tokens, and the
normal shared-viewer behaviour do not change. A disconnected browser relay
releases the child-side producer instead of leaving a camera or ffmpeg process
held open. The relay accepts at most 64 active sources and drops JPEGs over
2 MiB, bounding queued live frames to 128 MiB per process.
Experimental: verify before using on a production farm
worker fails closed. If its process containment or local connection cannot
start, BamDude does not switch that request back to inline transport.
Built-in Bambu live view is worker-owned too; its RTSPS path uses the same
per-model probe and reconnect profile as the inline view. Virtual Printer
camera passthrough is a worker-owned, byte-for-byte raw TCP lease. Keep the
default inline setting unless you specifically test the camera and Virtual
Printer paths on your host first.
The setting does not replace a hardware test. Camera firmware, Wi-Fi, ffmpeg
and hardware-decoder behaviour still depend on the host and the camera model.
Restart and INFO diagnostics¶
Set the variable in the environment used to launch the backend (or its .env)
and restart BamDude. Removing it or setting CAMERA_RUNTIME=inline takes effect
on the next restart. A development reloader can add a Python launcher process;
count worker-ready log entries and child PIDs, not just all Python processes.
The normal backend log includes worker ready/stopped records, viewer attach/detach,
relay start/first frame/end, and completed-session metrics. Match the printer and
session/identity fields; they connect parent records to the child. First-frame
latency and frame counts describe backend delivery, not proof of browser rendering.
viewers_gone after close is normal. subscribers=0 confirms that no browser viewer
remains on that relay; another viewer legitimately keeps the shared source alive.
Download the current log from System Info. DEBUG is not required for this basic diagnosis. Worker forwarding bounds record size and rate and redacts credentials/URLs; it does not log each video frame. Keep an issue's time and printer name when sending a log to support. Isolation does not automatically enable VAAPI/D3D11 or remove HTTP/1 browser connection limits.
Zoom & Pan¶
| Method | Action |
|---|---|
| Mouse wheel | Zoom in/out (100% - 400%) |
| Click and drag | Pan when zoomed |
| Pinch gesture | Touch device zoom |
Technical Details¶
graph LR
A[Printer Camera] -->|RTSP| B[ffmpeg]
B -->|MJPEG| C[BamDude API]
C -->|HTTP Stream| D[Browser]
| Requirement | Details |
|---|---|
| ffmpeg | Must be installed (included in Docker image). Needed for the RTSP camera on the X1 / X2 / H2 / P2 series; the A1 / P1 chamber-image protocol does not use it. |
| Camera enabled | Must be enabled in printer settings |
| Developer Mode | Required for camera access |
Pointing at ffmpeg — FFMPEG_PATH
If ffmpeg is installed but not on the running service's PATH — most often a fresh Windows install whose PATH change hasn't reached an already-open shell — an RTSP camera (X1 / X2 / H2 / P2) connects but shows no frames. Set FFMPEG_PATH in your .env (or the environment) to the full path of the ffmpeg binary and BamDude uses it directly, skipping the PATH search:
Left unset, behaviour is unchanged (PATH + common install locations are searched). The Docker image and native installers bundle ffmpeg, so this is typically only needed for local Windows development.
OBS Overlay¶
BamDude includes a streaming overlay at /overlay/{printer_id} combining camera feed with real-time print status. No login required.
Customize with query parameters: ?size=large&fps=30&show=progress,eta,filename
Stream Token Gate¶
Camera endpoints (live stream, snapshot, cover thumbnail, plate-detection reference) are not Bearer-token-friendly -- a <img src> tag can't attach an Authorization header. BamDude routes these through a short-lived query-param token instead:
- The frontend hits
POST /api/v1/printers/camera/stream-tokento mint a token tied to the current user (TTL 60 min). - The token is appended as
?token=...to every camera URL viawithStreamToken()in the API client. - Already-rendered DOM nodes (e.g. an
<img>mounted before the token arrived) are retrofitted byrewriteMediaSrcWithToken(). - The token is keyed by
user.idin React-Query so login/logout invalidates the cache.
Tokens are stored in auth_ephemeral_tokens so they survive backend restarts and work behind multi-worker deploys. Operators don't need to do anything -- this is invisible plumbing -- but the implication is that copying a camera URL out of the browser only works for the lifetime of the embedded token.
Long-lived tokens for Home Assistant / Frigate / kiosks / OBS¶
The 60-min UI-side token is the wrong shape for a wall-mounted dashboard, a Home Assistant camera entity, or a Frigate front-end that re-fetches the same URL for months. BamDude mints long-lived tokens for those cases.
Settings → API Keys → Camera and monitor tokens → Create new token. Give it a name, pick a scope (below) and a lifetime (1–365 days, default 90), and click Create. The token is shown exactly once.
Shown once only
BamDude stores only a pbkdf2 hash, so the plaintext can never be retrieved again — and a stolen database dump can't be replayed against the camera endpoints. If you lose the token, revoke the row and create a new one.
Scopes¶
Each scope is a separate grant. They never widen one another, and creating one never changes what an existing token can do.
| Scope | Reaches |
|---|---|
| Camera stream only | The camera stream and snapshot endpoints, nothing else. The right choice for Home Assistant, Frigate, or anything embedding a single camera. |
| Cam Wall | Those same streams plus the read-only Cam Wall feed: every printer's name, connection state and print progress — but never the print filename. |
| Streaming Overlay | One printer's camera stream plus the live print status the OBS overlay draws — which does include the filename shown on screen. |
The boundaries are deliberate and enforced in both directions: a Camera-stream token is refused by both the Cam Wall and overlay feeds; a Cam Wall token is refused by the overlay feed (the wall is trusted never to name the part on the bed, so folding the two together would silently widen every wall token already handed out) and vice versa. None of them exposes a printer's IP address, serial number or access code, or reaches any other BamDude API.
Status monitor is a separate, metadata-only scope in the same panel. It opens the operator monitor for all non-archived printers, with no cameras, filenames or printer control. It cannot be used as a camera token, and camera scopes cannot open the status monitor.
Token properties¶
| Property | Detail |
|---|---|
| Format | bblt_<prefix>_<secret>. The 8-character prefix is indexed so a lookup doesn't scan the table; it's also how you tell rows apart in the UI when you've forgotten which device holds which token. |
| Storage | The long_lived_tokens table, separate from the 60-minute browser tokens — the ephemeral sweeper never touches these. |
| Hashing | pbkdf2_sha256 over the full token, same as the rest of the codebase's password hashing. |
| Maximum lifetime | 365 days. "Never expires" is rejected by design: a leaked permanent token would be an irrevocable footgun. Rotate annually as part of normal credential hygiene. |
| Audit | last_used_at is stamped on each successful use (rate-limited to once a minute, so an MJPEG keep-alive doesn't hammer the DB). Tokens idle for 30+ days get a warning chip. |
| Revocation | Effective on the next request — there is no caching layer to wait out. |
| Scope of a token | All printers. These tokens are not narrowed per printer; a Camera-stream token can pull any printer's stream. Use the per-printer API keys if you need that narrowing. |
Administrators see an extra All users section listing every active token in the install — useful for triage if one is suspected of being leaked, or to enforce farm-wide hygiene.
Camera scopes require camera:view, plus the API-key permission for the action:
api_keys:create, api_keys:read or api_keys:delete. Access to the Settings UI
also needs settings:read. A Status monitor token instead needs printer and
queue read permissions along with the API-key permissions; it does not require
camera access. See the monitor setup guide.
URL shape: /api/v1/printers/{id}/camera/stream?token=<token> — the same
query-param contract as the short-lived flow, so Home Assistant's generic camera
platform, Frigate's mjpeg_streams, or a plain <img src> all work with no
further plumbing.
Cam Wall on a TV or kiosk¶
The Cam Wall has its own URL, so you can bookmark it or point a wall-mounted screen at it:
Opened in a browser you're signed in to, that's the same wall the Printers page shows — tiles stay clickable and the settings popover works as usual.
A TV or a Raspberry Pi in kiosk mode has no login, so it authenticates with a Cam Wall-scoped token in the URL instead:
A token wall is deliberately reduced to what a passive display needs:
- No settings popover, no click-through. Nobody is standing at a TV.
- Compact status overlay only. The state badge is shown; the print filename is not. The feed behind the page doesn't serve filenames at all, so the part on the bed is never named to a room anyone can walk into.
- No printer addresses or serial numbers, for the same reason.
- Archived printers don't appear. Printers in maintenance mode still do — they're still on the farm.
Because a kiosk browser is awkward to configure (you can't open devtools on a wall-mounted TV), the wall's settings can come from the URL:
| Parameter | Meaning | Range |
|---|---|---|
maxLive |
How many tiles stream live at once; the rest poll snapshots | 1–16 |
interval |
Seconds between snapshot refreshes on non-live tiles | 2–60 |
status |
Status overlay: off or compact (a token wall cannot select full) |
— |
Out-of-range or unreadable values fall back to the defaults rather than producing a wall you can't fix from the same URL. A kiosk never writes these back to the browser, so opening a kiosk link once won't overwrite your own wall preferences.
The URL is the credential
Anyone who can read that URL — off the screen, out of the browser history, out of the kiosk's config file — can watch the wall. Treat it like a key. If a display is retired or compromised, revoke the token and the wall goes dark on its next request.
Revoking a token¶
- Settings → API Keys → Camera and monitor tokens.
- Find the row by name or by
lookup_prefix. - Click Revoke, confirm in the modal.
Any device using that token loses access on the next request — no grace period, no caching layer to wait out. The row's hash is removed from the DB so even DB-dump replay won't work.
Cover Thumbnails¶
GET /api/v1/printers/{id}/cover returns the thumbnail of whatever the printer is currently printing. It is served exclusively from the local archive directory -- BamDude never initiates an FTP download from this endpoint. While a print is active and its 3MF hasn't been attached yet, the endpoint returns 404 and the UI falls back to a generic placeholder. Expect that for the opening minutes of any print started outside BamDude: its archive row is created the moment the print starts, and the 3MF is fetched off the printer afterwards -- on a P1S that fetch has been measured at over eight minutes. Once the file lands, whether from that fetch or a later archive_download_retry attempt, the endpoint starts returning the real PNG without any client action.
Embedded vs Window Mode¶
The camera viewer has two modes, configurable per-user in Settings > Camera:
- Embedded (default) -- The viewer overlays directly on top of the printer card. Multiple printers can have their cameras open at once and each viewer tracks its own size/position via local state. The page header's status bar continues to drive the rest of the UI.
- Window -- The viewer launches in a separate browser window (or PWA window). Useful for parking a single camera on a second monitor.
Embedded is the right default for live monitoring; window mode is for setups where the camera lives on a different screen from the printer dashboard.
Embedded viewer features¶
When using embedded mode, the camera appears as a floating window with the following affordances:
- Draggable — click and drag the header to reposition.
- Resizable — drag the bottom-right corner to resize.
- Persistent position — position and size are remembered per printer across sessions.
- Navigation persistence — leaving Printers closes its media requests; returning restores the last selected camera.
- Minimize — collapse to the title bar and stop the live request; expand to start it again.
- Close — click X to close the viewer.
- One viewer — clicking another printer replaces the current camera and cancels the old stream. Old saved multi-camera lists restore only the last camera.
Embedded mode for the whole farm
Use the single floating viewer to inspect a printer; use Camera Wall for the whole fleet.
Snapshot mode & FPS settings¶
For lower bandwidth, switch the per-camera mode to Snapshot instead of Live:
- Captures a single frame on demand, click refresh to fetch a new one.
- Ideal for cellular connections, slow networks, or cheap kiosks that don't need motion.
The default frame rate for live mode is 15 FPS. Tune via the URL ?fps=N parameter or the per-camera setting:
| FPS | Use case |
|---|---|
| 5 | Low bandwidth / A1/P1 cameras (hardware limit) |
| 10–15 | Balanced (15 is default) |
| 20–25 | Smoother video |
| 30 | Maximum quality (X1 / H2 / P2 only — also works for USB) |
FPS limits by camera type
- External cameras — capped at 15 FPS.
- A1 / P1 printers — capped at 5 FPS (hardware limitation).
- X1 / H2 / P2 printers — up to 30 FPS.
Higher FPS = more bandwidth
Higher frame rates consume more network bandwidth and server resources — for a multi-printer farm running 30 FPS on every viewer at once, plan accordingly.
Stream cleanup & auto-reconnect¶
BamDude properly cleans up camera streams to prevent orphaned ffmpeg processes:
- Window close — stream stops automatically.
- Tab hidden — stream pauses to save resources.
- Page unload —
ffmpegprocess terminated. - Refresh — old stream stopped, new one started.
Stall detection¶
The browser periodically checks if the stream is still receiving frames:
- Check interval — every 5 seconds.
- Detection — compares last frame timestamp.
- Threshold — stalled if no new frames received for >5 seconds.
Automatic recovery¶
When a stall is detected:
- Detects no frames received within threshold.
- Closes the stalled connection.
- Reconnects automatically.
- Resumes streaming.
For a brief break, the first RTSP reconnect is immediate. Repeated failures use a short, capped exponential delay with per-stream jitter, so a whole farm does not retry in one burst when an access point or switch returns. Closing the viewer interrupts that wait; it does not leave a reconnect task behind.
Network blips
If your network briefly drops, the stream will automatically recover once the connection is restored — no manual intervention needed.
Camera diagnostics¶
When a camera won't stream, BamDude can run a built-in diagnostic that tests the connection stage by stage and tells you which link in the chain is broken. A Diagnose button sits next to Retry on the viewer's error state, and a small stethoscope icon lives in the always-visible control bar (between Refresh and Fullscreen) for a pre-flight check before you even start streaming.
It calls POST /api/v1/printers/{id}/camera/diagnose and shows the results inline in a modal: one row per stage with a pass / fail / skipped marker, the per-stage duration in milliseconds, and a translated remediation hint. A Run again button re-runs the whole check without closing the modal.
Stages¶
| Stage | What it checks |
|---|---|
tcp_reachable |
Opens a raw TCP socket to the camera port — 322 for RTSPS, 6000 for chamber-image — with a 3-second timeout. It distinguishes a timeout ("printer not reachable"), a refused connection ("camera port closed — check LAN-Only Mode + Developer Mode"), and a host-unreachable error. |
first_frame |
Captures one JPEG end-to-end with a 15-second timeout, using the same pipeline that powers /camera/snapshot. Proves the full path actually delivers an image, not just an open port. |
Live-stream shortcut
If a viewer is already watching the camera and the buffered last frame is fresher than 10 seconds, the diagnostic skips the real test and reports the stream as live / healthy. Opening a fresh socket would kick the live viewer off on firmwares that allow only a single camera connection — so when there's already proof the camera works, BamDude doesn't disturb it.
The result also carries metadata for support triage: the protocol (rtsp / chamber_image), the port, the profile in use (default or a model-specific name), the mirrored Bambu Studio catalog resolution when known, and a summary code. The catalog is descriptive only; it never chooses a camera transport over live evidence.
If the first_frame check joined a camera capture that was already in progress,
the dialog says Shared concurrent capture. That is a successful result which
avoided opening a second socket to a printer that allows only one reader; New
camera capture means this diagnostic opened the capture path itself.
Camera Snapshot on Print Complete¶
BamDude can automatically capture a camera snapshot when prints complete:
- Settings → General.
- Enable Capture snapshot on print complete.
- Snapshots are saved to the print's archive folder and surface in the archive's photo gallery.
This creates a visual record of every completed print. It is off by default: an installation with no explicit saved setting does not keep background frames, take finish photos, or add notification images. Enabling it does not enable the printer's own timelapse; that remains the per-print choice made by the slicer or printer.
How BamDude picks the moment
The ideal moment is the last object layer, while the print is still on the bed and before the End G-code parks the toolhead, swaps the plate or clears it. BamDude tries three sources in order of quality:
- the printer's internal "end" stage, if the firmware reports it;
- the crossing into the final layer, caught from the printer's status updates;
- a rolling snapshot taken while the print was still running — the fallback for firmware that reports neither (the A1 Mini is the known case).
That third source is why a photo of an auto-swapped plate no longer comes back empty. The rolling snapshot refreshes at most every 25 seconds and stops updating the instant printing ends, so what it holds is the finished print rather than the aftermath. It's only taken when finish photos are enabled, never carries over between prints, and is skipped while you're watching the live camera so the stream isn't interrupted.
Other cameras (not tied to a printer)¶
An external camera above is a replacement for one printer's camera: that printer's live view, finish photo, plate check and Obico frames all come through it. A camera that watches a room, a shelf or a filament dryer is a different thing, and lives in its own list.
- Settings → Printing → Camera → Other cameras → Add camera.
- Give it a name (it is what the tile, the button and the window title say, so it must be unique), pick the type — MJPEG, RTSP, Snapshot or USB — and enter the URL or device path. The same optional Snapshot URL override and rotation as above are available.
- Optionally pick a Location — the same places printers and Zigbee sensors are filed under.
- Test opens the source once and confirms a frame, exactly as it does for a printer's camera.
Where it then appears:
- The camera wall, after the printers and in name order, on both the signed-in wall and a
?token=kiosk wall. The tile can go live like any other and counts against the same live budget; it carries no print status, because there is no print. - The Printers page, as a button on the heading of the location you filed it under, beside that location's sensor readings. Clicking it opens the floating window or a browser window, following the same Camera view mode setting the printer cards use.
Show on the wall switches a camera off without deleting it: the tile and the button disappear and its stream and snapshot routes stop answering, while the settings stay for later.
What a standalone camera never does
It does not take finish photos, check the build plate, feed Obico's failure detection, record a layer timelapse, or switch a chamber light on: every one of those belongs to a printer, and this camera has none. It also never replaces a printer's camera — if you want that, use External Cameras above.
One connection per camera
Everyone watching the same standalone camera shares one upstream connection, so a USB camera — which allows exactly one reader — does not drop the first viewer when a second opens it. A snapshot taken while somebody is watching reuses the live frame rather than opening a competing reader.
The kiosk list carries no URL
A kiosk wall authenticates with a token in its URL, and an RTSP camera's credentials live inside its own URL. The kiosk feed therefore serves a name, a rotation and a location and nothing else — the same reason it never serves a printer's serial number.
Light for the camera¶
A dark chamber makes a dark photo. BamDude can switch the chamber light on for the camera and off again afterwards — for every use of it: a photo in Telegram, a browser stream, the Camera Wall, the finish photo, the plate check, the camera diagnostics. (A standalone camera above has no printer, so none of this applies to it.)
- Settings → Printing → Camera → Light for the camera. Off by default — nothing changes for a farm that does not switch it on. This is the master switch: with it off, nothing below applies.
- In the same card, the External cameras list, per printer: Light for the camera — As on the farm / No. The printer that must not glow towards the window says No. The selector is shown only while the farm toggle is on, and not for a connected printer that has reported no controllable light.
- Also for Obico failure detection — a separate toggle, off by default, shown only when the farm toggle is on and Obico detection is enabled. Obico looks at the camera every few seconds for the whole print, so with this on the light stays on for the whole print.
Two rules make it safe to leave on:
- Only a light that is off is switched on. A light that was already on — you switched it, or the firmware did at print start — is never touched, before or after.
- Only a light BamDude switched on is switched off, and only once nobody is using the camera any more. If you switch the light off yourself during a stream, BamDude does not switch it back on; if you switch it on yourself, BamDude does not switch it off after.
How it behaves in practice: a one-off photo waits for the printer to confirm the light before the frame is taken (no fixed pause, no dark first photo), and the light goes off about ten seconds after the last use, so two photos in a row do not blink it. Several browser tabs on one printer are one switch-on and one switch-off. The Camera Wall in snapshot mode keeps the light on for as long as the wall is open, and lets it go within one refresh interval after the wall is closed. The layer-based timelapse deliberately does not take the light — it would flash on every layer; leave the light on yourself if the timelapse needs it. The printer's own timelapse lights itself.
One use does not wait for the toggle: the build plate check (below) lights the plate for its comparison whatever the settings say, as it always did — its reference was calibrated with the light on, and a check in the dark would pause a print for nothing. It now does so through the same mechanism: confirmed by the printer instead of a fixed pause, and never touching a light that was already on.
A1 / A1 mini
These printers do not switch their light on at print start the way the X1 and P1 series do, so a photo from the bot on a dark A1 was always dark. This setting is what fixes that.
After a restart
Nothing here is remembered across a BamDude restart. A light switched on for a stream that was open when BamDude restarted stays on; the next use finds it on and, by the second rule, leaves it alone.
Build Plate Empty Detection¶
Automatically detect if objects are left on the build plate before a print starts. If detected, the print is paused and a notification fires.
How it works¶
- Calibrate — capture reference images of your empty build plate.
- Enable — toggle plate detection on for the printer.
- Auto-check — when any print starts, BamDude compares the current camera view to your references.
- Auto-pause — if objects are detected, the print is immediately paused.
Calibration¶
Store up to 5 reference images per printer for different plate types (textured, smooth, high-temp, etc.):
- Click the scan icon on the printer card to open the modal.
- Ensure the build plate is completely empty and chamber light is ON.
- Click Calibrate Empty Plate.
- Optionally add a label (e.g.
Textured PEI,Cool Plate). - Repeat for each plate type you swap between.
Multiple references
The system automatically selects the best-matching reference when checking. Calibrate every plate type you actually use for accurate detection.
Enabling detection¶
The printer card has a split button:
| Button part | Action |
|---|---|
| Main (scan icon) | Toggles detection on/off. |
| Chevron (▼) | Opens the calibration / management modal. |
When enabled, the button shows a green border.
ROI (Region of Interest) editor¶
Adjust which part of the camera view is analysed:
- Open the plate-detection modal.
- Scroll to Detection Area (ROI).
- Click Edit.
- Use the X / Y / Width / Height sliders to size and position the green ROI box.
- Save.
The green box in the preview shows the detection area. Focus it on the build plate to avoid false positives from the printer frame, AMS unit, or background.
Detection mechanics¶
- Captures the current camera frame (or uses the buffered frame if a stream is active).
- Applies heavy Gaussian blur to both current and reference images.
- Normalises both for consistent comparison.
- Extracts the ROI region.
- Calculates pixel-difference percentage.
- If difference > 1%, plate is considered "not empty".
Notifications when objects detected¶
- Print pauses immediately.
- Toast notification appears in BamDude.
- Push notification sent (Telegram / Discord / Email / Pushover / ntfy / HA — whatever you have wired up).
- WebSocket event broadcast for integrations.
Requirements¶
| Requirement | Details |
|---|---|
| OpenCV | opencv-python-headless (already installed in the Docker image). |
| Chamber light | Switched on for the check by BamDude if it is off, whatever the Light for the camera setting says, and off again after. Calibrate with it ON. |
| Calibration | At least one reference image required. |
Troubleshooting¶
False positives (detects objects when plate is empty)
- Calibrate with chamber light ON (same as during prints).
- Adjust the ROI to exclude printer frame edges and AMS units.
- Add multiple calibrations for different lighting conditions.
False negatives (doesn't detect objects)
- Ensure chamber light is ON.
- Recalibrate — plate surface may have changed (resin residue, sticker peel, scratches).
- Confirm objects are within the ROI area — anything outside the green box is ignored by design.
Troubleshooting¶
Stream won't start
- Is the printer on? Camera requires power.
- Is the camera enabled in printer settings?
- Is
ffmpeginstalled? (Included in the Docker image.) - Is Developer Mode enabled? (Required for camera access on Bambu printers.)
- For external cameras, verify the URL with
curlfrom inside the BamDude host:curl -I http://192.168.1.50/mjpeg. - Running in Docker? If default bridge networking doesn't reach the printer, switch to
network_mode: host.
Stream freezes
- Network congestion or WiFi drops — try lowering FPS to 5 or 10.
- Check the printer's WiFi signal strength (poor signal causes erratic frame delivery).
- Try snapshot mode instead — it doesn't depend on a continuous stream.
High latency (1–3 second lag)
This is normal for MJPEG over HTTP and stems from RTSP buffering, ffmpeg processing pipeline, and HTTP-stream chunk boundaries. Cannot be eliminated entirely. Reduce by:
- Lowering FPS to reduce per-frame buffering.
- Using snapshot mode for monitoring vs streaming.
- Switching to an external camera with hardware MJPEG output (skips the RTSP→MJPEG transcoding).
Black screen
- Camera may be initialising — wait 5–10 seconds and refresh.
- Confirm camera works in Bambu Studio first; if it fails there, it's a printer-side issue, not BamDude.
- Check user-permission grants —
camera:viewis required.
Docker: camera not working
If camera streaming doesn't work in Docker, try host networking:
Default bridge networking with NAT works in most setups. Host mode is only needed when your network configuration prevents NAT'd traffic from reaching the printer's RTSP port.
API endpoints¶
For developers and integrations:
| Endpoint | Method | Description |
|---|---|---|
/api/v1/printers/{id}/camera/stream |
GET | MJPEG live stream. |
/api/v1/printers/{id}/camera/snapshot |
GET | Single JPEG frame. |
/api/v1/printers/{id}/camera/stop |
POST | Stop active streams for the printer. |
/api/v1/printers/{id}/camera/test |
GET | Test camera connection (returns success/failure without streaming). |
/api/v1/printers/camera/stream-token |
POST | Mint a 60-min query-param stream token (see Stream Token Gate above). |
OBS Browser Source recipe¶
Embed the live stream in OBS as a Browser Source:
- In OBS, click + under Sources.
- Select Browser.
- URL:
http://your-bamdude:8000/api/v1/printers/{id}/camera/stream?token=<long-lived-token>(use a long-lived camera token — short-lived ones expire mid-stream). - Width / height to match your scene (e.g. 1920×1080).
- OK.
For a richer overlay with status text, see the next section.
OBS Streaming Overlay¶
The dedicated overlay page combines the camera feed with real-time print status — one Browser Source instead of separate camera + text sources. URL shape:
OBS needs a token
Everything the overlay draws — print status, printer name, the camera feed — is behind authentication. It works in a browser where you are already signed in, but OBS is a fresh browser with no session, so the plain URL above renders a blank overlay.
This has nothing to do with how you reach the server: a reverse proxy, Cloudflare Tunnel or remote domain changes nothing, and an incognito window fails identically. What OBS needs is a token.
Streaming Overlay token¶
- Settings → API Keys → Camera and monitor tokens.
- Create a token with the Streaming Overlay scope and copy it.
- Append it to the overlay URL, with the printer number matching the printer's
own URL on the Printers page (
/overlay/1is printer 1, and so on):
In token mode the overlay skips the WebSocket entirely and refreshes on a 2-second poll — the token can't open a WebSocket, and the poll is the feed.
Treat the URL like a key
Anyone who can read that URL can watch the printer's stream and see the print filename. It cannot reach the printer's address, serial number or access code, and it cannot enumerate your other printers — a Streaming Overlay token opens one printer's overlay and nothing else. Revoke it from the same Settings page to cut the overlay off.
What's included¶
| Element | Description |
|---|---|
| Camera feed | Full-screen live camera view. |
| BamDude logo | Branding in the top-right corner. |
| Filename | Current print file name. |
| Status | Printing, Paused, Idle, etc. |
| Progress bar | Visual progress with percentage. |
| Layer count | Current layer / total layers. |
| Time remaining | Estimated time left. |
| ETA | Estimated completion time. |
Customising via query parameters¶
| Param | Values | Effect |
|---|---|---|
size |
small / medium / large |
Text and logo scale. medium is default. |
fps |
1–30 |
Live-stream FPS. Clamped server-side per camera type. |
camera |
true (default), false/0 |
false hides the camera feed and shows status on a black background. |
show |
comma-separated: progress, layers, eta, filename, status, printer |
Which status elements appear. |
Examples:
# Compact corner overlay with full status
/overlay/1?size=small&show=progress,layers,eta,filename,status
# Status-only display, no camera (low-bandwidth scenario)
/overlay/1?camera=false&show=progress,eta,status
# Maximum quality, full screen
/overlay/1?size=large&fps=30&show=progress,layers,eta,filename,status,printer
Idle state¶
When no print is running, the overlay still works — it shows the camera feed plus an "idle" / "offline" message and the BamDude logo. Useful for streaming farm cleanup, plate swaps, or off-hours.
Troubleshooting overlay¶
Overlay blank in OBS but fine in your browser
- This is almost always a missing token. Your browser is signed in; OBS is
not. Add
?token=…with a Streaming Overlay token — see above. - Verify the URL in a private / incognito window, not your normal one. If it fails there, it will fail in OBS for the same reason.
Overlay not loading at all
- Check that OBS can reach your BamDude server (same network, no VPN restrictions).
- Right-click the source in OBS → Refresh cache of current page.
Camera not showing in overlay
- Confirm the printer is connected.
- Confirm camera streaming works in BamDude directly first — the overlay uses the same stream.
- Signed in, status updates over WebSocket; in token mode it always polls every 2 seconds instead (a token can't open a WebSocket).
Tips¶
Multiple Cameras
Use Camera Wall for multiple cameras. Embedded mode keeps one viewer and switches it when you select another printer.
Bandwidth Conservation
Close camera windows when not actively watching to save server resources.
Mobile viewing
Camera streaming works on mobile with full touch support — pinch to zoom, drag to pan when zoomed. Access via the camera icon on each printer card.
Originally based on Bambuddy documentation.