Skip to content

Manifest format

Manifest format

A deck is a list of frames, each frame a list of items:

slides:
  - frame:
      - title: First slide
      - ...
  - frame:
      - ...
    same_title: true      # keep the previous frame's title

Content items

Item Content
title: text slide title
load: key latex content from the definitions file (text/formula mode comes from the entry)
latex: \emph{inline} latex inline text-mode latex
formula: e^{i\pi}+1=0 inline math-mode latex
image: figure.png image (optional scale:)
shader: plasma.frag single-pass fragment shader (optional resolution: [w, h], uniforms:, textures:, see below)
object: name C++-registered object or group
mesh: bunny.obj mesh from an obj file (optional smooth:, normalize:; at: is a persistent transform label)
camera: view_name camera view from views/view_name.json (fly: true for a flight transition)
pause: 3 timed pause in seconds
keyframe: label labels this frame for C++ updaters (see keyframes)

Placement

Screen items take one placement key:

- load: my_key
  at: my_label            # persistent, drag-editable label
- formula: x^2
  at: [0.5, 0.4]          # fixed position
- latex: some text
  at: TOP                 # TOP | CENTER | BOTTOM
- image: fig.png
  below: my_key           # below/above/right_of/left_of another item
  padding: 0.05

When omitted, load/image items default to a label derived from their key or filename, so everything is drag-editable out of the box.

Steps

A bare - step marker splits a frame into clicks (the equivalent of inNextFrame): every item after it appears on the next click.

- frame:
    - load: question
    - step
    - load: answer
      below: question

Keyframes

A keyframe: labels the frame it appears in, so C++ updaters can branch on t.afterKeyframe("label") (also atKeyframe, beforeKeyframe) instead of counting frames: the test follows the label wherever manifest edits move it.

- load: usual_pipeline
- step
- keyframe: pipeline_shown
- load: reconstruct

Referencing items : ids and groups

Operations refer to items by their key (latex key, image filename stem, object name, title), or an explicit id:. Any item can also join a tagged group with group: name; a group has no position of its own, operations simply map over its members.

- formula: \mathcal{S}
  id: isurf
- latex: a remark
  group: side_notes

Operations

After a - step (or in a later frame), existing items can be manipulated:

- step
- remove: [isurf, side_notes]     # item ids or groups
- replace: fig
  with: {image: better_fig.png}
- set: isurf                      # re-place an existing item,
  at: new_label                   # transition animated

Connectors and layout

- arrow: {from: KR2, to: KR2_sub, bend: 0.25, color: "#aa0000"}

Arrow endpoints follow their target every frame: an item id, a [x,y] position, or a label. from_offset / to_offset shift the attach points (see shapes & arrows).

- box:                       # rectangle englobing its items, following them
    - latex: framed content
    - image: fig.png
  padding: 0.02              # also padx/pady, color, thickness,
  filled: true               # filled, fill_color, alpha, id

- stack:                     # children laid out below one another
    - latex: first paragraph
    - step
    - latex: appears later     # space is reserved, earlier children
  at: column_handle            # never move (see stacks page)
  spacing: 0.02
  align: left                # left | center | right

Typos

Unknown keys are reported in the terminal instead of being silently ignored. If an item does not move where you expect, check the indentation: a field must be aligned with the first key after its item's dash.

Shader items

A shader: item can declare its uniforms and its textures, which covers most of what a single-pass shader needs without touching C++. Multi-pass, ping-pong and storage buffers stay on the C++ side, since they need a streaming order the manifest cannot express.

- shader: sky.frag
  resolution: [900, 600]
  uniforms:
    sun:      dir                                       # a type name on its own
    steps:    {type: int, default: 64}                  # long form, with a default
    tint:     {type: color, default: "#ffcc88"}
    speed:    {type: float, default: 1.0, min: 0, max: 5}   # bounded, so a slider
    controls: "vec3[8]"                                 # an array
  textures:
    noise: noise.png
    grad:  {file: gradient.png, filter: nearest, wrap: repeat}

Each uniform becomes a persistent tunable parameter: it appears in the Tuner panel while the shader is on screen, you drag it live, Ctrl+S saves it to views/params.json and the next run picks it up. The shader follows it every frame.

Types are float, int, bool, vec2, vec3, dir and color (vec4). A dir is a unit vector, aimed on a ball rather than typed component by component. Bounds are optional, and a bounded parameter is drawn as a slider rather than a drag field.

<type>[N] declares an array, from 1 to 64 elements. The shader sees uniform vec3 controls[8]; and the panel shows one parameter per element, named controls[0] to controls[7], each with its own handle. Its default is a list of one value per element. Watch the quotes: inside a flow mapping yaml reads the brackets itself, so write {type: "vec3[8]", default: [...]}.

Each texture binds an image file to the sampler of the same name, which the shader declares itself:

uniform sampler2D noise;
uniform vec2      noise_size;   // optional, its size in pixels

filter is linear (default) or nearest, wrap is clamp (default) or repeat. Only image files here: a texture fed by another pass or by a previous frame stays in C++.