TEASEDocs
ProductsStreamConcepts

Scenes and layers

What a scene actually is, what a layer can be, and the exact rule that decides whether the GPU pod re-encodes your frame or copies it byte-for-byte.

A scene is a layout, not a platform

A StreamScene row (table stream_scenes) is keyed by framewide or tall — not by platform. Its own model docstring is explicit about why: "two vertical platforms reference one scene, not diverging copies." If you arm a vertical layout for two platforms, editing it once updates the picture both of them get; there's no per-platform copy to keep in sync.

A platform scene (StreamPlatformScene, table stream_platform_scenes) is the exception — a second, platform-specific set of layers laid over the shared frame, covered in Platforms, studio, and secrets and Per-platform scene overrides.

A layer, and the one hard rule about its asset

layers is a JSON array stored on the scene row, parsed by app/products/stream/core/scene_layers.py — "the shared contract of the panel, the backend, and the video-motor" (the GPU pod; see Going live and signal for why that's not the StreamMotor row). Two things about layer geometry are load-bearing enough that the module docstring states them directly:

  • Geometry is in fractions of the frame, never pixels. A phone sending 720p instead of 1080p would otherwise scramble every layer's position.
  • A layer's asset field can only be a media:<id> reference — never a path or a URL. The GPU pod resolves that reference over an internal loop; accepting an arbitrary URL there would let a scene ask the box to fetch anything at all.

No-re-encode vs re-composing — the actual function, not a guess

Whether the GPU pod copies your feed byte-for-byte or decodes and re-encodes it is decided by one function, needs_compose(layers, frame), and its logic is short enough to state exactly:

  1. A vertical frame always needs compose. A widescreen source can't reposition itself into 9:16 on its own.
  2. An empty scene never needs compose. No visible layers means the prior byte-for-byte behavior, not a black frame.
  3. More than one visible layer always needs compose.
  4. Exactly one visible layer needs compose unless it is a plain, full-frame, uncropped, default-fit live sourcekind == "source", no src override, positioned at (0,0) sized (1,1), no crop, fit == "cover". Anything else about that single layer — a crop, a non-default fit, an offset position — flips it to needing compose too.

"Visible" itself has its own filter: a layer only counts if visible is set, its kind is one of the live kinds, its opacity is greater than zero, and it actually has content (layer_has_content) — an added-but-empty layer doesn't force a re-encode.

This is why five platforms don't cost five times as much

The GPU pod runs needs_compose once per scene, not once per platform. A composed frame is fanned out identically to every armed platform on that layout — the recomposition cost is paid once regardless of how many platforms receive the result. Only a platform scene — a layer set specific to one platform — pays its own, separate re-encode.

What's next

Platforms, studio, and secrets — the three different per-platform configuration surfaces, and the three secrets this product keeps separate on purpose.

On this page