The Scenet language#
Status: this is the specification. It is not a report of what is implemented — see implementation status. At present, nothing compiles.
A panel source is a YAML document describing what is in a panel, never where things are drawn. Coordinates do not appear anywhere in the language; producing them is the compiler’s entire job.
The layers#
A panel description has four authored layers. A fifth — resolution — is computed, and a sixth — rendering — is emitted.
Layer |
Block |
What it says |
|---|---|---|
Frame |
|
Size and margins of the panel |
Camera |
|
How the scene is framed |
Cast and staging |
|
Who is present, and how they relate |
Narrative |
|
What is said, and in what order |
Layers are separable on purpose. Changing camera.shot re-frames the same scene without touching
anything else, exactly as transposing a score changes its key without rewriting the melody.
A complete example#
panel:
size: [1000, 1000]
camera:
shot: medium_shot
angle: eye_level
cast:
alice: {reference: alice, pose: pointing, at: left_third, facing: right}
bob: {reference: bob, pose: arms_crossed, at: right_third, facing: left}
staging:
- alice left_of bob
- alice looking_at bob
- alice ground_shared_with bob
script:
- say: {by: alice, text: "You forgot your umbrella!", prefer: top_left}
- say: {by: bob, text: "I know."}
panel#
Key |
Type |
Default |
Meaning |
|---|---|---|---|
|
|
required |
Panel dimensions in panel units |
|
number |
|
Inset all content by this much |
Panel units are arbitrary and internally consistent; they become SVG user units.
camera#
Key |
Values |
Default |
|---|---|---|
|
see shot types |
|
|
|
|
shot determines the scale of every actor, by naming where the frame cuts the body rather than
what fraction of the panel a figure fills. This is the single most consequential value in a panel.
cast#
A mapping of actor id to properties. Ids are chosen by the author and referenced everywhere else.
Key |
Type |
Meaning |
|---|---|---|
|
asset name |
Which puppet to pull from the library |
|
pose name |
A named joint configuration declared by that puppet |
|
anchor |
Horizontal placement preference |
|
|
Which way the figure is turned |
at accepts left_third, center, right_third, left_edge, right_edge. It is a
preference, not a command — see conflicts.
staging#
A list of relations between actors, written subject predicate object. This is a scene graph: the
cast are nodes, these are the edges. Predicates come from the spatial subset of the Visual Genome
vocabulary rather than being invented here, so a Scenet scene remains convertible to and from the
scene-graph representations used elsewhere in computer vision.
Predicate |
Effect |
|---|---|
|
Fixes horizontal ordering |
|
Fixes draw order and occlusion |
|
Sets the subject’s gaze vector toward the object |
|
Places both actors on the same ground line |
Why ordering must be explicit#
left_of looks redundant next to at: left_third, but it is not. The layout engine is a linear
constraint solver, and “A and B must not overlap” is a disjunction: A is left of B, or B is
left of A. Linear solvers cannot express that choice.
So the language resolves it instead. By the time the solver runs, ordering is already decided — by
at, or by an explicit left_of — and what reaches the solver is a linear system it can always
solve. This is the reason there is no unordered beside predicate, and why any future construct
must resolve its own ordering in the frontend.
script#
An ordered list of narrative events. Order is meaningful: it is the order the reader reads them, and it constrains where balloons may be placed.
script:
- say: {by: alice, text: "You forgot your umbrella!", prefer: top_left}
- say: {by: bob, text: "I know.", kind: whisper}
Key |
Type |
Meaning |
|---|---|---|
|
actor id |
Who speaks; the tail points at this actor’s mouth |
|
string |
The dialogue. Line breaking is computed, not authored |
|
anchor |
A hint about placement, honoured when possible |
|
|
Balloon styling |
text is never pre-wrapped by the author. Wrapping is computed from real font metrics during
compilation, because a balloon’s size determines whether it fits where it is wanted — so the
compiler must decide the line breaks before it can place anything.
Reading order is enforced, not suggested#
Balloons are placed in script order, and a balloon may never sit above-and-left of the one before it. Violating this is not a cosmetic flaw: it makes the panel read in the wrong order, which is a correctness bug in a comic. The constraint is therefore hard, and a panel whose balloons cannot be placed without breaking it is rejected rather than rendered wrongly.
When constraints conflict#
Placement values are preferences of differing strength, and the solver resolves conflicts by priority rather than by failing:
Strength |
Examples |
|---|---|
Required |
Actors stay inside the panel; balloons never cover a face; reading order holds |
Strong |
Declared |
Weak |
|
So two actors both asked to stand at center will be pushed apart rather than overlapping: the
required non-overlap wins and the weak anchors yield. This is why at is documented as a
preference. If you need a guarantee, express it as a relation in staging.
Determinism#
The same source always compiles to byte-identical output. No wall-clock time, no unseeded randomness, no dependence on mapping iteration order. This is what makes a panel description a durable artifact rather than a prompt: it will render the same in five years as it does today.