Skip to content
Docs
Open Promoeyes
camera-framing

camera-framing

Camera-framing primitives — every parameter of `<frame>`, one scene each.

Read the rig's notes
`<frame>` is a CAMERA POSE TRACK. It lives under `<camera><properties>` and
holds `<keyframes><kf …/>`; each kf is resolved fresh EVERY FRAME and the
two bracketing kfs are lerped. There are exactly two kinds of kf:

  COORDINATE kf   kf(time= position="x y z" look_at="x y z" fov= …)
                  a literal pose, the same thing a hand-authored camera does.
  FRAMED kf       kf(time= node="<node-id>" fill= …)
                  "put the target on screen at N% of the viewport" — the pose
                  is SOLVED from the node's LIVE world bounds, so it tracks a
                  target that is moving, growing, or still laying itself out.

Both kinds live in one track and lerp into each other, which is the whole
point: fly from a hand-picked establishing pose straight into a solved
close-up without ever typing a coordinate for the close-up.

THE PARAMETERS (packages/engine/lib/frame-shot-target.ts):
  node      the node id to frame. Its presence is what makes a kf "framed".
  framing   "all" (also scene / stage / world) frames the WHOLE SCENE instead
            of one node — the union of everything it shows, solved live, so
            no id is named and nothing is missed. Haze, lights, camera layers
            and off-screen ui stages are not content and stay out of it; the
            fit clears the scene's depth, so the front of the box is on
            screen at 100 too. Scene 09 is its catalogue.
  fill      1 to 1000, default 80 — percent of the viewport the target fills.
            Distance is solved so the LARGER of (height, width·aspect) fits:
            100 is the exact fit, below it pads, above it keeps closing in on
            the centre of the target (140 = "inside the composition").
  fov       the lens the shot is solved FOR. Same fill, different fov = same
            on-screen size, completely different perspective. Defaults to the
            camera's live fov.
  padding   distance multiplier applied after the fill solve, default 1.
            padding=1.5 = "the same shot, 50% farther back".
  offset    "x y z" added to the LOOK-AT point, so the target slides off
            centre by the negated offset while the camera translates with it.
  side      which way the camera sits off the target's face:
              auto  (default) the target's outward normal, flipped if it
                    faces away from the usual overview (0 1.2 10.5) — and
                    NEVER flipped for a live device screen, whose readable
                    side is already known
              front the target's own +normal, whatever it is aimed at
              back  the mirror of it
              world fixed world +Z — the target's rotation is ignored
                    entirely (what camera-facing text reels want, so a
                    flapping target cannot swing the camera around)
  bounds    what gets measured:
              (unset) ONE mesh — the node's live display surface if it has
                      one, else its largest-area mesh
              union   the union of the whole subtree — for g / row / column /
                      isle / grid containers
              visible only the members currently on screen, each box scaled
                      by its live opacity, so the shot GROWS continuously as
                      a group fades in instead of jumping per entrance.
                      (word-cloud nodes default to this.)
  mesh      pick one mesh inside the node by name. Every mesh is renamed to
            its authoring node id at mount, so this takes an id.
  mesh_match  exact (default) or contains — substring match on that name.
  up        "x y z" overrides the derived camera up (which follows a rolled
            target, rolling the horizon with it).
  zoom      applied on top of the solved pose — punch in without moving.
  focus / aperture / max_blur   the DOF trio also rides these kfs; the depth
            of field reel (promos/primitives/camera-dof/) is its catalogue.

AND ON THE TRACK ITSELF:
  frame(smooth=0.5)  makes the camera CHASE the track with an exponential lag
            of that many seconds instead of riding it exactly. Raw segment
            lerping is velocity-discontinuous — the camera dead-stops at
            every keyframe — and it twitches whenever a live-solved target
            changes size. The damped chase keeps velocity continuous through
            both. Scene 08 is nothing but hard steps, made fluid by it.

AUTHORING TRAP worth knowing before you copy anything here: a `<g>` in this
format is an IDENTITY container. Its own position / rotation are folded into
an SVG child context, NOT applied as a 3D transform, so a `<g>` carrying
rotation="0 -38 0" silently does nothing to its children. Every element below
therefore carries its own absolute position and rotation, and the only two
`<g>`s left in the reel (s05-cluster, s06-board) exist purely as FRAMING
TARGETS for bounds=union and bounds=visible. Likewise a `<rect>` with no
`position` is anchored SVG-style at its top-left, so its centre lands at
(w/2, h/2) — every rect here states its position explicitly.

