open-standards/wai/extensions/linear-film.md
Transaction Science cd6dcf41bb SPEC §7 Signatures: the one Ed25519 rule, cited by the receipt extensions
Every Ed25519 signature WAI defines is verified strictly, in order: a canonical
key, a key not of small order, S < L, R not of small order, and the cofactorless
equation with R compared byte for byte. OER/2's chain signature keeps the rule
oer-receipts §5 settles. jwp-receipts §3.1 step 3, http-delivery's playlist
receipt and packaging's receipt cite it instead of restating it; the worlds,
avatars, feeds, spatial-audio, haptics, volumetric, score, film, provenance,
ternary and quantum receipt texts cite it where they name their signature.
CHANGELOG records the sweep.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 14:39:04 -04:00

189 lines
9 KiB
Markdown

# 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."