Appearance
Anim v0.1: Portable Animation Engine
Status: approved foundation plan
Summary
Build anim as a Rust library and headless CLI that compiles statically typed .anim programs into deterministic 2D/2.5D scenes, raster/vector frames, mixed audio, and AV1/Opus WebM.
The architecture separates language evaluation, scene representation, rendering, and media encoding so future codecs, renderers, abstractions, and browser runtimes do not alter DSL semantics.
Core Architecture and Language
- Create a modular Cargo workspace for syntax/compiler, engine/scene/audio, CPU and GPU renderers, media codecs, and CLI. The compiler and engine core must build for
wasm32-unknown-unknownwithout filesystem, threads, GPU, or codec dependencies. - Use a lossless CST followed by typed HIR and optimized evaluation IR. Keep a portable evaluator and allow host-only backends to compile supported monomorphic IR to machine code; constant-fold static work and memoize scene subtrees that do not depend on time or changed inputs.
- Give
.anima Typst-inspired expression syntax with inferred static types, immutable values, functions, named arguments, modules, records, collections, generics, and dimensional units such aspx,deg,s,ms, andfps. - Require an exported
main: Composition. A composition defines exact dimensions, rational duration, rational frame rate/timebase, visual content, and an optional audio timeline. - Represent animated values as
Signal<T>, a pure function of exact rational time. Providetween,keyframes, easing, sequence, stagger, repeat, map, and signal-combination constructs. Properties accept constants or compatible signals. - Keep DSL evaluation referentially transparent. Do not expose mutable bindings, collections, objects, hidden random cursors, ambient inputs, or source-callable IO. Treat parameters, exact sample time, assets, imported data, seeds, and event streams as typed immutable inputs.
- Represent sequential work as a deterministic pure
Simulation<State, Input>fold/scan with a fixed rational tick, explicit seed, checkpoints, and replay rules. Use this for particles without exposing a mutable simulation object or making targeted frames depend on prior frame evaluation. - Resolve assets and other host operations through engine-managed typed requests/results, capability checks, resource limits, and structured errors. The compiler records their source-aware input dependencies; private caches and implementation mutation remain permitted when unobservable.
- Make functions and immutable scene values the primary abstraction mechanism. Keep tracks, clips, transitions, layout helpers, and higher-level animation systems in standard-library modules rather than privileged syntax.
- Emit source-spanned diagnostics for parsing, type/unit errors, dependency cycles, invalid frame conversions, incompatible path morphs, missing assets, and unsupported export features.
Scene, Rendering, and Media
- Define a renderer-neutral retained scene IR containing groups, planar layers, paths, glyph runs, images, gradients, transforms, clips, masks, opacity, blend modes, filters, and cameras.
- Support 2.5D through planar layers, 4×4 transforms, perspective projection, explicit depth/painter ordering, and camera transforms. Admit only bounded flat-color 2D deformation meshes under ADR 0028; exclude 3D/general meshes, lights, intersecting geometry, and general depth-buffer rendering.
- Include rectangles, ellipses, Bézier paths, fills/strokes/dashes, path trim, compatible-contour morphing, text-on-path, images, masks, blur, shadows, color matrices, gradients, and deterministic particles.
- Use linearized sRGB with premultiplied alpha as the sole working color space. The canonical CPU path uses controlled rounding/fixed-point coverage and compositing kernels so pinned builds produce identical pixels across supported architectures. Export 8-bit sRGB PNG and BT.709-tagged 8-bit video; defer wide gamut, HDR, and 10-bit output.
- Build the CPU renderer as the reference implementation, using project-controlled deterministic raster/compositing kernels and font outlines rather than platform drawing APIs. Golden output from this backend defines correctness.
- Build an optional
wgpu/Vello-style GPU render graph for batching, path rendering, masks, filters, and compositing. GPU results use pixel-tolerance tests; an unsupported GPU operation falls back for the entire frame to CPU and is reported byinspect. - Shape and lay out text with the pure-Rust Fontations/HarfRust stack, Unicode bidi and line-breaking rules, variable-font axes, fallback chains, and text-on-path. Bundled fonts are preferred; system fonts are allowed for development but must be hash-pinned for locked renders.
- Import PNG, JPEG, WebP, SVG, OpenType fonts, WAV, FLAC, MP3, Ogg/Vorbis, Ogg/Opus, JSON, and CSV. Defer video input.
- Export:
- Single PNG frames and numbered PNG sequences.
- One SVG per sampled frame, with text outlined by default.
- AV1/Opus WebM through streaming frame/audio sinks.
- Raw premultiplied RGBA frames and 48 kHz PCM through the Rust API.
- Make SVG export fail with source-linked diagnostics for unsupported subtrees. Permit fallback only through explicit
rasterize(node, resolution:)or the equivalent CLI option. - Decode audio to a canonical 48 kHz timeline. Support offset, trim, gain automation, fades, mono/stereo conversion, pan, resampling, mixing, and muxing. Use deterministic fixed-point mixing with 64-bit accumulation; exclude synthesis and general DSP effects.
- Isolate codecs behind internal traits. Use rav1e for AV1, ropus for fixed-point Opus, and a pure-Rust WebM muxer such as oxideav-mkv. Pin exact versions and validate their output independently because the container ecosystem is still young.
- Stream evaluation → rendering → encoding with bounded, ordered frame queues. Parallelize independent frames and asset preparation without retaining the complete animation in memory. Permit bounded, discardable previous-frame caches and baseline/incremental kernel racing without making targeted frames depend on render history.
Public Interfaces and Workflow
- Expose Rust types including
Engine,CompiledProject,RenderSession,RenderRequest,Composition,Scene,AudioTimeline,Time,Duration,FrameRate,FrameIndex,Frame, andDiagnostic. - Provide:
Engine::compile(ProjectInput, CompileOptions) -> CompiledProject.- Composition discovery and typed parameter inspection.
- Targeted frame evaluation and rendering.
- Streaming range rendering into image, audio, or media sinks.
- Experimental
AssetProvider,Renderer,ImageEncoder,VideoEncoder, andMuxertraits.
- Treat all Rust and DSL interfaces as versioned experimental 0.x APIs. Store a language/IR version in compiled artifacts and provide migration diagnostics after intentional breaks.
- CLI commands:
anim check <input>for compilation and capability validation.anim inspect <input> [--json]for compositions, parameters, assets, timing, backend support, and cache dependencies.anim render -i <input> -o <output>with--frame, half-open--range,--backend cpu|gpu,--reproducible,--locked, quality, seed, and worker controls.anim watch <input>to invalidate affected modules/assets and rerender a selected frame or range.anim lock <input>to hash transitive sources, assets, fonts, and dependencies.anim initfor an optional project manifest and starter composition.
- Support standalone
.animfiles using their containing directory as the local read root. An optionalanim.tomldefines entrypoint, defaults, asset roots, font aliases, path or pinned-Git dependencies, and capabilities.anim.lockrecords revisions and content hashes. - Provide no ambient clock, environment, subprocess, or network access to DSL programs. Locked mode rejects undeclared files, unpinned system fonts, changed hashes, floating dependency revisions, and missing lock entries.
- Keep host effects outside ordinary DSL evaluation. Module/asset resolution, decoding, rendering, encoding, output writes, progress, and cancellation run through explicit engine phases; equivalent immutable inputs must produce equivalent compiled and rendered values regardless of evaluation history.
- Keep implementation source, tests, generated browser/runtime bundles, and native binaries proprietary. License authored documentation and examples under MIT through explicit nested license boundaries.
Delivery Sequence
- Establish workspace boundaries, core value/time types, diagnostic conventions, manifest/lock formats, conformance fixtures, and codec/rendering feasibility spikes.
- Implement parser, type checker, module graph, typed optimized evaluation IR, portable evaluation, measured host-native acceleration, standard animation primitives,
check, andinspect. - Implement scene IR, typography, assets, deterministic CPU rendering, PNG/SVG export, targeted rendering, and golden examples.
- Implement exact audio timelines, decoding/mixing, AV1/Opus encoding, WebM muxing, streaming scheduling, and reproducible media mode.
- Implement the GPU backend, incremental watch workflow, cache instrumentation, benchmarks, documentation, language reference, and cross-platform release packaging.
Release v0.1 only when one representative project with Unicode title animation, SVG artwork, path motion, masks/effects, deterministic particles, imported data, and synchronized mixed audio renders successfully to PNG sequence, SVG frames, and browser-playable WebM.
Test and Acceptance Plan
- Parser/type-system snapshots, malformed-input recovery, unit checking, module cycles, exact rational timing, non-integer rates such as 24000/1001, and zero-drift frame/sample conversion.
- Cross-platform canonical pixel and PCM goldens on Linux, macOS, and Windows, including x86-64 and ARM64.
--reproduciblealso pins encoder configuration, threading, timestamps, metadata ordering, and CPU feature paths. - Typography fixtures for Arabic, Indic scripts, CJK, mixed bidi text, combining marks, emoji fallback, variable fonts, line wrapping, and text-on-path.
- Scene tests for clipping, masks, blend modes, perspective projection, filter boundaries, path morph validation, transparency, particles, and SVG unsupported-feature failures.
- Independent WebM validation and playback tests in Chromium and Firefox, plus AV1/Opus decode comparisons, duration checks, A/V synchronization, odd dimensions, empty audio, and partial final frames.
- Capability tests proving undeclared reads, network, environment, clock, and lockfile drift are rejected.
- Incremental tests proving changes invalidate only dependent modules, signals, assets, and frames.
- Performance gates on a documented reference machine:
- Warm
checkor targeted-frame rebuild under 200 ms for the standard 50-module/1,000-node project. - GPU rendering at 1080p60 for the standard 10,000-path/1,000-glyph scene without heavy filters.
- At least 70% parallel efficiency from one to eight CPU workers on the independent-frame benchmark.
- Default in-flight memory bounded to twice the configured worker count.
- Warm
- Fuzz the parser, evaluator, path handling, font ingestion, image/audio decoders, and container writer; run sanitizers/Miri where supported.
Assumptions and Deferred Work
- “Pure Rust codecs” means the canonical/reproducible media backend has no FFmpeg runtime, subprocesses, or C codec linkage; an explicitly requested, non-reproducible CLI export may use a locally installed FFmpeg as decided in ADR 0027. Optional architecture-specific assembly inside pinned Rust crates may be disabled by reproducible mode.
- Browser v1 means browser-compatible output and a WASM-ready core, not an in-browser renderer or preview application.
- Deferred beyond v0.1: H.264/MP4, video input, HDR/wide gamut, native 3D, interactive editor/player, remote package registry, network data sources, WASM/native plugin ABI, shader DSL, and full audio DSP.