Docs

Cameras in 3D

Create named cameras in the same space as your artwork. One camera supplies the view at a time. Move it to reveal depth, change its lens to reframe, or blend to another moving camera without rendering two images.

@version 5
size: (1920, 1080)
fill: "#101522"

imagine lens
  camera: perspective
  focalLength: 50
  sensorHeight: 24
  near: 1
  far: 10000

imagine headline
  text: "A new perspective."
  fontSize: 100
  fill: "#ffffff"

create headline as title
create lens as wide
  active: true
  position: (0, 0, 2250)
  lookAt: title
  animate active
    0s: true
    2s: false
    4s: true

create lens as detail
  active: false
  position: (300, 120, 900)
  lookAt: title.glyph(-1)
  animate position.z from 900 to 650 over 5s
    ease: "ease-in-out"
  animate active
    0s: false
    2s: true
    4s: false
  animate pose from wide to detail over 0.6s
    delay: 2s
    ease: "ease-in-out"

Pose and lens

Coordinates are scene units, with Y up. A camera with zero rotation looks along its local negative Z axis. Rotation uses the same clockwise XYZ degrees as artwork. Parent a camera under a group to build an orbit or travelling rig. Parent scale affects position; orientation follows the rig's rigid rotations and lens values stay unchanged. Zero or nonfinite scale is invalid.

Artwork parented to a camera rides its full orientation, including lookAt, up and roll. A layer at local (0, 0, -1650) stays centred in the view while the camera aims around. Glyph targets resolve during rendering, so children of a camera aimed at name.glyph(index) aim at that text instance's anchor instead.

Choose free rotation, or lookAt with optional up and roll. Do not combine both controls. lookAt accepts a world (x, y, z) point, a unique instance name, or name.glyph(index). Negative glyph indices count from the last shaped glyph. An instance target follows its world anchor. A glyph target follows its current ink centre, including glyph animation. Whitespace has no ink target. Hidden and offscreen targets still prepare their layout. Missing or ambiguous targets, self/dependent camera targets and coincident camera/target positions are errors. At an up-vector pole, orientation uses a deterministic world-axis fallback.

targetOffset is an XYZ vector in target-local units for instance/glyph targets, or world units for point targets. up defaults to (0, 1, 0) in world space. roll rotates clockwise around the target direction, in degrees.

Property Perspective Orthographic
camera perspective orthographic
Lens focalLength: 50, sensorHeight: 24 in millimetres, or vertical fov in degrees orthoHeight, default scene height
near / far 1 / 10000 1 / 10000

Lens and clipping values can animate. Use fov or focalLength, never both. All lengths must be finite and positive; 0 < fov < 180 and near < far. Projection is fixed for each camera. Without an authored position or rig transform, the default camera frames the scene at Z=0 using its numeric rest lens values: a longer lens stands further back, so two unpositioned cameras with different focal lengths show the Z=0 plane at the same size and differ only in depth. Give each camera a position when a lens change should reframe. Lens animation does not implicitly move the camera. Set position explicitly when driving the base lens with an expression. Higher export pixel density does not change the view. Aspect ratio determines horizontal extent. There is no depth of field.

Activation and ordinary animation

active is an ordinary boolean property on a camera. Exactly one camera must resolve to true at the sampled scene time. Multiple active cameras, no active camera, and nonboolean values produce diagnostics. A single camera is inferred only when active is unspecified; explicit false never falls back to true. Blueprint activation is inherited normally, so activating a shared blueprint can create a conflict between its instances. Visibility does not select a view.

Animate active with ordinary keys or expressions. Boolean keys switch at their exact time, independently of easing overshoot or hold. Delay, repeat, direction and speed use the existing animation engine. Activation and pose tracks use exact scene time even when artwork has reduced FPS. Normal position/lens tracks retain their existing FPS sampling. All cameras resolve before selecting the view, so a simultaneous off/on change is atomic and backward seeks are deterministic.

Camera transforms and lenses use the existing animation blend modes. For example, an additive position animation can layer shake over a travelling rig. To move between complete live camera views, animate pose between camera names as in the example above. The same engine owns keyframe interpolation, easing, delay, speed, repetition and composition. Use its existing blend: mix and weight options to layer a pose animation over the camera's base view. pose itself can reference a camera as that base view. Complete poses support replace and mix; apply add and multiply to individual numeric channels.

Pose references read each endpoint's current authored world transform, target and lens, including animation. They do not recursively follow the endpoint's own pose track. Self references therefore mean the camera's authored pose, not a dependency cycle. A pose track changes the effective view; it does not move scene nodes or artwork parented to the endpoint cameras.

The value interpolator uses shortest-path quaternion rotation, interpolated position/lens/clipping values and bounded progress. Only matching projections can interpolate. A held pose key or an active switch can cut between projection types. One view is rendered throughout; this is not an image crossfade.

Camera animation ends contribute to ordinary scene duration. References bind to instance identity and save using current names.

Surfaces, effects and output

Opaque artwork writes surface depth, including text, strokes, images and copies. Antialiased edges and translucent paint test depth but do not write solid depth. Coplanar surfaces use authored layer order without changing world positions. Artwork remains double-sided. Unfiltered groups keep their children's depths.

Fully covered pixels resolve by depth whatever the draw order, so text or a group with opacity below 1 on a turned opaque card keeps its antialiased edges. Transparent surfaces and antialiased edges blend back to front over them. Intersecting translucent surfaces can have ordering artifacts. An isolated filtered group is one composition unit: its original surface depth survives the color filters, while added halos do not write solid depth. Interleaving translucent surfaces across filtered groups is approximate. Screen-space distortions do not create new physical geometry. Root effects apply after composition.

A filtered group's effect covers its projected on-screen bounds plus the effect's own padding, as in 2D. Effect sizes such as dotSize, cellSize or a blur radius are scene units measured at the group's depth, so a halftone dot shrinks with the poster as it moves away. Root effects measure sizes in stage units: size spans the output, like a 2D scene. Content whose painted area is only known on the GPU (copies, animated glyphs) or that crosses the near plane processes the whole view.

Root fill is the output background. It does not rotate as a finite panel. Nested frames remain world-space clipping geometry. Keep essential content between the near and far planes; inspect reports partial clipping for near-plane crossings.

Tools and compatibility

Files from format 4 retain their host view when opened. Older editors that support only format 4 cannot open files saved as format 5. Editing navigation keeps its own orthographic camera; playback and exports use the authored view. This release adds no visual camera panels or gizmos.