Foundations: Overlays¶
An Overlay is a scene-like container — a layer's motion chain can start at it exactly as at a Scene (Resolver Engine → The Motion Graph settles the motion inheritance side) — that does not participate in scene sequencing. It covers a set of Scene instances rather than taking a slot in the sequence the way a Scene does. This page owns everything about that: the coverage shape, what travels with a Scene when it's reused in another Project, and the naming question.
Coverage, not sequence¶
Project altitude's Change types reflect the split directly: addSceneInstance /
removeSceneInstance / setInstanceOrder for the sequenced case, addOverlayInstance /
removeOverlayInstance / setOverlayCoverage for the coverage case
(The Change Model). List order means something
for a Scene instance and nothing for an Overlay instance, so the two can't share a mechanism that
depends on it.
Decided: scene-level coverage only, for now
An Overlay's range is the set of Scene instance ids it covers — not a range with endpoints,
and not an offset into or across a Scene. A set survives reordering: the covered instances stay
covered wherever setInstanceOrder puts them, and coverage must stay a contiguous run of
the Project's order — a reorder or an insertion that would split a covered set is rejected, since
an Overlay's span is derived from what it covers and a gapped span has no defined length for an
instance's fractional timing to resolve against (below). Two more rules keep it consistent under
removal:
setOverlayCoverage is how an author adds or drops a Scene from an existing Overlay's set
(never remove-and-re-add, which would orphan the Overlay instance's own overrides), and a
removeSceneInstance that would leave an enumerated Overlay covering nothing is
rejected rather than silently emptying it — otherwise the removal drops that instance from
every Overlay covering it and the rest of each set stands. The rejection is specific to
enumerated sets: an Overlay on the implicit all-Scenes coverage below has nothing to empty, so
emptying a Project down to its last Scene is never blocked by the brand-level floor riding along
with it. The shape leaves room to add finer sub-scene granularity later
— an offset into a covered Scene — if a real need ever shows up, which isn't expected.
flowchart LR
subgraph project["Project — ordered Scene instances"]
S1["Scene A"] --> S2["Scene B"] --> S3["Scene C"] --> S4["Scene D"]
end
O1["Overlay: legal disclaimer<br/>covers {A, B, C}"] -. covers .-> S1
O1 -. covers .-> S2
O1 -. covers .-> S3
O2["Overlay: brand watermark<br/>coverage: all Scenes"] -. covers .-> S1
O2 -. covers .-> S4
Scene reuse across Projects: what travels with the Scene¶
Scene reuse across Projects is already supported by the model as it stands: a Scene instance is a placement of one frozen, immutable Scene version, so nothing stops the same Scene being placed into more than one Project. What a review comment from Blair surfaced is a gap in what travels along when that happens: an Overlay's coverage is tied to the Project it was authored in — which Scene instances, within that Project, it spans — not to the Scene itself. When a Scene that carries an Overlay elsewhere, most often a legal disclaimer or brand watermark, gets picked into a different Project, nothing carries that Overlay along automatically. Reusing a Scene would need someone to notice the old Overlay and re-add an equivalent — exactly the silent, easy-to-miss gap a disclaimer requirement can't tolerate.
Decided, per direct instruction: a brand-level default Overlay is the floor
The brand-level default is another field on the brand's own style profile (Brand Scoping & Brand Style), alongside primary colour and heading font: sourced from the brand, not any individual Scene, so a disclaimer or branding requirement attaches to the brand and applies to every Project built for it, automatically, regardless of which Scenes were picked.
What makes it structural rather than remembered is its coverage kind. A brand default is placed with an implicit all-Scenes coverage rather than an enumerated set, so a Scene added to the Project later is covered the moment it's added, with nothing to extend and nothing to remember. That is the only coverage kind besides the explicit set, and it exists exclusively for this case; an author-placed Overlay always enumerates.
It is a real instance, system-emitted. The default is materialised into the Project's
content by an addOverlayInstance the system emits when the Project is created, and re-emitted
when a Project rebases onto a newer brand-style version that changed it. That keeps "Change[]
is the only path by which content mutates" literally true rather than carving out a writer that
bypasses it, and it gives the Overlay a real instance id — which the derived-span reasoning
below needs, since an Element instance inside it has to attach to something.
But it is not the composing rungs' to remove. A system-emitted default Overlay instance
rejects removeOverlayInstance and setOverlayCoverage from every rung on the composition
scale; it changes only through the brand's style profile, behind the same off-scale
manage:brand-style capability that governs every other field on it
(Permissions & Enforcement). Without that, a
High-tier user could delete the disclaimer or narrow it to one Scene, and neither the
"structural rather than remembered" claim nor the custom-Overlay prompt's licence to be merely
informational would hold.
It reuses the style profile's own versioning discipline unchanged, no new plumbing: the default resolves and freezes at the point a Project's reference to it is committed, and a later edit to the brand's default only becomes visible once a Project explicitly rebases onto the newer brand-style version, surfaced through the existing staleness badge (Fork, Rebase & Conflict Resolution). Which brand's default a multi-brand Project carries is the open binding question on the brand page (Brand Scoping & Brand Style).
An Overlay's span is derived, which is what an instance's fractional timing resolves against
A Scene owns an authored duration; an Overlay doesn't — its span is derived from the Scene
instances it covers, so it changes whenever one of those Scenes is replaced by a longer or
shorter version. An Element instance inside an Overlay therefore has an instance duration
defaulting to that derived span, and a { value, unit: "targetFraction" } start or end resolves
against the instance's own resolved duration exactly as it does inside a Scene
(Elements, Scenes & Projects) — the difference is
only that the span it ultimately derives from can move without the Overlay itself being edited.
A rebaseInstance on a covered Scene is the concrete case, and it is one more reason that
Change is gated by the rebase check like any other.
A separate, lighter mechanism covers what the floor deliberately doesn't: a Scene carrying its own
custom, non-brand-default Overlay (via addOverlayInstance), reused in a new Project. This isn't
structurally guaranteed the way the brand-level floor is — a custom Overlay is deliberately
Scene-specific, not something every Project for the brand should inherit — so a manual, lightweight
heads-up prompt at pick time, offering to bring that specific Overlay along too, is the right
weight: informational, not load-bearing, since the compliance-critical case is already covered by
the floor regardless of whether this prompt is ever acted on.
Naming: is "Overlay" the right term?¶
Flagged as a possible future rename, not decided. Matthew's own reframing: an Overlay is genuinely scene-like, not categorically different from a Scene — just not a core, sequenced one, more like a floating scene. "Overlay" on its own can read as something painted visually on top rather than "not part of the sequence," which is the distinction that matters. The engine's own docs already treat it as a scene-like container and describe it as a floating scene spanning several underlying scenes, so this framing is latent in the source material, not a new idea.
| Candidate | For | Against |
|---|---|---|
| Floating Scene / Loose Scene / Detached Scene | Keeps "Scene" in the name, matching the reframing; Matthew endorsed "Floating Scene" independently | Leaves plain "Scene" needing a contrasting label of its own ("Core Scene") if the distinction is ever meant to read symmetrically |
OverlayScene |
The leading candidate if revisited — states the scene-kinship in the identifier itself, no separate label needed for the plain case | Longer |
| Span | Names the mechanic (spans a set of Scene instances) | Collides with the engine's "spanning" vocabulary for a background spanning its members |
| Track | Familiar from video-editing timelines | Risks being misread as an audio track — a real collision, since audio is a layer type in this schema |
Overlay stays the actual name for now. If this is revisited for real, the two questions to settle together are which term to land on, and whether renaming Overlay also means giving plain "Scene" its own paired label, or leaving the contrast implicit as it is today.
Risks¶
- The contiguity requirement constrains reordering in a way authors won't expect. Coverage is a
set, which is why it survives reordering, but it must stay a contiguous run for the derived span
to have a length — so a
setInstanceOrderthat would drop an uncovered Scene into the middle of a covered run is refused. That is a defensible rule and an unhelpful error message; the alternative (a defined span for a gapped set) is real added complexity for a case nobody has asked for yet. - A brand-default Overlay no composing rung can remove is the right default and a hard stop. A Project that genuinely needs one Scene without the brand disclaimer has no path through composition, only through the brand's own style profile, which changes it for every Project. Whether that ever comes up is unknown; if it does, the answer is a per-Scene exemption on the profile rather than letting the floor be removed locally.
Open questions¶
- Whether an Overlay is its own identity/version family or a Scene row with a kind
discriminator.
addOverlayInstanceplaces a frozen Overlay version and the placement dialog fires on a major-behind Overlay (Editing UI & Canvas), so a version row has to exist; Storage & Versioning names no Overlay pair. Given an Overlay is scene-like in every respect but sequencing, reusingretail_sceneswith a discriminator is the candidate that avoids a fifth near-identical table family — but it isn't decided, and the brand-scope intersection rule has to apply to Overlays either way. - The rename above — fine to bikeshed later, blocks nothing.
- Where an Overlay sits in time beyond scene-level coverage (its own cue/duration) is the engine's open question, untouched here (Resolver Engine → The Motion Graph).