Appearance
Language and CLI reference
A concise reference for the common Anim syntax, scene constructors, collection functions, CLI subcommands, and diagnostic codes used throughout these docs.
1. Lexicon, Literals & Dimensional Units
1.1 Identifiers & Comments
- Identifiers: Match
[a-zA-Z_][a-zA-Z0-9_]*. - Line Comments:
// comment until end of line - Block Comments:
/* multi-line block comment */ - Reserved words:
export,let,param,fn,if,else,true,false, and every builtin that uses keyword syntax (composition,rect,step,stack,translate,clip,tween,opacity,repeat,cubic_bezier,mask,sequence,ellipse,scale,rounded_rect,rotate,triangle,map,range,flat_map,filter,fold,scan,zip,enumerate,reverse,sort_by). A reserved word cannot be a binding name or a record field name.
1.2 Dimensional Unit Literals
Anim requires explicit dimensional units for physical domain types:
| Unit | Category | Description | Example |
|---|---|---|---|
px | Length / Subpixel | Screen space coordinates and dimensions | 1920px, -45.5px |
s | Time | Timeline seconds (rational fraction) | 2s, 0.5s |
ms | Time | Timeline milliseconds | 500ms, 16.66ms |
fps | Frame Rate | Presentation frequency | 60fps, 120fps |
deg | Angle | Rotation angles in degrees | 90deg, -180deg |
| unitless | Scalar / Ratio | Pure numbers, opacity, scale factors | 1.0, 0.5, 42 |
1.3 Text Literals
Double-quoted text names host-supplied assets. \" and \\ are the only escapes, and a literal may not span lines. Text has no arithmetic or concatenation; it exists to address declared inputs.
"ball.png","sprites/frame.png"
1.4 Hexadecimal Color Literals
Source colors use straight 8-bit sRGB channels. The CPU renderer converts them to its linear-light premultiplied working representation during compositing.
#RRGGBB(e.g.#1e293b, with implicit full opacity)#RRGGBBAA(e.g.#1e293b80)
2. Expressions & Primitive Constructs
2.1 Bindings & Functions
anim
// Variable binding
let width = 800px;
// Named pure function
let add(a, b) = a + b;
// Anonymous callable
let double = fn(x) => x * 2;
// Block body: name intermediate values before the result expression.
// `{` opens a block only when it is followed by `let`; every other `{` is a
// record literal. Locals bind values, evaluate in order, and may shadow
// outward. See docs/specs/0047-local-binding-blocks.md.
let badge(size) = {
let half = size / 2;
let inset = half / 2;
rect(x: inset, y: inset, width: half, height: half, fill: #ffffff)
};
// Public parameter: a constant a host may replace at compile time
param accent = #f472b6;
param scale_factor = { default: 1.0, min: 0.5, max: 2.0, increment: 0.05,
label: "Scale" };
// Exported composition entrypoint
export let main = composition( ... );2.1.1 Public Parameters
A param declares a constant the host may replace, so one source can drive a slider or a colour picker without being edited. See docs/specs/0051-host-overridable-parameters.md.
- The right-hand side is a literal, a negated literal, or a record of literals holding
defaultand optionallymin,max,increment, andlabel. It may not reference another binding, which is what lets a host list the parameters without evaluating anything. - Types are
Scalar,Length,Angle,Duration,Boolean, andColor, inferred fromdefault.min,max, andincrementmust share that type and are not accepted onBooleanorColor.TextandFrameRateare not overridable. - Parameters share the top-level namespace with
letand may be referenced in any order. A parameter cannot be exported. - A supplied value outside the declared range is rejected, never clamped.
- A
LengthorDurationparameter may feedcomposition, so an override can change the header a host must then re-read.
2.2 Control Flow & Records
anim
// Branching expression
let bg = if is_dark { #000000 } else { #ffffff };
// Record literal & field access
let point = {x: 10px, y: 20px};
let px = point.x;3. Built-in Scene Node Constructors
3.1 rect
Creates an axis-aligned rectangle primitive.
anim
rect(
x: Length,
y: Length,
width: Length,
height: Length,
fill: Color
) -> Scene3.2 ellipse
Creates a filled ellipse bounded by specified coordinates and dimensions.
anim
ellipse(
x: Length,
y: Length,
width: Length,
height: Length,
fill: Color
) -> Scene3.3 rounded_rect
Creates a rectangle with rounded corners (rx, ry corner radii).
anim
rounded_rect(
x: Length,
y: Length,
width: Length,
height: Length,
rx: Length,
ry: Length,
fill: Color
) -> Scene3.4 triangle
Creates a filled 2D triangle from three vertices.
anim
triangle(
x1: Length, y1: Length,
x2: Length, y2: Length,
x3: Length, y3: Length,
fill: Color
) -> Scene3.5 image
Draws a decoded raster asset into a whole-pixel destination rectangle.
anim
image(
src: Text,
x: Length, y: Length,
width: Length, height: Length,
fit: Text, // optional: "fill" (default) | "contain" | "cover"
smoothing: Text // optional: "linear" (default) | "nearest"
) -> Scenesrc names an asset the host resolved before compiling. The CLI resolves each name relative to the directory holding the .anim file; a name may not be absolute or escape that directory. PNG with straight alpha is the supported input format.
An image composes exactly like the vector primitives: put it in a stack, wrap it in translate, scale, or rotate, and animate those wrappers with any signal. Rotation and general affines resample through an exact inverse map, and destination edges antialias on the same lattice the vector shapes use.
fit: "contain"preserves the source aspect ratio inside the box and centres the result.fit: "cover"preserves the aspect ratio, fills the box, and clips the overflow to the requested rectangle.smoothing: "nearest"keeps hard texel edges for pixel art.
anim
let sprite = translate(x: 160px, y: 48px,
scene: rotate(angle: swing,
scene: image(src: "ball.png", x: -22px, y: 0px, width: 44px, height: 44px)));3.6 stack
Composites a list of child scenes in painter's order (first index at back, last index on top).
anim
stack(children: List<Scene>) -> Scene3.7 translate
Applies continuous subpixel 2D offset to a target scene.
anim
translate(x: Length | Signal<Length>, y: Length | Signal<Length>, scene: Scene | Signal<Scene>) -> Scene | Signal<Scene>scene may itself be Signal<Scene>. If any accepted input is a signal, the result is Signal<Scene>.
3.8 scale
Applies axis-aligned scaling around local origin (0, 0).
anim
scale(x: Scalar | Signal<Scalar>, y: Scalar | Signal<Scalar>, scene: Scene | Signal<Scene>) -> Scene | Signal<Scene>3.9 rotate
Applies clockwise rotation around local origin (0, 0).
anim
rotate(angle: Angle | Signal<Angle>, scene: Scene | Signal<Scene>) -> Scene | Signal<Scene>3.10 opacity
Applies uniform alpha multiplier (0.0 to 1.0) to a target scene hierarchy.
anim
opacity(value: Scalar | Signal<Scalar>, scene: Scene | Signal<Scene>) -> Scene | Signal<Scene>3.11 clip
Clips a scene subtree to an axis-aligned rectangular boundary.
anim
clip(x: Length, y: Length, width: Length, height: Length, scene: Scene | Signal<Scene>) -> Scene | Signal<Scene>3.12 mask
Masks content using the retained alpha coverage of another scene.
anim
mask(alpha: Scene | Signal<Scene>, content: Scene | Signal<Scene>) -> Scene | Signal<Scene>4. Signal & Timeline Constructors
4.1 step
Creates a discrete signal that switches value at exact time at. Both branches observe composition time; only the selection changes. T is Scene, Length, Scalar, or Angle, and constants are lifted, so step is also the shortest way to promote a static value into a signal.
anim
step(before: T | Signal<T>, after: T | Signal<T>, at: Time) -> Signal<T>4.2 tween
Creates a continuous interpolation signal between from and to over time interval [start, end].
anim
tween(
from: Length | Scalar | Angle,
to: Length | Scalar | Angle,
start: Time,
end: Time,
easing: Easing (optional)
) -> Signal<Length | Scalar | Angle>Both endpoints must have the same type. The signal clamps to from before start and to to after end, so a stage that outlives its tween rests on the endpoint — that is how a hold is written.
4.3 keyframes
Builds a scalar, length, angle, point, or compatible path signal from strictly increasing exact times. Values hold before the first frame and after the last.
anim
let ease = cubic_bezier(x1: 0.2, y1: 0.8, x2: 0.3, y2: 1);
let turn = keyframes(frames: [
{time: 0s, value: -20deg, easing: ease, hold: false},
{time: 1s, value: 20deg, easing: ease, hold: false}
]);4.4 sequence
Concatenates two signals sequentially at exact timestamp at.
anim
sequence(first: Signal<T>, second: Signal<T>, at: Time) -> Signal<T>T is Length, Scalar, Angle, or Scene. The second branch restarts local time at zero, which is the only difference from step. Constants are not lifted. Longer timelines nest to the right.
4.5 repeat
Repeats a finite time signal periodically every duration every.
anim
repeat(signal: Signal<T>, every: Duration) -> Signal<T>T is Length, Scalar, Angle, or Scene. The child observes local time t - floor(t / every) * every.
4.6 cubic_bezier
Defines a cubic Bézier velocity/easing curve with control points ((x_1, y_1)) and ((x_2, y_2)).
anim
cubic_bezier(x1: Scalar, y1: Scalar, x2: Scalar, y2: Scalar) -> Easing5. Exact Rounding
Quantize an exact constant so it satisfies a whole-pixel geometry field. round is nearest with half ties away from zero, matching the sampled Q16 boundary rule.
anim
round(value: Scalar | Length) -> Scalar | Length
floor(value: Scalar | Length) -> Scalar | Length
ceil(value: Scalar | Length) -> Scalar | Lengthanim
let w = 45px;
rounded_rect(
x: 0px - round(value: w / 2), y: 0px,
width: round(value: w), height: ceil(value: w * 0.66),
rx: round(value: w * 0.1), ry: round(value: w * 0.1),
fill: #ffffff,
)These are identifier builtins, so a user binding of the same name shadows them. Signals cannot be rounded. See docs/specs/0049-exact-rounding-builtins.md.
6. Collection Operations API
| Function Signature | Description |
|---|---|
range(from: Scalar, to: Scalar) -> List<Scalar> | Generates a whole-scalar range [from, to) |
map(items: List<T>, transform: fn(T) => U) -> List<U> | Maps elements via a transformation |
flat_map(items: List<T>, transform: fn(T) => List<U>) -> List<U> | Maps and flattens lists |
filter(items: List<T>, predicate: fn(T) => Bool) -> List<T> | Keeps matching elements |
fold(items: List<T>, initial: U, combine: fn(U, T) => U) -> U | Left-fold reduction |
scan(items: List<T>, initial: U, combine: fn(U, T) => U) -> List<U> | Returns the initial and every accumulated state |
zip(left: List<T>, right: List<U>) -> List<{first: T, second: U}> | Pairs items through the shorter input |
enumerate(items: List<T>) -> List<{index: Scalar, value: T}> | Pairs items with zero-based indices |
reverse(items: List<T>) -> List<T> | Reverses list order |
sort_by(items: List<T>, key: fn(T) => Key) -> List<T> | Stable-sorts by an exact quantity key |
7. CLI Command Specification
anim check <INPUT>
Parses, type-checks, and validates an .anim file without rendering.
--set <NAME=VALUE>: Override one declaredparam; repeatable.- Exit Code 0: Valid file.
- Exit Code 1: Source diagnostics reported to stderr.
- Exit Code 2: An operational error, such as an unreadable input.
anim inspect <INPUT> [--json]
Prints composition names, dimensions, frame rates, durations, frame counts, and any parameters the source declares with their effective values.
--json: Output machine-readable JSON format (schema version 2).--set <NAME=VALUE>: Override one declaredparam; repeatable.
anim render <INPUT> -o <OUTPUT>
Renders target frames, sequences, or video.
--frame <N>: Render exact zero-based frame index (N).--range <A..B>: Render half-open frame range (A \dots B-1).--force: Overwrite existing output files.--video <raster|vector>: Specify video rendering pipeline.--video-codec <CODEC>: Set FFmpeg codec (e.g.libx264,libvpx-vp9).--background <#HEX>: Opaque video background color (default#ffffff).--ffmpeg <PATH>: Explicit path toffmpegbinary.--set <NAME=VALUE>: Override one declaredparam; repeatable. The value is written in ordinary literal syntax, such as--set width=180px,--set tilt=-30deg, or--set tint=#09131f80.
anim import-lottie <INPUT.json> -o <OUTPUT.anim> [--force]
Converts a Lottie JSON document to standalone .anim source code.
8. Selected Compiler Diagnostic Codes
| Code | Category | Summary Description |
|---|---|---|
A0001 | Lexer | Unrecognized source character |
A0100 | Syntax | Unexpected syntax token |
A0205 | Composition | Dimension is nonpositive or not a whole pixel |
A0206 | Composition | Duration is nonpositive |
A0207 | Composition | Frame rate is nonpositive |
A0400 | Type Checking | Unresolved identifier |
A0402 | Type Checking | Dependency cycle detected |
A0403 | Type Checking | Type mismatch in expression |
A0405 | Evaluator | Division by zero in constant expression |
A0504 | Scene | Rectangle dimension is nonpositive |
A0603 | Signal | Negative switch time in step signal |
A1103 | Signal | Invalid tween time interval |
A1203 | Scene | Opacity value out of [0.0, 1.0] range |
A1703 | Scene | Ellipse geometry is not whole pixels |
A1903 | Scene | Rounded-rectangle geometry is not whole pixels |
A2700 | Syntax | Block has no result expression |
A2701 | Scoping | Duplicate local binding in one block |
A2702 | Scoping | Local binding declares parameters |
A2800 | Builtin | Invalid field on round, floor, or ceil |
A3000 | Parameter | Declaration is not a literal or a record of literals |
A3001 | Parameter | Spec field value is not a literal |
A3002 | Parameter | Record spec has no default field |
A3003 | Parameter | Unknown spec field |
A3005 | Parameter | Bound does not have the type of default |
A3007 | Parameter | min is greater than max |
A3008 | Parameter | default is outside the declared range |
A3011 | Parameter | Declaration is exported |
A3012 | Parameter | Override names an undeclared parameter |
A3013 | Parameter | Override does not have the declared type |
A3014 | Parameter | Override is outside the declared range |
A3015 | Parameter | Declared type cannot be supplied by a host |