Scene map — the tight beats always carry their own label ON the framed card,
because a 
camera dives in.
  01 node · fill        35 to 60 to 95 on one card
  02 side               front · back · world (auto is the default everywhere else)
  03 padding · offset   the same fill, pushed back and pushed off-centre
  04 fov                18 · 42 · 80 at ONE fill, down a depth corridor
  05 bounds             visible (live build-up) · unset (one mesh) · union
  06 mesh · mesh_match  exact id · substring · then the whole board
  07 up · zoom          a rolled card: derived up vs world up, then zoom
  08 smooth             a step track with no easing at all, damped to fluid
  09 framing="all"      the whole scene as the target: 55 · 100 · 140

Preview: /packages/runtime/index.html?project=promos/primitives/camera-framing/camera-framing.promo
Topic
camera
Scenes
9
Kind
reference rig
//
    Camera-framing primitives — every parameter of `<frame>`, one scene each.

    `<frame>` is a CAMERA POSE TRACK. It lives under `<camera><properties>` and
    holds `<keyframes><kf …/>`; each kf is resolved fresh EVERY FRAME and the
    two bracketing kfs are lerped. There are exactly two kinds of kf:

      COORDINATE kf   kf(time= position="x y z" look_at="x y z" fov= …)
                      a literal pose, the same thing a hand-authored camera does.
      FRAMED kf       kf(time= node="<node-id>" fill= …)
                      "put the target on screen at N% of the viewport" — the pose
                      is SOLVED from the node's LIVE world bounds, so it tracks a
                      target that is moving, growing, or still laying itself out.

    Both kinds live in one track and lerp into each other, which is the whole
    point: fly from a hand-picked establishing pose straight into a solved
    close-up without ever typing a coordinate for the close-up.

    THE PARAMETERS (packages/engine/lib/frame-shot-target.ts):
      node      the node id to frame. Its presence is what makes a kf "framed".
      framing   "all" (also scene / stage / world) frames the WHOLE SCENE instead
                of one node — the union of everything it shows, solved live, so
                no id is named and nothing is missed. Haze, lights, camera layers
                and off-screen ui stages are not content and stay out of it; the
                fit clears the scene's depth, so the front of the box is on
                screen at 100 too. Scene 09 is its catalogue.
      fill      1 to 1000, default 80 — percent of the viewport the target fills.
                Distance is solved so the LARGER of (height, width·aspect) fits:
                100 is the exact fit, below it pads, above it keeps closing in on
                the centre of the target (140 = "inside the composition").
      fov       the lens the shot is solved FOR. Same fill, different fov = same
                on-screen size, completely different perspective. Defaults to the
                camera's live fov.
      padding   distance multiplier applied after the fill solve, default 1.
                padding=1.5 = "the same shot, 50% farther back".
      offset    "x y z" added to the LOOK-AT point, so the target slides off
                centre by the negated offset while the camera translates with it.
      side      which way the camera sits off the target's face:
                  auto  (default) the target's outward normal, flipped if it
                        faces away from the usual overview (0 1.2 10.5) — and
                        NEVER flipped for a live device screen, whose readable
                        side is already known
                  front the target's own +normal, whatever it is aimed at
                  back  the mirror of it
                  world fixed world +Z — the target's rotation is ignored
                        entirely (what camera-facing text reels want, so a
                        flapping target cannot swing the camera around)
      bounds    what gets measured:
                  (unset) ONE mesh — the node's live display surface if it has
                          one, else its largest-area mesh
                  union   the union of the whole subtree — for g / row / column /
                          isle / grid containers
                  visible only the members currently on screen, each box scaled
                          by its live opacity, so the shot GROWS continuously as
                          a group fades in instead of jumping per entrance.
                          (word-cloud nodes default to this.)
      mesh      pick one mesh inside the node by name. Every mesh is renamed to
                its authoring node id at mount, so this takes an id.
      mesh_match  exact (default) or contains — substring match on that name.
      up        "x y z" overrides the derived camera up (which follows a roll