Video tile
A single sensor feed — FPV camera, gimbal, IR, EO. RC3 ships the frame; the consumer plugs the stream player into the children slot.
Live
Three feed states. Toggle recording and reticle to see the optional overlays. The frame renders a stylised placeholder where the consumer's real video element would sit.
Healthy feed with full telemetry burn-in. Ember dot marks the source as the active RC3 feed; status reads on the label.
Bandwidth-degraded link. Only the sensor mode is in scope — bearing and range are stale and deliberately omitted.
Minimal default. No telemetry passed; the tile stays sparse. Pass props only when the consumer has signal to surface.
Signal lost. The overlay blanks the frame so a stale image cannot be misread as live; telemetry suppressed.
Anatomy
Frame + five overlay layers. Everything outside the consumer's video element is RC3 chrome — restrained, never competing with the feed for attention. The source dot carries the Ember signature (identity); status reads in the label colour (semantic).
Top-left chip. Ember identity dot + mono source identifier + semantic status label. Always visible.
Bottom-left mono row, single line. Surfaces only the cells the consumer passes — coordinates, sensor mode, bearing, range, zoom. Suppressed when feed is lost.
Top-right chip. Small danger dot + REC. Optional; surfaces only when the consumer marks the feed as recording.
Centre crosshair + outer ring for operator aim. Optional. Hidden when the feed is lost — meaning is undefined.
Replaces the frame on status="lost". Backdrop-blurred bg-bg with danger dot + NO SIGNAL caption. The frozen frame is never shown alone.
The source dot. Marks this surface as RC3-managed regardless of feed state. Distinct from semantic status — operators read identity and state independently.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| source | string | — | Operator-visible identifier — e.g. "FPV · UGV-04". Rendered in the top-left corner in mono. |
| status | "live" | "degraded" | "lost" | — | Feed status. Drives the corner state label and the no-signal overlay. Honours invariant 5: a lost feed surfaces as an unambiguous overlay, not a frozen frame. |
| recording | boolean | false | Surfaces a danger-toned REC indicator in the top-right. The consumer drives recording state from their pipeline. |
| aspectRatio | "16:9" | "4:3" | "9:16" | "1:1" | "auto" | "16:9" | Aspect ratio constraint. auto releases the constraint so the consumer's video element sizes the tile naturally. |
| reticle | boolean | false | Optional centre crosshair + outer ring overlay for aim. Hidden when the feed is lost. |
| coordinates | string | — | Consumer-formatted look-point coordinates — e.g. 01°20'58"N 103°49'13"E. Surfaces in the telemetry strip when present. |
| bearing | number | — | Camera bearing in degrees (0–359). Formatted as BRG ###°. |
| range | number | — | Range to look-point in metres. Formatted as RNG N m below 1 km, N.N km above. |
| zoom | number | — | Zoom multiplier — e.g. 4 for 4×. |
| sensor | "EO" | "IR" | "LL" | — | Sensor mode badge — electro-optical, infra-red, low-light. |
| children | ReactNode | — | The consumer-rendered video element, canvas, or stream player. Sized to fill the frame; RC3 does not ship the player. |
| className | string | — | Forwarded to the root container. |
Types
FeedStatusFeed status classifier. Your application owns the classification — typically derived from link metrics + frame rate + last-frame timestamp on the consumer side.
type FeedStatus = "live" | "degraded" | "lost";SensorModeSensor mode tag. EO (electro-optical, daylight), IR (infra-red), LL (low-light). Drives the sensor badge in the telemetry strip.
type SensorMode = "EO" | "IR" | "LL";Wiring
Read-only — the tile renders whatever you pass into children. Video transport varies hugely across platforms (RTSP / H.264, WebRTC, MJPEG, HLS, raw frames over WebSocket); RC3 stays out of the protocol business and lets you bring your own player.
// WebRTC / HLS — with telemetry burn-in
<VideoTile
source="FPV · UGV-04"
status={feed.status}
recording={feed.recording}
coordinates={lookAt.geo}
bearing={camera.bearing}
range={camera.rangeMetres}
zoom={camera.zoom}
sensor={camera.mode}
>
<video
ref={feed.videoRef}
autoPlay
muted
playsInline
className="h-full w-full object-cover"
/>
</VideoTile>// RTSP via your JS player — minimal
<VideoTile source="GIMBAL · UAV-09" status={feed.status}>
<YourRtspPlayer
url={feed.rtspUrl}
className="h-full w-full"
/>
</VideoTile>Behavioural rule
Telemetry never silently stale
A stale frame masquerading as a live feed is the worst-case failure — operators act on imagery that is no longer true. When the feed is lost, the tile blanks with an explicit NO SIGNAL overlay rather than holding the last frame.
Accessibility
| Figure role | The tile carries `role="figure"` with an `aria-label` of the form `{source}, {status}` so screen readers can announce the active feed and its state in one breath. |
|---|---|
| Decorative chrome | Source label backdrop, status dot, reticle, and recording chip background are all `aria-hidden`. The accessible label on the figure root carries the meaning. |
| Recording state | The recording chip carries `role="status"` with `aria-label="Recording"` so a screen reader can announce the start of recording. |
| Colour and meaning | Status colour is never the only signal — every state carries an explicit text label (LIVE / DEGRADED / NO SIGNAL). Operators with colour-vision differences still read state. |
| Consumer responsibility | The video element inside `children` is the consumer's surface — captions, audio descriptions, and player controls are the consumer's accessibility responsibility. |
JavaFX
Ships in the PRIZM JavaFX library for thick-client C3 apps as Rc3VideoTile (extends StackPane). Run the gallery to see it natively.
import design.prizm.fx.rc3.Rc3VideoTile;
Rc3VideoTile()
Rc3VideoTile(String source, FeedStatus status)| Member | Type | Default | Description |
|---|---|---|---|
| FeedStatus | enum | — | LIVE / DEGRADED / LOST — corner label + no-signal overlay. |
| AspectRatio | enum | R16_9 | R16_9 / R4_3 / R9_16 / R1_1 / AUTO — height derives from width. |
| SensorMode | enum | — | EO / IR / LL — sensor badge cell. |
| setContent | (Node) → void | — | The consumer's video / canvas / player; fills the tile. |
| setSource / setStatus | (String) / (FeedStatus) → void | — | Source label + feed status. |
| setRecording / setReticle | (boolean) → void | — | REC chip + centre reticle overlays. |
| setCoordinates / setBearing / setRange / setZoom / setSensor | → void | — | Telemetry burn-in cells (suppressed when lost). |
Ships the surface; the consumer plugs the feed. Honours invariant 5 — a lost feed reads NO SIGNAL, never a frozen frame. The source dot is Ember. JavaFX has no backdrop-blur, so overlay pills use a solid fill. Mirrors components/rc3/video-tile.tsx.
Usage
A single tile renders one feed. Multi-feed surfaces (FPV + gimbal + IR side-by-side, picture-in-picture) are template-level compositions — drop several VideoTile instances into a grid and let your application decide which is primary. The status prop must reflect actual feed health on the consumer side — never default to "live" while you wait for the first frame; show "degraded" or "lost" until the stream is verified.