# WAI Extension: Linear Film > Status: Draft. "A film is a sealed, camera-on-rails world." Ship a > feature not as pixels but as the **world** that generates it (a sealed > `wai.world.replay`) plus a **camera track** and a **frame schedule**; > the sink renders the frames itself, at its own resolution, frame rate, > and quality. Conformance is **film-equivalence**. Reference impl: the > `film` feature of `wai-rs`; corpus: `film-conformance/`. Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174. ## 1. Scope and model A `wai.film.linear` object is a **linear (non-interactive) film** composed of three parts: 1. an embedded **sealed `wai.world.replay`** — the world that produces the imagery, carried verbatim (kilobytes of signed CausalOps + a seal); 2. a **camera track** — a keyframed camera pose (position, orientation, field of view) over the world's tick timeline; and 3. a **frame schedule** — which world tick each frame samples. The sink renders the frames. The wire carries the *instructions* that generate the film (the world + the camera), not the rendered result — lever 3 (instructions-at-the-sink) applied to the largest captured-as-pixels category there is. A two-hour feature generated from a world is the world's op log plus a camera spline, together with the assets the world references (meshes, splats, textures, audio), which travel as their own content-addressed capabilities and can be large; the pixels are reconstituted at the sink's native resolution and frame rate. A film captured from a camera has no world to replay and is not this capability. In scope: the container, the camera-track model, the frame schedule, the deterministic **film-equivalence** conformance criterion, and the receipt profile. Out of scope: a transport; the **render** — projection, rasterization, shading, lighting, the actual pixels — which is the sink's, exactly as the worlds renderer and the audio-scene HRTF are; encoding a *captured* film (live-action photography is irreducible samples — that is `wai.video.*`, not this). This extension carries and verifies a film that is *generated* from a world, it does not fit a world to existing footage. Relationship to the core spec: it **composes** the interactive-worlds extension. The embedded world's **replay-equivalence** (every sink agrees on the state hash at every tick) is reused unchanged; the camera track runs in the same `wai.det.fixed64` floor as every other cargo class. `media` takes the informational value `"video"`; `wai.film.linear` is authoritative. ## 2. Container (`WFLM`) ``` "WFLM" | u16 n_sections | section table (u8 kind, u32 off, u32 len) | sections ``` | kind | section | required | |---|---|---| | `0x01` | contract (canonical JSON) | REQUIRED | | `0x02` | embedded sealed world (a `WWLD` container, verbatim) | REQUIRED | | `0x03` | camera track | REQUIRED | ### 2.1 Contract ```json { "frames": 9, "numeric": "wai.det.fixed64", "start_tick": 0, "ticks_per_frame": 5 } ``` `frames` ≥ 1 and `ticks_per_frame` ≥ 1. Frame `f` samples the world at tick `start_tick + f * ticks_per_frame`. The map is exact and resolution-free: a sink that wants smoother motion renders *between* these sampled states, but the verified quantity is the schedule above. `fps` is **not** in the contract — it is playback speed (presentation), not part of the film's identity. ### 2.2 Embedded world The exact bytes of a sealed `wai.world.replay` (`WWLD`) container. The film is content-addressed over these bytes; the world's own seal and replay-equivalence are inherited unchanged. ### 2.3 Camera track `n_keys u32`, then per key `tick u64 | pos[3] | rot[4] | fov`, the 8 attributes as `i64` Fx in that field order (72 bytes/key). Keys MUST be strictly ascending by `tick`; the track MUST carry at least one key. `rot` is a quaternion `(x, y, z, w)`. ## 3. Reconstruct contract For each frame `f` at tick `t = start_tick + f * ticks_per_frame`: 1. **World state** — the embedded world's state hash at tick `t`, the same replay-equivalent hash a full replay computes at `t` (the sink samples the timeline at frame ticks; `t` MUST be ≤ the world's sealed final tick). 2. **Camera pose** — each of the 8 camera attributes is the `Fx` linear interpolation of the track's keys at `t`; before the first / after the last key the endpoint holds. The floor stays trig-free — the quaternion interpolates component-wise (the sink renormalizes for rendering, which is presentation, exactly as in the splat/skeletal tracks). ## 4. Conformance — film-equivalence > Given the same `wai.film.linear`, a conforming sink MUST compute the > identical per-frame identity for every frame, hence the identical > BLAKE3 — on every machine, no tolerance parameter. ``` frame_hash = BLAKE3("wai:film-frame\x01" || frame_le || world_state_hash(frame_tick) || camera_pose) film_hash = BLAKE3("wai:film\x01" || contract_bytes || world_bytes || camera) ``` `camera_pose` is the canonical `pos[3] | rot[4] | fov` (raw `Fx` bits, little-endian). `frame_hash` is the per-frame reconstruction block; `film_hash` is the frame-independent identity binding the exact inputs. A sink MUST first validate the embedded world as a conformant sealed replay (its seal verifies and the log re-derives the sealed final hash). ## 5. Fidelity and size (honest) `wai.film.linear` is tiered **Lossless**: film-equivalence reproduces the per-frame `(world state, camera pose)` identity exactly, the simulation analog of lossless, like the world it is built on. The **size** win is real and large — a generated film travels as its world + camera (kilobytes in the conformance corpus) instead of an encoded pixel stream — but it applies **only to films that are generated from a world** (animation, CGI, motion graphics, a sealed simulation). It makes **no** claim about live-action capture, which is irreducible samples and belongs to `wai.video.*`. The honest scope: this is the medium-shift lever for the synthesizable slice of "video," not a video codec. ## 6. Receipt (JWP profile) `film_hash` is the identity; the group's objects are the per-frame reconstruction blocks (`frame_hash` per frame), Merkle-bound; one Ed25519 signature (verified as SPEC §7 *Signatures* requires) over film_hash + root + `work` (= frames) + carried `joules_micro` (the native `wai_meter` supplies a **measured** value); `parent_receipt_hash` chains — the worlds / volumetric receipt shape reused. The figure's acquisition class rides beside it as an optional signed label ([energy-measurement](energy-measurement.md) §2.8): absent, the receipt keeps its legacy bytes and the figure is unlabelled; present, it is inside the signature. The reference meter labels every figure it measures `OnChipCounter`, with its declared uncertainty. A reel's receipt is the same profile. ## 7. Reel — a film with cuts (`wai.film.reel`) A [`wai.film.linear`](#) is a single continuous camera move. A **reel** is the editor's layer over it: one embedded sealed world, and a sequence of **shots** played back to back — each shot its own camera track + frame schedule. Cutting from a wide to a close-up, or showing one beat from two angles, is the essence of film grammar; a reel carries it as a few extra kilobytes of camera keys, the world (the cargo) shared across every shot. Container `WREL`: `0x01` contract (canonical JSON, `{ "numeric", "shots" }`), `0x02` embedded sealed world (`WWLD`), `0x03` shots — `n_shots u32`, then per shot `frames u64 | start_tick u64 | ticks_per_frame u64 | camera track`. Each shot's keys MUST be strictly ascending by tick; a shot's local frame `j` samples world tick `start_tick + j * ticks_per_frame`. Conformance is **reel-equivalence**, film-equivalence extended over cuts: the global frame `f` belongs to a shot, samples the world at that shot's tick, is framed by that shot's camera, and its identity is ``` frame_hash = BLAKE3("wai:reel-frame\x01" || frame_le || world_state_hash(tick) || camera_pose) reel_hash = BLAKE3("wai:reel\x01" || contract || world_bytes || shots) ``` Shots MAY revisit earlier world ticks (a cut back to a moment from a new angle), so the world is sampled at the **sorted, de-duplicated** union of every shot's frame ticks — the convergence guarantee still holds, because the world's replay-equivalence is order-independent. The receipt is the same JWP profile (per-frame Merkle leaves, identity = `reel_hash`); the meter reports measured joules for the whole reel. ## Appendix Film-equivalence is the composition the taxonomy promised: a film is a sealed world (replay-equivalence, already a corpus) read through a camera track (the `wai.det.fixed64` floor, the same one behind every cargo class). The world engine gained one general primitive for it — a sampler that reads the state hash at an arbitrary frame cadence, independent of the world's internal checkpoint cadence — and the film is a thin, exact layer on top. One floor, one more cargo class; the representation is the cheapest possible (the generating world), and the *reconstruction* of every frame is exact and auditable — the honest thing the standard adds to "video."