open-standards/wai/extensions/browser-sink.md
Transaction Science bf7c9fcfee
All checks were successful
WAI operation catalogue / does this change touch it (push) Successful in 17s
wai-fleet service / does this change touch it (push) Successful in 16s
joule-moq-web browser client / joule-moq-web · does this change touch it (push) Successful in 25s
wai-app and Create / wai-app · does this change touch it (push) Successful in 23s
wai-ledger service / does this change touch it (push) Successful in 19s
wai-sink-swift native sink package / does this change touch it (push) Successful in 22s
joule-moq-room room service / joule-moq-room · does this change touch it (push) Successful in 53s
WAI Studio / does this change touch it (push) Successful in 18s
wai-quantum for JavaScript / does this change touch it (push) Successful in 54s
wai-audit collector and player integration / does this change touch it (push) Successful in 57s
joule-moq-remote host agent / joule-moq-remote · does this change touch it (push) Successful in 1m7s
wai-key-proxy / does this change touch it (push) Successful in 57s
wai-quantum studio and desktop / does this change touch it (push) Successful in 55s
wai-sink-android native sink library / does this change touch it (push) Successful in 55s
joule-moq-remote host agent / joule-moq-remote, every target (linux-arm64) (push) Successful in 2m46s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Successful in 5m2s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Successful in 5m5s
joule-moq-room room service / joule-moq-room, every target (push) Successful in 6m41s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 16s
wai-quantum energy-landscape jobs / does this change touch it (push) Successful in 15s
wai-app and Create / wai-app · tests, clippy, the line language's verifier (linux) (push) Successful in 1m2s
wai-fleet service / service · linux (push) Successful in 3m16s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Successful in 1m14s
wai-ledger service / service · linux (push) Successful in 4m28s
wai-audit collector and player integration / collector · linux (push) Successful in 5m1s
WAI operation catalogue / the catalogue and every door · linux (push) Successful in 8m15s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 1m28s
wai-quantum studio and desktop / studio · browser (push) Successful in 5m18s
wai-key-proxy / proxy · linux (push) Successful in 3m1s
wai-quantum studio and desktop / desktop app · windows (push) Successful in 10m1s
wai-quantum for JavaScript / javascript · linux (push) Successful in 7m14s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (push) Successful in 12m23s
wai-fleet service / service · macos (push) Successful in 38s
wai-quantum energy-landscape jobs / native against WebAssembly, over many seeds (push) Successful in 7m35s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 9m15s
wai-quantum energy-landscape jobs / reader, kernels, answers and the stated result · linux, release (push) Successful in 9m29s
wai-quantum studio and desktop / desktop app · linux (push) Successful in 12m24s
WAI Studio / Studio in WebKit · linux (push) Successful in 20m25s
wai-ledger service / service · macos (push) Successful in 1m28s
wai-app and Create / wai-app · Create, browser end-to-end (macos) (push) Successful in 7m54s
wai-quantum energy-landscape jobs / the module under wasm32-wasip2 (wasmtime) (push) Successful in 20m38s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (push) Successful in 15m54s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Successful in 2m9s
joule-moq-remote host agent / joule-moq-remote, every target (macos) (push) Successful in 49s
WAI Studio / Studio in Chromium and Firefox · macos (push) Successful in 10m34s
wai-quantum studio and desktop / desktop app · macos (push) Successful in 4m34s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Successful in 15m59s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Successful in 6m42s
wai-quantum energy-landscape jobs / the same bits · macos (push) Successful in 7m0s
The browser sink presents a graphics component's text floor, with captions as graphics-vector §6.4 has them
browser-sink §14.9: where a device has no decoder of wai.graphics.vector, a composing
page presents each graphics piece's floor (graphics-vector §6.3), the piece's transcript
as wai.text.zstd, as text: the blocks active at the clock's tick, in block order, each
with its language, direction and writing mode. A piece presents its overlay or its
floor, never both.

Before presenting, the page holds the floor object to its piece: its derived_from names
the piece's base (floor_base), its payload is the raw frame and no other Zstandard
frame (floor_frame), its target states the transcript's length and a timebase
(floor_target), and its content is a transcript in graphics-vector §5.4's one encoding
(floor_transcript).

A caption or a subtitle block is never presented from the floor (caption_track): the
text component of its language carries it, and the text layer presents it or not as the
viewer set it. A forced_narrative block is presented from the floor unless the text
layer shows a cue of its language with its string at that millisecond (forced_in_cue),
so the words are on the page once. The text layer presents one text component of a
package, or none.

wai-rs browser_sink::floor is the reference (read, present), which wai-web's compose
bundle calls as TextFloor. Corpus live-conformance/browser-floor/ (49 floors, 25
presentations), written by wai_browser_floor_vectors and regenerating byte for byte on
x86_64 and aarch64; tests/browser_floor.rs also reads the floors wai-pack wrote for
pack-conformance/multi/multi-captions-01, and wai-web's compose::floor_tests holds them
to wai-vector's transcripts. browser_floor_verify.py, the second implementation,
refuses 72 mutated floors each with its defect's reason, fails 23 mutated copies of the
corpus, and each of its 32 checks, dropped, is caught. compose/run.mjs gains case 10.

Not here: a base that is routed and then refused when loaded still refuses the package,
and the overlay path does not yet leave out caption items (graphics-vector §5.7).

Co-Authored-By: Claude <[email protected]>
2026-11-10 22:36:11 -05:00

1143 lines
79 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# WAI Extension: Browser Sink
> Status: Draft. How a sink that runs in a web page answers SPEC §4's "the sink advertises
> `cap`", and what the frames it decodes can be checked against. For each object it is about
> to decode, the page **routes** the object's capability to the user agent's decoder, a native
> host's or WAI's own WebAssembly decoder (§4): the user agent is asked only about codec strings
> the WebCodecs Codec Registry lists, and its decoder is preferred where it is exact for the
> tier the object needs. Among objects that are alternatives of one content, the page decodes
> the one SPEC §4's energy order puts first, inside the routing: a decode routed to the user agent
> that it answers supported, smooth and power-efficient before a software one, and a learned one
> without a sink-metered figure of its decode last (§4.7). The page evaluates the constraint sets of
> [capability-constraints](capability-constraints.md) itself (§5), and reports the joules a
> decode was measured to cost beside the user agent's `powerEfficient` boolean, which stays the
> user agent's word (§6). Every frame the page decodes carries a **content binding** (§7): the
> content hash of the receipted object it was decoded from, the link hash of the group receipt
> that object verified under, and the tier its samples hold. The binding's life through
> `clone()`, transfer, `copyTo()` and re-encode is in §8, its carriage in a page in §9, and the
> state of the frame on screen, which joins the binding to the receipts and to
> [live-provenance](live-provenance.md)'s verdict, in §10. The member a frame carries,
> `contentBinding`, is proposed for the WebCodecs VideoFrame Metadata Registry in
> [`proposals/webcodecs-content-binding.md`](../proposals/webcodecs-content-binding.md), a
> draft that has not been filed; until a registry lists the member, a page carries it in a side
> table (§9.2). No transport, no record, no change to the wire format or to SPEC §4's algorithm.
> The reference implementation is the `browser_sink` module of `wai-rs` (feature
> `browser_sink`), which `wai/wai-live-web` builds for the page; the page's own code is
> `joule-code/joule-code-rs/crates/joule-moq-web/web/room/binding.js`; the corpus is
> [`live-conformance/browser/`](../live-conformance/browser/), and its second implementation is
> `live-conformance/browser_sink_verify.py`. Each takes a `wasm` frame's decoder to be a module
> the page holds at the integer level of the WAISM™ profile (§4.3 step 6, §12).
> Keywords **MUST**, **MUST NOT**, **SHOULD**, **MAY** are RFC 2119/8174.
>
> Texts read for this revision, each pinned to the version named: WebCodecs, W3C Working Draft
> 19 October 2026: the VideoDecoder's *Output VideoFrames* steps, *Create a VideoFrame*, the
> `VideoFrameMetadata` dictionary and `metadata()`, *Copy VideoFrame metadata*, *Clone
> VideoFrame*, the constructors' metadata steps, the serialization and transfer steps, `copyTo()`
> and `EncodedVideoChunk`; the WebCodecs Codec Registry, W3C Registry Draft 24 September 2026,
> its video and audio tables; the WebCodecs VideoFrame Metadata Registry, W3C Registry Draft
> 14 April 2026, its entries and its registration requirements; and Media Capabilities, W3C
> Working Draft 18 October 2026, `MediaCapabilitiesInfo` (`supported`, `smooth`,
> `powerEfficient`) and its privacy considerations. A section number in this text is this
> extension's unless it names another document.
## 1. Scope
In scope: the capability report a page builds (§3); routing an object to a decoder (§4), and
choosing among alternatives (§4.7);
constraint sets evaluated in the page (§5); the energy entry beside `powerEfficient` (§6); the
content binding, its form and when a page writes one (§7); what happens to it through a frame's
life (§8); how a page carries it (§9); the state of the frame on screen (§10); the reasons
(§11); and a page that composes a package of several components on one timeline (§14), the
text floor of a graphics component among them (§14.9).
Out of scope: a transport; the user agent's decoders and whether they conform to their
standards, which a page cannot see beyond the answers they give and a probe (§4.4); the
decoders of a native host; how a meter measures (energy-measurement §3); and what a party does
with a state.
Relationship to the core spec and the other extensions:
- **SPEC §4.** A page answers step 3's "the sink advertises `cap`" for an object with its route
(§4): it advertises `cap` for that object when the route is not `none`. Among alternatives it
applies SPEC §4's energy order (§4.7).
- **[http-delivery](http-delivery.md) §4.3 and §12.** A WAI element is never put to a user-agent
interface (§4.3 there). The route gives the Sink Capability Descriptor's `via` (§4.6).
- **[capability-constraints](capability-constraints.md).** §5 is its evaluation in a page, over
the register of its §3.1.
- **[energy-measurement](energy-measurement.md) §2.** The classes a figure carries (§6).
- **[platform-decode](platform-decode.md) §2, §3.** The query a page asks the platform, read
from the payload, and how the answers enter the report.
- **[jwp-receipts](jwp-receipts.md) §2.1 and §3 step 3.** The content hash a binding names, and
the link hash of the group receipt.
- **[determinism-tiers](determinism-tiers.md).** The tier vocabulary a need and a binding use.
- **[live-provenance](live-provenance.md) §11.** The verdict on the segment a frame's object
belongs to, for a CMAF track (§10).
## 2. Terms
- **Page**: a sink running in a web page.
- **UA decoder**: a decoder of the user agent: WebCodecs `VideoDecoder`, `AudioDecoder` or
`ImageDecoder`. **Wasm decoder**: a WAI decoder compiled to WebAssembly that the page loads.
**Host decoder**: a decoder of a native host the page hands a payload to (http-delivery §12.1,
`host`).
- **Route**: `ua`, `host`, `wasm` or `none`.
- **Receipted object**: the bytes a JWP content hash covers (jwp-receipts §2.1): a WAI envelope;
in an end-to-end encrypted room, the sealed object, over which the receipt is signed
([confidentiality](confidentiality.md)); in a CMAF track, a chunk (live-provenance §8.1).
- **Tier**: `decode-equivalence`, `entropy-consistency` or `none`
([determinism-tiers](determinism-tiers.md) §2), ordered `none` < `entropy-consistency` <
`decode-equivalence`. A token outside this vocabulary is read as `none`, as a pin's is
(determinism-tiers §3).
- **Registered tier** of a capability: the tier its decoded output holds on every conforming
sink, from its replicate class ([SPEC](../SPEC.md) §7):
| replicate class | registered tier |
|---|---|
| `StandardDefined`, `NeuralIntegerExact` | `decode-equivalence` |
| `NeuralEntropyExact` | `entropy-consistency` |
| every other class, and a string this revision does not register | `none` |
`StandardDefined` is read as `decode-equivalence` because SPEC §7 has its decoded output the
same bytes on every conforming sink, which is the property that tier names; determinism-tiers
defines the tiers for neural capabilities, and this extension uses the same tokens for that
property and claims nothing more. `GenerativeExact` output also reproduces, but it is
generated rather than decoded, so it is held at `none` here.
- **Need**: the tier the page holds an object's decoded output to: the object's pin `tier`
([SPEC](../SPEC.md) §3.1) where it has one, else its track's `wai_determinism`
([moq-streaming-format](moq-streaming-format.md) §4), else `none`.
- **Frame**: a WebCodecs `VideoFrame`. **Binding**: a `contentBinding` value (§7).
Hexadecimal is lowercase. JSON in this text is RFC 8259.
## 3. The capability report
A page states what it knows about its decoders, and what it is about to decode, in a **capability
report**: one JSON object.
```json
{ "browser_sink": 1,
"ua": { "<codec string>": { "supported": true, "smooth": true, "power_efficient": false,
"probe": "match" | "mismatch" | "not_run" } },
"host": { "<capability>": "<tier>" },
"wasm": [ "<capability>" ],
"constraints": { "<capability>": [ { "<parameter>": { "enum": [ ], "minimum": 0, "maximum": 0 } } ] },
"energy": { "<capability> <route>": { "joules_micro": 0, "decodes": 0, "window_us": 0,
"class": "<class>", "relative_ppm": 0,
"filtering": "On" | "Off" | "Unknown",
"calibration_ref": "<text>",
"coverage": "Whole" | "Partial", "fabrics": [ "cpu" ] } },
"objects": [ { "name": "<text>", "capability": "<capability>", "codec": "<codec string>" | null,
"need": "<tier>", "params": { "<parameter>": 0 }, "choice": "<text>" } ] }
```
- `browser_sink` is `1`.
- `ua` holds the user agent's answers, by codec string, video and audio alike: `supported`, from
`isConfigSupported` (`VideoDecoder`'s or `AudioDecoder`'s) for the configuration the page built
from the object (§4.6), read from its payload ([platform-decode](platform-decode.md) §2);
`smooth` and `power_efficient`, from Media Capabilities' `smooth` and `powerEfficient` for the
same configuration, asked with a `video` or an `audio` member, or `null` where the page did not
ask; `probe`, from §4.4. Where the page asked about several configurations under one codec
string, the answer is their merge, as platform-decode §3 has it: supported only where each was,
and a hint `true` only where each one's was.
- `host` names the capabilities a native host decodes for the page, each with the tier its
decode holds, as the host declares it.
- `wasm` lists the capabilities the page's wasm decoders decode. A wasm decoder of an integer
neural capability may run its `wai-ops/1-i32` graphs ([integer-operators](integer-operators.md)
§9.1) on the page's WebGPU, through `wai/wai-ops-gpu`'s WGSL kernels compiled by the page's own
WGSL compiler (the `gpu` unit on the `wgsl` backend, an `integer` path in
[neural-dispatch](neural-dispatch.md) §1), once the adapter has given the host's result for
every word of the arithmetic probe and WGSL's result for every guarded case of the
integer-semantics suite in the page. Its
output is then the decode's on the page's CPU, byte for byte, so the capability's tier is
unchanged; every graph the device does not run, and every device error, runs on the page's
CPU, which is also the decoder where no adapter passes. A capability that plays is listed only
as §4.6 has it.
- `constraints` holds the constraint sets the page's decoders publish, by capability (§5). A
capability with no entry is unconstrained.
- `energy` holds measurements, by capability and route (§6).
- `objects` lists what the page is about to decode, each object (or each track, whose objects
share these values) with a `name`, its `capability`, the codec string a UA decoder would be
asked under (`null` where none applies), its need, and its declared parameters (§5); and,
optionally, a `choice`, a non-empty string that objects which are alternatives of one content
share (§4.7).
A report that does not read in this shape, or whose `browser_sink` is not `1`, is refused whole
(`report_malformed`): every member above is required but an object's `choice`, which, when
present, is a non-empty string; `objects` may be empty, and a member this text does not define is
ignored. A codec string with an answer in `ua` that §4.2's gate refuses is
never read (§4.2).
## 4. Routing
### 4.1 The user agent's decoders
A capability is put to a UA decoder only through a row of this table:
| capability | UA decoder | codec string | UA tier |
|---|---|---|---|
| `wai.video.vp9` | `VideoDecoder` | `vp09.` and more | `decode-equivalence` |
| `wai.video.avc` | `VideoDecoder` | `avc1.` or `avc3.` and more | `decode-equivalence` |
| `wai.video.hevc` | `VideoDecoder` | `hev1.` or `hvc1.` and more | `decode-equivalence` |
| `wai.video.av1`, `wai.video.av1.lossless` | `VideoDecoder` | `av01.` and more | `none` |
| `wai.video.vp8` | `VideoDecoder` | `vp8` | `none` |
| `wai.audio.opus`, `wai.audio.opus.packet` | `AudioDecoder` | `opus` | `none` |
| `wai.audio.flac` | `AudioDecoder` | `flac` | `none` |
| `wai.audio.aac` | `AudioDecoder` | `mp4a.` and more | `none` |
The **UA tier** is what a UA decoder's output holds when the decoder conforms to its standard:
- `decode-equivalence` for the video codecs whose decoder conformance requires every output
sample to equal the decoding process's (SPEC §7, `StandardDefined`), over the samples of the
frame the decoder outputs, in the format it outputs them in (§8);
- `none` for AV1, whose film-grain synthesis is held to a perceptual criterion, for VP8, whose
conformance this revision has not read, and for Opus and AAC (SPEC §7); and for FLAC, whose
decoded samples are exact but which an `AudioDecoder` hands back in a sample format the user
agent chooses, a conversion no standard fixes (SPEC §7, *What `replicate` promises over a
standard codec*).
AV1's row stays at `none` because a codec string does not say whether a stream signals film
grain. A stream that signals none decodes to the decoding process's frames on a conforming
decoder (AV1 §7.2), and a page that has shown its decoder conforming by a probe (§4.4) may use
the frames of a **grain-free** stream as the base of a residual refinement
([platform-decode](platform-decode.md) §6): staged delivery holds that base step at
`decode-equivalence` (staged-delivery §5). That changes no route and no row of this table.
A capability with no row has no UA decoder: `wai.video.av2`, `wai.video.vvc`, `wai.video.evc`,
`wai.audio.iamf`, `wai.audio.ac4` and `wai.audio.ivas` among the registered ones, since the
registry of §4.2 lists no codec string for them (the strings their header probes read are
[platform-decode](platform-decode.md) §2.5's), and every WAI-native capability, whose decoder is
WAI's own. `ImageDecoder` has no row in
this revision: an image decode in a user agent may apply colour conversion, and the user agent
does not say whether it did.
### 4.2 The registry gate
The WebCodecs Codec Registry (W3C Registry Draft, 24 September 2026) lists these codec strings:
| media | rows |
|---|---|
| video | `av01.*`, `avc1.*`, `avc3.*`, `hev1.*`, `hvc1.*`, `vp8`, `vp09.*` |
| audio | `flac`, `mp3`, `mp4a.*`, `opus`, `vorbis`, `ulaw`, `alaw`, `pcm-*` |
A codec string **matches** a row equal to it; a row ending `.*` or `-*` is matched by a string
that begins with the row up to and including its `.` or `-` and has at least one character after
it. A codec string that matches no row:
- **MUST NOT** be passed to `isConfigSupported`, to Media Capabilities or to any other
user-agent interface by a page that routes by this extension;
- is never answerable: an answer for it in a report is not read, whatever it says, and the
object's UA leg is refused `codec_unregistered`.
The registry is how WebCodecs names a codec. Its abstract says no implementer is required to
support any entry; a string outside it names nothing the registry defines, so two user agents'
answers about it need not be about the same codec, and a page has no basis to read one. Such a
capability resolves through a host decoder or a wasm decoder, or not at all. A row the registry
adds later enters this table by a revision of this extension.
### 4.3 The steps
For each object `o` of a report, in order, the page computes its route:
1. **Kind.** If `o.capability` is not dispatchable (SPEC §4: a record, a derivation, a candidate,
a companion-only or a refinement-only capability), the route is `none`
(`not_dispatchable`).
2. **Need.** If `o.need` is above the capability's registered tier, the route is `none`
(`tier_exceeds_class`, determinism-tiers §3).
3. **Constraints.** §5 over the report's sets for `o.capability` and `o.params`: if they cannot
be evaluated the route is `none` (`constraints_unevaluable`), and if no set is satisfied it is
`none` (`constraints_unmet`) — the capability is unsupported for this object, which is not a
decode error (capability-constraints §2).
4. **The UA leg.** It is taken when every check holds; otherwise its reason is the first that
fails:
1. `o.codec` is a string (`no_ua_mapping`: the object names nothing to ask a UA decoder);
2. `o.codec` passes §4.2's gate (`codec_unregistered`), whatever the capability;
3. `o.capability` has a row in §4.1 (`no_ua_mapping`);
4. `o.codec` is a string of that row's form (`codec_mismatch`);
5. the report holds an answer for `o.codec` (`ua_not_asked`), and its `supported` is true
(`ua_unsupported`);
6. the need is at most the row's UA tier (`ua_inexact`);
7. where the need is `decode-equivalence`, the answer's `probe` is `match` (`ua_unprobed`
for `not_run`, `ua_probe_mismatch` for `mismatch`).
5. **The host leg.** It is taken when `o.capability` is in `host` (`no_host`) with a tier at
least the need (`host_inexact`).
6. **The wasm leg.** It is taken when `o.capability` is in `wasm` (`no_wasm`). The route's tier
is the registered tier, which step 2 has already held to be at least the need. A wasm
decoder's output holds that tier only where the decoder is a module the page holds at the
integer level of the WAISM profile
([waism](waism.md) §6), authorised under
[jwp-receipts](jwp-receipts.md) §7.9 and checked before the page instantiated it; the frames
of any other wasm decoder are bound at `none` (§7.2).
7. **Route.** The route is the first leg taken, in the order UA, host, wasm. Where none is taken
the route is `none` (`no_decoder`), and each leg's reason is reported.
The UA leg comes first because a decoder the device ships is the one a page is expected to use,
and checks 4.6 and 4.7 take it only where its output is exact for what the object needs; the wasm
decoder is the fallback that depends on nothing the device must provide. A page **MUST** decode
an object only through its route, and **MUST NOT** decode one whose route is `none`.
Each object's entry in the **routing table** states: its `name`, its `route`, its `reason`
(`null` unless the route is `none`), each leg's outcome (`"taken"`, `"not_reached"` for a leg
after the one taken and for every leg when steps 1 to 3 decide, or the leg's reason), `asked`
(whether the page may put `o.codec` to the user agent: true exactly when steps 1 to 3 pass and
checks 4.1 to 4.4 hold), the route's `tier`, and the energy entry of §6. The entry's `reason` is
the code of the step that decided, or `no_decoder` from step 7. The route's tier is the row's UA
tier for `ua` (lowered to `none` unless the probe matched), the host's declared tier for `host`,
the registered tier for `wasm`, and `none` for `none`; in each case no higher than the
registered tier.
### 4.4 The probe
An answer of `supported` says a UA decoder accepts the configuration; it does not say the
decoder's output is the decoding process's. Where an object needs `decode-equivalence`, the page
decodes a **probe**: an object of the same capability and configuration whose decoded frames'
SHA-256 digests are published (http-delivery §12.4's probe object), each over the frame's samples
as `copyTo()` writes them with no `format`, `rect` or `layout` option (planes in order, rows
tightly packed). A frame output in `NV12` is compared after its interleaved chroma plane is split
into its two planes, which reorders samples and changes none. The probe is `match` when every
frame's digest is the published one, `mismatch` when any is not, and `not_run` when the page
decoded no probe. This revision publishes no probe for routing, so a page reports `not_run`
and a need of `decode-equivalence` is never routed to a UA decoder. It publishes one probe of
another use: a grain-free AV1 stream whose frames' digests are in
[`platform-decode-conformance/grain-free-av1/probe.json`](../platform-decode-conformance/grain-free-av1/probe.json),
by whose outcome a page takes its decoder's frames of a grain-free stream of that
configuration as a refinement base or does not ([platform-decode](platform-decode.md) §6.2).
### 4.5 The user agent's answer is a constraint check too
`isConfigSupported` answers for a whole configuration, before any decoder is configured, and
returns the configuration it would accept. It already does, for the parameters a configuration
carries, what capability-constraints asks of a sink: an answer before dispatch, for one object,
that is "unsupported" rather than a decode error. A page **SHOULD** build the configuration from
the object's own declared parameters, and **SHOULD** configure the decoder with the
configuration the answer returns. The two checks are separate and both apply: an object routes
`ua` only when its constraint sets are satisfied (step 3) and the user agent answered
`supported` (check 4.5). Neither covers the neural, quantum, world or haptic families, which have
no UA decoder; their constraints are evaluated by §5 alone.
### 4.6 What a page asks, and what it advertises
A page builds a report in two passes: from the objects alone it computes `asked` for each, and
puts to the user agent exactly the codec strings an entry marks asked; then it adds the answers
and computes the routes. Media Capabilities is asked only about a codec string the user agent
answered `supported` (http-delivery §12.2). A capability routed `ua`, `wasm` or `host` is, for
http-delivery §12.1's descriptor, `via` `"ua"` (with the codec strings asked and their answers,
`provisional` true), `"wasm"` or `"host"`; one routed `none` is absent from it.
A capability whose objects play to a deadline, such as `wai.video.int_hyper`, is in `wasm` only
where the page's decoder keeps up with the object on this device, from the decode times the page
measured on the path it decodes with (on the page's CPU, or with its graphs on WebGPU as §3 has
it): a group of `n` frames at `frame_rate_milli` frames per 1000 s, its keyframe and the frames
after it, decodes in no more microseconds, each time rounded up to a whole microsecond, than it
plays, `n · 10⁹ / frame_rate_milli`. A time that is not finite or is negative, and a group of no
frames or no rate, never keeps up. A capability whose objects are stills, such as
`wai.neural.int_hyper` and `wai.neural.int_synth`, is listed wherever the page decodes it. A
capability left out is routed `none`, its wasm leg `no_wasm`. One listed is routed `wasm`, and
§4.7 ranks it as it ranks any learned capability: after a classical alternative unless the
report holds a sink-metered figure of its decode. So a piece whose floor this device can route
plays the floor; a piece whose only alternative is the clip plays it where the decoder keeps up,
and nothing where it does not (`no_alternative`). The reference is `wai/wai-web`'s `neural_webgpu`
bundle: `IntNeural` decodes on either path, and its `capabilities` and `route` give the leg and
the report routed and chosen with it (`src/neural_leg.rs`).
### 4.7 Choosing among alternatives
Objects of a report that carry the same `choice` are **alternatives**: renditions of one content
of which the page decodes one, such as the entries of a `WAI2` envelope, a `WAI1` envelope's
capability and fallback, or the renditions of one track. For each `choice`, in order of its first
appearance in `objects`, the page puts the alternatives in SPEC §4's energy order. The order sits
inside the routing: each alternative's route is the one §4.3 gives it, and the order changes none.
- An alternative routed `ua` ranks by the user agent's answer for its codec string
([platform-decode](platform-decode.md) §3): `first` when `supported`, `smooth` and
`power_efficient` are all `true`; `smooth` when `smooth` is `true` and `power_efficient` is
not; `classical` otherwise. A `false` or a `null` is not a `true`: no answer is not a
power-efficient one.
- An alternative routed `host` or `wasm` ranks `classical`. The user agent's answers are about
its own decoder, which does not run this object, so they are not read for it, whatever they
say: AV1, whose user-agent tier is `none`, and a codec whose need is `decode-equivalence` while
no probe ran (§4.4) route elsewhere and rank on nothing the user agent said.
- An alternative whose capability is learned (SPEC §4: its replicate class is not one of the
three standard classes, or the string is not registered) ranks `classical` when the report
holds a measurement under its route's key (§6, `"<capability> <route>"`) that reads under §6
with the class `OnChipCounter` or `CalibratedInstrument` and the coverage `Whole`: a
sink-metered figure of its decode, with its class, uncertainty and scope. It ranks
`learned-without-budget` otherwise. The figure's value is not read.
- An alternative routed `none` comes after every other, with no rank.
Report order within a rank. `powerEfficient` is the user agent's hint, used as the user agent
gave it, as an order and never as an energy figure or as evidence of hardware. The **chosen**
alternative is the first in that order whose route is not `none`, and where every route is `none`
nothing is chosen (`no_alternative`). A page **MUST** decode the chosen alternative of a choice
and no other, so a learned alternative without a qualifying measurement is decoded only where no
classical alternative can be routed. For a `WAI1` envelope this is SPEC §4's order of steps 3 and
5: the chosen alternative is tried first, and the other after it if the first does not decode.
The **choice table** has one entry a choice: `choice`; `order`, the alternatives' names in that
order, those routed `none` included; `ranks`, each one's rank in the same order, `null` for one
routed `none`; `chosen`, a name or `null`; and `reason`, `null` or `no_alternative`. An object with
no `choice` is in no entry, and a report whose objects carry none has an empty table.
## 5. Constraint sets in the page
A page evaluates [capability-constraints](capability-constraints.md) §2 in this form:
- A capability's sets are a JSON array of one or more **constraint sets**. A set is a JSON
object whose members are parameter names, each mapped to a **constraint**: a JSON object with
one or more of `enum` (a non-empty array of values), `minimum` and `maximum` (integers), and no
other member. A value is a JSON string, or an integer: a JSON number with no fraction and no
exponent, from −(2^53 − 1) to 2^53 − 1. A page whose own numbers do not keep `1920.0` apart
from `1920` writes both as `1920`, so the integers it writes are exactly its integral
values; a value with a fractional part stays a fraction.
- A parameter name **MUST** be one of the register of capability-constraints §3.1.
- An object's declared parameters are a JSON object of the same names and values.
`evaluate(sets, params)` is:
1. No sets for the capability: `unconstrained`.
2. The sets break the form above — not an array, an empty array, a set that is not an object, a
constraint that is not an object, a constraint with no member or with a member other than the
three, an empty `enum` or one holding something other than a value, a `minimum` or `maximum`
that is not an integer, or a `minimum` above a `maximum`: `unevaluable`
(`constraint_malformed`).
3. A set names a parameter outside the register: `unevaluable` (`parameter_unregistered`). A
page **MUST NOT** advertise a capability whose sets it cannot evaluate (capability-constraints
§2).
4. A set is **satisfied** when, for each parameter it names, the object declares a value for it
and the value meets each member of the constraint: for `enum`, it equals one of the values,
a string only a string and an integer only an integer; for `minimum` and `maximum`, it is an
integer at least, or at most, the bound. An undeclared parameter, or a declared value that is
neither a string nor an integer, meets no constraint. An empty set is satisfied by every
object.
5. `satisfied` when one set is; otherwise `unsatisfied` (`constraints_unmet`).
The grammar is the one capability-constraints adopts, enumerations, minima and maxima ORed over
sets, with integers in place of rationals, so every evaluation is exact.
## 6. Energy beside `powerEfficient`
Each entry of the routing table carries an **energy entry**:
```json
{ "power_efficient": true | false | null, "figure": null | { … }, "reason": null | "<reason>" }
```
- `power_efficient` is Media Capabilities' `powerEfficient` for the object's codec string,
copied as the user agent gave it, for an object routed `ua`; it is `null` for any other route
and where the page did not ask. It is the user agent's word: its own specification leaves the
meaning of optimal power draw to the user agent. A page **MUST NOT** set it, invert it or
compute it from a figure, **MUST NOT** state it for a route the user agent did not answer for,
and **MUST NOT** derive a figure from it.
- `figure` is the measurement the report holds under the key `"<capability> <route>"`, where
`<route>` is the object's route, and `null` when it holds none or the route is `none`. A
measurement under another route's key is not this route's figure. A page **MAY** take a measurement
from its sink's decode table ([sink-energy](sink-energy.md) §4): a row whose status is
`figure` gives one, as that extension's §1 maps it, and a row of any other status gives
none.
A measurement is read as energy-measurement §2 has it, and refused, with `figure` `null` and the
entry's `reason`, when:
- its `class` is not one of `CalibratedInstrument`, `OnChipCounter`, `ModelBased` and `Estimator`
(`energy_class_invalid`; `HwShunt` is never emitted, energy-measurement §2.6);
- `joules_micro` is `0` (`unmetered_as_zero`): an unmetered decode has no measurement, and a
class beside a zero claims a measurement that did not occur;
- `joules_micro`, `decodes` or `window_us` is not a positive integer below 2^53
(`energy_count_invalid`);
- a hardware class lacks its evidence: `relative_ppm`, a positive integer, for
`CalibratedInstrument` and `OnChipCounter`; `filtering`, one of `On`, `Off` and `Unknown`, for
`OnChipCounter`; `calibration_ref`, a non-empty string, for `CalibratedInstrument`
(`energy_evidence_missing`);
- `coverage` is not `Whole` or `Partial`, or `fabrics` is not a non-empty array of distinct
strings (`energy_evidence_missing`).
A figure that reads is reported with every member it was given and
`per_decode_micro = ⌊joules_micro / decodes⌋`; `joules_micro` and `decodes` stay beside it, so the
floor loses nothing. An object with no measurement is **unmetered**: `figure` and `reason` are
both `null`, and a page presents it as unmetered, never as 0 J. In this revision a figure never
changes a route, and none is steered on: §4.7 reads only whether a learned alternative's figure
qualifies, never its value. How a figure is measured, bracketed and
attributed is energy-measurement §3 and §4's, and a `Partial` coverage says the meter saw only
the fabrics it names.
## 7. The content binding
### 7.1 The value
A **content binding** is a JSON object, and in a page a dictionary of the same members, the
`contentBinding` member of a frame's metadata: proposed for the WebCodecs VideoFrame Metadata
Registry, and not registered (§9):
| member | value |
|---|---|
| `alg` | the algorithm `digest` is computed with. This revision defines `jwp-object-1`: the content hash of jwp-receipts §2.1, `BLAKE3("moq-jwp:object\x01" ‖ object)`. |
| `digest` | 64 hexadecimal digits: the content hash of the receipted object the frame was decoded from. |
| `receipt` | optional; 64 hexadecimal digits: the link hash (jwp-receipts §3 step 3) of the group receipt that object verified under. |
| `tier` | the tier the frame's samples hold (§7.2). |
Its **canonical form** is the JSON text of those members in that order, `receipt` left out when
absent, with no whitespace and no other member. A reader:
- refuses a value that is not an object, that lacks `alg`, `digest` or `tier`, whose members
are not strings, whose `digest` or `receipt` is not 64 hexadecimal digits
(`binding_malformed`);
- refuses an `alg` it does not know (`binding_alg_unknown`): it cannot check the digest;
- reads a `tier` outside the vocabulary as `none`;
- ignores a member this text does not define, as a dictionary drops one.
### 7.2 When a page binds a frame
A page binds a frame it decoded, and only then, when:
1. the frame was output for a chunk whose coded data came from one receipted object, and the page
computed `digest` over that object's bytes as received, or holds it as the content hash of the
object receipt that object verified against;
2. the content hash of every object the decoder was given since its last key frame — its last
`key` chunk, or its configuration — was established: the object verified against its JWP
object receipt (jwp-receipts §3 steps 1 to 5), or it was fetched whole under that hash as its
content identifier, or through a verified head proof (http-delivery §6, §8.3). A frame
predicted from data whose hash nobody established is not bound
(`binding_unverified_reference`). A page that verifies every object before a decoder sees it
meets this by construction.
`receipt` is written exactly when the object verified against a group receipt (jwp-receipts §3
steps 1 to 5), and is that receipt's link hash; a page **MUST NOT** write a receipt it did not
verify the object against. For an object carried over MoQ, `digest` and `receipt` are then the
`content_hash` and `group_receipt` of its `WAI_RECEIPT` property
([moq-streaming-format](moq-streaming-format.md) §5). `tier` is the route's tier (§4.3),
lowered to `none` where:
- the route is `ua` and the frame's `format` is `null`: its samples cannot be read without a
conversion, so nothing can check them;
- the route is `wasm` and its decoder is not a module the page holds at the integer level of the
WAISM profile (§4.3 step 6);
- the page altered the samples after the decode (determinism-tiers §2, *What a tier covers*).
Where the frame's tier is below the object's need, the page **MUST NOT** present the frame as
meeting it.
On the wasm route the wasm decoder writes the binding itself: it holds the object's bytes, hashes
them, decodes them, and returns every frame with the binding.
## 8. Through the frame's life
| operation | the binding |
|---|---|
| a decoder's output | none from the decoder: WebCodecs' *Create a VideoFrame* takes no metadata. The page binds it (§7.2, §9). |
| `clone()` | carried: the clone references the same samples, and *Clone VideoFrame* assigns it every slot but the resource reference, `[[metadata]]` among them. |
| serialization (`postMessage` without transfer) | carried: the serialization steps copy `[[metadata]]`. |
| transfer | carried to the receiving realm; the source is closed and keeps none. |
| `close()` | gone with the frame. |
| `copyTo()` | the bytes written carry none. With no `format` option they are the frame's samples, and a page **MAY** check them against a published digest (§4.4); with one they are a conversion, which no tier covers. |
| `new VideoFrame(frame, init)` | carried only when the page puts it in `init.metadata`: the constructor copies `init`'s metadata, not the source's. The new frame references the source's samples; a page **MAY** copy the binding into it, and a narrower `visibleRect` presents part of the same samples, from the same object. |
| `new VideoFrame` from a canvas, an image or a buffer | a frame of other samples: none, except on the wasm route, where the samples are the ones the wasm decoder output from the bound object (§7.2). |
| drawing the frame (`drawImage`, a texture) | the drawn pixels are a rendering and carry none; §10 says which frame a region of the screen shows. |
| `VideoEncoder.encode()`, and a decode of its output | **does not survive.** A re-encoded frame is a new object: its chunks carry no binding, and a frame decoded from them is bound, if at all, to that new object and its own receipt. |
## 9. Carriage in a page
### 9.1 As metadata
Where the user agent's `VideoFrameMetadata` has a `contentBinding` member, a page binds a decoded
frame `f` by constructing `new VideoFrame(f, { metadata: { ...f.metadata(), contentBinding } })`
and closing `f`. `clone()`, serialization and transfer then carry it by WebCodecs' own steps (§8).
### 9.2 In a side table
`VideoFrameMetadata` is a dictionary whose members are the registry's, and a dictionary keeps only
the members it declares, so a member no registry lists is dropped from `init.metadata`. Until
`contentBinding` is listed, a page carries the binding in a side table:
1. **Pending.** Before it gives a decoder a chunk, the page enters the chunk's binding under (that
decoder, the chunk's `timestamp`). A decoder outputs a frame with the timestamp of the chunk it
came from (WebCodecs *Output VideoFrames*), and nothing else in a frame says which chunk that
was. Where a second chunk is given the same decoder under a timestamp still pending, both
entries are struck and neither frame is bound (`binding_ambiguous`). A page bounds the entries
pending per decoder, and strikes the oldest past the bound.
2. **Output.** On a decoder's output, the page takes the entry under (that decoder, the frame's
timestamp), removes it, and attaches it to the frame object, held weakly so that it lives as
long as the frame does. An output with no entry is unbound.
3. **Reset.** `reset()`, `close()` and a decoder error strike that decoder's pending entries, and
so does `flush()` once it resolves, for the entries whose frames it did not output; a frame
output afterwards for a struck entry is unbound.
4. **Clone.** `clone()` attaches the source's binding to the clone.
5. **Transfer.** A frame posted to another realm is posted with its binding beside it, and the
receiver attaches it on arrival. A frame that arrives without one is unbound.
6. **Close.** `close()` detaches it.
A page **MAY** test whether the user agent keeps the member (construct a frame with it in
`init.metadata` and read `metadata()`), and use §9.1 where it does.
## 10. The frame on screen
For each region of the screen a page draws frames into (a tile), the page keeps the binding of the
frame it last drew there. The **state** of that frame is computed from:
- the binding, or none;
- `object`: `verified` (the object verified against its receipt), `refused`, or `unverified` (no
receipt has been checked);
- `chain`: the group receipt's admission to its chain (jwp-receipts §3 step 3): `linked`,
`anchor`, `history`, `held` (not yet admitted: `parent_not_held`) or `refused`;
- `provenance`: for a track with a provenance track (live-provenance §10.2), the verdict of
live-provenance §11.4 on the segment of the frame's group, `accepted` or `refused`, or `missing`
while its provenance object has not arrived; `not_applicable` for a track packaged as WAI
envelopes, which has no C2PA provenance (live-provenance §1).
The state is, in this order:
1. `unbound`, with no binding or with one the reader refuses (§7.1);
2. `refused`, when `object`, `chain` or `provenance` is `refused`;
3. `pending`, when `object` is `unverified`, `chain` is `held` or `provenance` is `missing`;
4. `verified` otherwise.
`anchor` and `history` are admissions: a chain a joiner anchored, or filled below its anchor. A
page that shows `verified` for them **MUST** also say from which group it holds the chain, as
jwp-receipts §3 step 3 requires of a verifier that re-anchors, and **MUST NOT** state the groups
before it verified.
A page that shows the state, behind a control the reader turns on, shows for each tile the state,
the tier, the first digits of the digest and of the receipt, the provenance, the route and the
energy entry. The state is about the frame the tile last drew, not about the pixels on the
display: what the display emits is a rendering of the frame, scaled and converted to the
display's colour space, which no tier covers.
## 11. Reasons
| reason | raised at |
|---|---|
| `report_malformed` | §3 |
| `not_dispatchable` | §4.3 step 1 |
| `tier_exceeds_class` | §4.3 step 2 |
| `constraints_unevaluable` | §4.3 step 3 |
| `constraints_unmet` | §4.3 step 3, §5 step 5 |
| `no_ua_mapping` | §4.3 steps 4.1, 4.3 |
| `codec_mismatch` | §4.3 step 4.4 |
| `codec_unregistered` | §4.2, §4.3 step 4.2 |
| `ua_not_asked` | §4.3 step 4.5 |
| `ua_unsupported` | §4.3 step 4.5 |
| `ua_inexact` | §4.3 step 4.6 |
| `ua_unprobed` | §4.3 step 4.7 |
| `ua_probe_mismatch` | §4.3 step 4.7 |
| `no_host` | §4.3 step 5 |
| `host_inexact` | §4.3 step 5 |
| `no_wasm` | §4.3 step 6 |
| `no_decoder` | §4.3 step 7 |
| `no_alternative` | §4.7 |
| `constraint_malformed` | §5 step 2 |
| `parameter_unregistered` | §5 step 3 |
| `energy_class_invalid` | §6 |
| `unmetered_as_zero` | §6 |
| `energy_count_invalid` | §6 |
| `energy_evidence_missing` | §6 |
| `binding_malformed` | §7.1 |
| `binding_alg_unknown` | §7.1 |
| `binding_unverified_reference` | §7.2 |
| `binding_ambiguous` | §9.2 step 1 |
An object routed `none` is inert at the sink for that object, as SPEC §4 step 6 has it; a refused
energy measurement leaves the route as it is; a refused binding leaves the frame unbound.
## 12. Conformance
[`live-conformance/browser/`](../live-conformance/browser/) holds the corpus, `vectors.json`,
written by `wai_browser_sink_vectors gen`. It has:
- **Routes**: capability reports, each with the routing table §4 and §6 give it: every route and
every reason of §4.3, the registry gate with codec strings for AV2, VVC and IAMF answered
`supported` and never read, a need of `decode-equivalence` refused a UA decoder whose probe did
not run and taken by one whose probe matched, the host and wasm fallbacks, and a malformed
report.
- **Choices**: reports whose objects are alternatives, each with its routing table and the choice
table §4.7 gives it: `first`, `smooth` and `classical` before `learned-without-budget`; a
power-efficient answer that is not smooth below a smooth one; a power-efficient answer for a
codec routed to a host decoder (its need above the user agent's tier) read for nothing; a
learned alternative with a qualifying measurement (`OnChipCounter`, `CalibratedInstrument`)
ranked `classical`, and with a `Partial` or a `ModelBased` one ranked last; a learned
alternative chosen where no other routes; no answer read as not power-efficient; two choices
beside an object in none; a choice with nothing to choose (`no_alternative`); and an empty and a
non-string `choice`, each refused whole. Every measurement in them is synthetic, marked
`"note": "synthetic: no measurement"`.
- **Constraints**: constraint sets and declared parameters, with §5's outcome: satisfied by one
set of several, by an empty set, by `enum`, `minimum` and `maximum`; unsatisfied by an
undeclared parameter and by a value of the wrong type; each malformed form and an unregistered
parameter.
- **Energy**, among the routes: measurements beside answers, each with its energy entry: every
class, the unmetered case, a figure under another route's key, and each refusal of §6.
- **Bindings**: values with their reading: canonical forms, each refusal of §7.1, an unknown tier
read as `none`, and a member the text does not define; the tier §7.2 gives a frame for each
route, format and alteration, a `wasm` frame's decoder being a module the page holds at the
integer level of the WAISM profile (§4.3 step 6); and objects bound under a signed group
receipt, each binding's `digest` the object's content hash and its `receipt` the receipt's link
hash, the receipt's signature and the object's inclusion verifiable from the corpus.
- **Frames**: inputs to §10 with their state, covering every row.
- **Side tables**: sequences of chunks, outputs, clones, transfers and resets with the bindings
§9.2 leaves each frame, `binding_ambiguous` among them.
`wai_browser_sink_vectors verify` and the test `tests/browser_sink.rs` check that `vectors.json`
regenerates byte for byte, that `wai-rs` gives every case its stated outcome, that every reason of
§11 appears, and that §11 lists exactly the reference's codes. `frame_tier` in `wai-rs`, which
the page calls as `frameTier`, is given no module status: it gives a `wasm` frame the tier §7.2
gives the frame of a decoder held at the integer level, and every `wasm` frame of the corpus, the
chain's among them, is of such a decoder. The page whose code the header names loads no wasm
decoder for a room's tracks, so it binds no frame on the `wasm` route.
`live-conformance/browser_sink_verify.py` is the second implementation, written from this text:
it shares no code with `wai-rs`, reads each capability's kind, status and replicate class from the
registry export ([`registry/capabilities.json`](../registry/capabilities.json)), checks
signatures with WAI's strict rule and hashes with BLAKE3 from `claim-conformance/`, and mutates
copies of the corpus and requires every copy to fail. The page runs the route, choice, constraint,
energy, binding and frame cases through its wasm build, and its side table through the sequences, in a
browser (`joule-moq-web`'s end-to-end scenario `binding`). `wai conform sink --suite browser` runs
every call of the corpus but the chain against another implementation, its answer withheld, and
signs a record of the run (conformance-results); `wai_browser_sink_vectors answers` is the
reference's answer to one call.
## 13. Security and privacy considerations
- **A binding is a statement, not a signature.** Any script in the page can bind any frame. What
a binding gives is where to look: the receipt it names commits to the digest under a key, and a
party that holds the object, the receipt and its key log checks them. The state of §10 is the
page's own verdict on what it checked.
- **What `verified` covers.** The object and its group receipt, and, for a CMAF track, the
segment's C2PA provenance. It does not cover what the display shows, how a frame was scaled, or a
user agent's decoder: a UA decoder that misdecodes a verified object outputs a frame bound to that
object. §4.4's probe is how a page holds a UA decoder to the decoding process where the tier
matters.
- **What a report reveals.** A report holds the user agent's answers and, with a meter attached,
an energy figure; together they say a good deal about a device (Media Capabilities, privacy
considerations). Nothing in this extension sends a report anywhere: routing, binding and the
state of a frame are computed in the page. A service that collects reports learns that much
about each device it collects from; where it needs to record a legal basis for doing so,
jwp-receipts §2.4.6's `lawful-basis` claim is the place.
- **An energy figure is a side channel.** A per-process energy reading, taken often enough, leaks
what a process does. A page receives a figure only from a meter the host chose to attach, at the
granularity the host chose, and this extension never has a page read a counter itself.
- **Ambiguity is refused, not guessed.** A side table keyed by timestamp cannot tell two chunks of
one decoder with one timestamp apart, so neither frame is bound (§9.2).
- **Unregistered codec strings are not asked about.** Besides having no defined answer, a question
about a codec string outside the registry adds to what a page learns about a device for nothing
it can use.
## 14. Composing a package
A package ([packaging](packaging.md)) may carry a programme as several components, each in the
capability that suits it: camera footage in a video codec or an integer rung, titles and credits
as [vector graphics](graphics-vector.md), captions as [timed text](timed-text.md), music as a
[score](score.md), data as [signed state](state-feeds.md). A **composing page** opens such a
package, dispatches each component by §4, checks everything it is about to present against the
package's signed receipt, decodes each component on the route it chose, and presents them all on
one timeline. A piece's base may have layers ([staged-delivery](staged-delivery.md)), and the page
computes two kinds of them itself, in WebAssembly, before it presents any stage they give: the
levels of an integer neural field ([neural-fields](neural-fields.md) §7) and a picture refined by a
layer conditioned on side planes (staged-delivery §6.3) (§14.5). The reference is `wai/wai-web`'s
`compose` bundle (`src/compose.rs` and `src/compose_stage.rs`, feature `compose`) and its page,
`wai-web/compose/` (`compose.js`, `index.html`); the package it plays is
`wai-web/compose/package/`, which `wai-pack` writes under `compose/policy.toml` (packaging §5.8),
and the staged packages beside it are `wai-web/compose/staged/` (§14.7).
### 14.1 What the page reads
The page reads a version-2 package receipt (packaging §9.1), the index of each component it
names (packaging §6), and the objects the indexes list (`objects/<content hash>.wai`). Every
component is a run of pieces on its own timebase, the lead's cut at its scene cuts and every
other component's at the lead's piece starts (packaging §4.5, §5.8), each piece one object
or more. Within a component, a piece's **alternatives** are its base entry and each of its
floor entries; a refinement rides on the base it refines and is not an alternative. An asset
entry of a piece is an object a layer of that piece may name as a side object (staged-delivery
§3.1); the page reads it only when a layer names it (§14.5). The page
reads nothing else of a package: no playlist, catalog or listing, none of which the receipt
binds. A text piece names its language in its object's `target` (packaging §5.8), which is
how the page reads its segment.
### 14.2 Layers
The layer a decoded object is presented in follows from its capability, never from its
component's media class:
| capability | layer | drawn as |
|---|---|---|
| a video capability, `wai.image.png` in a video or an image component | video | frames on the picture, at the lead's frame rate; a still held for its piece |
| `wai.field.int_grid` | video | the field's render at the view its payload pins ([neural-fields](neural-fields.md) §6), held for its piece |
| `wai.graphics.vector` | overlay | an `RGBA8P` frame (graphics-vector §2.9) composited source-over above the picture |
| `wai.text.zstd`, the floor of a piece whose base is `wai.graphics.vector` | floor | the blocks of the piece's transcript that are active at the clock's tick, as text (§14.9) |
| `wai.text.timed` | text | text, in the page's own text layer, at each cue's anchor |
| an audio capability, `wai.audio.score` | audio | samples on the page's audio output |
| `wai.feed.state` | state | the records of the state at the clock's tick |
The vector overlay is rasterised at a size its payload lists (graphics-vector §3.10): the smallest
listed size at least as large as the overlay's area on the display in device pixels, else the
largest listed. The platform scales it from there (graphics-vector §1.1).
### 14.3 Dispatch
For each piece of each component, the page builds §3's report over the piece's alternatives,
each object carrying the `choice` `"<component>/<piece>"`, and decodes the alternative §4.7
chooses. The report's `wasm`, `host` and `ua` say what the device has: a decoder the page holds
in WebAssembly is in `wasm`, a capability that plays only where the decoder keeps up (§4.6); the
user agent is asked, as §4.6 has it, about every alternative whose capability platform-decode
maps (AV1, HEVC, AVC, VP9 and VP8 video; Opus, FLAC and AAC audio) and that the page does not
decode in WebAssembly itself, from a query read from that object's payload
([platform-decode](platform-decode.md) §2), after the object has been checked (§14.4); each
configuration is asked, and answers under one codec string are merged (platform-decode §3). A
capability the page decodes in WebAssembly (FLAC in the reference page) is not put to the user
agent, so its UA leg is `ua_not_asked` and it routes `wasm`: the page's decode holds the
capability's registered tier, the user agent's `none` (§4.1). A profile without the page's
decoder of it asks the user agent. A field (`wai.field.int_grid`) and the objects a conditioned
layer reads as side planes (`wai.image.png`, and `wai.splat.int_codec` for a render) the page
computes in WebAssembly too: platform-decode maps none of them, so their UA leg is
`no_ua_mapping`, the user agent is never asked about them, and they route `wasm`. The
capabilities whose layers the page applies in WebAssembly (`wai.field.int_refine`,
`wai.image.int_cond_refine`) are listed in the report's `wasm` leg beside its decoders. A layer is
not an alternative and is not routed: it is applied as §14.5 has it where its capability is in
that leg and its piece's base was chosen, and is otherwise not applied, its piece presenting the
stage below (a floor chosen takes no layer). An alternative routed `ua` is decoded through WebCodecs from
the chunks platform-decode §2.3 reads from its payload, with the first configuration the user
agent accepted under which it decodes; a video one is presented in the video layer, an audio one
on the page's audio output. An Ogg Opus stream (`wai.audio.opus`) states a pre-skip and an end
(RFC 7845 §4) that a decoder handed its packets does not apply: the page drops the pre-skip,
ends the samples at the last page's granule position, and holds an Opus floor's piece (packaging
§7.9) to exactly its piece's samples (`compose_frames` otherwise). A **forced profile** is a report from which named capabilities are
left out of every leg and never asked about; it routes and chooses as any other.
- The chosen alternative is decoded first. Where the user agent's decoder does not decode it,
having answered the configuration supported, the page decodes the next alternative in §4.7's
order whose route is not `none`, after checking that object as §14.4 has it: §4.7's "the
chosen alternative is tried first, and the other after it". The dispatch record keeps what was
tried and why it did not decode (`tried`).
- A piece whose alternative is a record (`wai.text.timed`) is not routed: SPEC §4 dispatches no
decoder for a record, and the page draws it in its text layer. Of a package's text
components the text layer presents one, or none (§14.9).
- A component with a piece for which nothing is chosen (`no_alternative`) is not presented on
this device, and the page says so. The lead component (the receipt's first) carries the clock
(§14.6): a package whose lead has such a piece is refused whole.
The **dispatch record** is what this device dispatched, and why, as data a page can show:
```json
{ "device": { "user_agent": "<text>", "forced_without": [ "<capability>" ],
"wasm": [ "<capability>" ], "ua": { "<codec string>": { "supported": true, … } } },
"receipt": { "receipt_hash": "<hex>", "signer_id": "<text>", … },
"components": [ { "id": "<id>", "layer": "<layer>" | null, "presented": true,
"pieces": [ { "piece": 0, "chosen": "<name>" | null, "capability": "<capability>",
"role": "base" | "floor" | null, "route": "ua" | "host" | "wasm" | "page" | "none",
"why": "<text>", "order": [ "<name>" ],
"tried": [ { "name": "<name>", "error": "<text>" } ],
"alternatives": [ { "name": "<name>", "capability": "<capability>",
"role": "base" | "floor", "route": "<route>",
"reason": "<reason>" | null, "legs": { … },
"tier": "<tier>", "rank": "<rank>" | null } ],
"layers": [ { "name": "<component>/<piece>/L<layer>/<capability>",
"capability": "<capability>", "layer": 1,
"route": "wasm" | "none",
"outcome": "pending" | "applied" | "refused" | "not-applied",
"reason": "<reason>" | null, "detail": "<text>" | null,
"stage": 1 | null, "sha256": "<hex>" | null,
"sides": [ { "entry": "<name>" | null, "object": "<hex>",
"render": { … } | null, "held": true,
"exact": true | null, "capability": "<capability>",
"via_fallback": false, "tier": "<tier>",
"why": "<text>" | null } ] } ] } ] } ] }
```
An alternative's `route`, `reason`, `legs` and `tier` are its §4.3 routing-table entry, and its
`rank` its §4.7 rank; `why` says in words which rule decided: the base first in §4.7's order, the
floor first in that order, or the floor because the base has no route here, with the base's
reason and legs. A piece with layers lists each, in layer order: its outcome (`pending` until it
arrives), the reason it was refused or not applied (a reason of staged-delivery §4.2, an object
check's code (packaging §6.3), or `prefix-digest`), the stage it presented and that stage's SHA-256, and each side
plane it named: the object, the render's pose for a render entry, whether the page held its plane,
whether exactly, how it was decoded, and why a plane was not held.
### 14.4 Checks before presenting
The page presents nothing of a package until each of these holds, in this order; the first that
fails refuses the package whole, and the page states which check failed and with which code:
1. the receipt's JSON, its indexes, their digests and stage roots, its counts and its Ed25519
signature (packaging §9.1, `PackageReceiptV2::verify_detailed`; in the page,
`verifyPackageReceipt`, and `verifyPackageReceiptSignature` for the signature alone);
2. each index, read now that the receipt holds it (packaging §6);
3. every object the page is about to decode, and every floor object it reads a query from,
against its index entry: its length and content hash, its envelope, lengths and capability,
its `refines` and `derived_from` (packaging §6.3);
4. each decoded output whose entry states decode-equivalence, against the entry's prefix digest
(packaging §6.5), piece by piece: a video base's clip digest (a still's frame held for its
piece); a vector piece's clip digest over its frames rasterised in the page as `RGBA8P` at
the first size its payload lists and its component's rate; a text piece's SHA-256 over its
segment, once the segment reads to its cues (timed-text §5.3); a score piece's SHA-256 over
its samples as signed 16-bit little-endian integers, a two-channel score's interleaved
(score §3.4), and a FLAC piece's over its samples, interleaved, as the page decodes them
in WebAssembly; a field's render at its own view, computed in the page, whose SHA-256 is
the field's stage hash (staged-delivery §5); a picture of an image component, its RGB8 frame
decoded in the page (packaging §6.5); and a feed piece's state, replayed from its signed ops
to its seal, against the sealed state hash ([state-feeds](state-feeds.md)),
each piece after a component's first starting from the state the one before it seals.
A floor decoded by the user agent is held to no digest: its tier is `none` (§4.1), and the page
says so; an audio floor among them too, whatever its codec, since the user agent chooses the
sample format it hands back. A check's outcome is listed with what it compared, so a page can
show every check it made. A piece whose base has layers is presented at its base once these
hold; each layer is checked as it arrives (§14.5), and one that fails a check is not applied,
without refusing the package.
### 14.5 Refinement layers
A piece whose chosen alternative is its base presents the base first. Its layers then arrive one
at a time, in layer order, and for each the page, in this order:
1. fetches it and checks it against its entry: its length and content hash, its envelope and
lengths, its capability and its `refines` (packaging §6.3);
2. computes the next stage in WebAssembly, with the reference implementation of staged-delivery
§4.1 compiled for the page, which refuses with that section's reasons (§4.2):
- over a field, the payload's level as the field's next level (neural-fields §7.3,
staged-delivery §6.2), the field holding no layer for later (a hold bound of zero), so a
level whose level below is not applied is refused `layer-gap`; then the field rendered
again at its own view;
- over a picture, `wai.image.int_cond_refine` (staged-delivery §6.3). First each side plane
its `refines.side` names (§3.1), held by the page or not. An object the piece's asset entries
list is fetched once and held only where the page decodes its capability in WebAssembly
under the device's profile, its bytes pass their entry's checks (packaging §6.3), and its
output, decoded through its manifest's capability or its fallback as SPEC §4 dispatches,
holds the digest its entry states. A render entry's plane is the frame
`wai-splat-render:i1` gives at the entry's pose ([splat-exact](splat-exact.md) §5), which the
page renders itself from a splat set held so, decoded through `wai.splat.int_codec` with the
parameter set the page holds, its pin verified. Then §4.1 step 4 refuses the layer where
the page does not hold a plane (`side-absent`), or holds one that is not exact
(`side-not-exact`: decoded through a fallback, or below `decode-equivalence`);
3. holds the stage's output to the prefix digest its entry states: for a field the SHA-256 of
its render at its own view, for a picture of its RGB8 frame, each the stage hash
staged-delivery §5 defines (`prefix-digest`);
4. presents the stage.
A layer refused at step 1 or 2 is not applied, and one refused at step 3 is computed and never
presented; either way the page presents the stage below it (the base, for a first layer) and
says why: the dispatch record states each layer's outcome and reason, and each side plane, held
or not, exact or not, and why not (§14.3). The page then takes the next layer, and the reference
implementation decides it (a level whose level below was refused at step 1 or 2 is refused
`layer-gap`). The page never presents a stage whose digest it has not compared, nor one that
failed. An asset's prefix digest is its decoded output's stage hash (staged-delivery §5),
the attribute tensor's bytes for a splat set.
### 14.6 One timeline
The lead component's video is the master clock. Its frame `n` is presented from `n / fps`
seconds, `fps` its index's timebase; every other layer is placed by that clock:
- the overlay presents its frame `g = ⌊t · num / den⌋` at its rate `num/den`, from the piece
whose window holds `g` (a piece's frames are the whole document's, graphics-vector §3.11), and
nothing outside its pieces' frames;
- the floor of a graphics piece presents the blocks active at the tick `⌊t · tb⌋` of its
transcript's timebase, from the piece the clock is in (§14.9), and nothing where that piece
presents its overlay;
- the text layer shows the cues active at `t` of the piece the clock is in (a cue across a piece
start is in both pieces' segments, with its whole interval, timed-text §5.2);
- the state layer shows the state at tick `⌊t · tb_num / tb_den⌋` of its component's timebase,
from the piece that holds the tick, never past that piece's seal;
- the audio layer plays its pieces' samples end to end, in their channels and at their
rate (every piece of a component the same, or the page refuses it,
`compose_audio_format`), from the position the clock gives, and re-places them at the
clock's position whenever they drift from it by more than one video frame.
The reference page plays a piece's decoded frames from a clock it starts on the page's own
timer, and the score through Web Audio, so the measure of drift is the audio context's own time.
A layered piece's picture is the latest stage it presented; the reference page moves the clock to
a piece as it presents that piece's next stage, so the stage is on screen.
### 14.7 The package's shape
[packaging](packaging.md) §5 (*Components beside the lead*, 5.8) is the normative home of a
package of several components: the
media classes `graphics` (7) and `text` (8) beside packaging's others, the rungs each medium
takes, how each component is cut at the lead's piece starts, each piece's object and `target`,
and the decoded-output digest each entry states. The reference package is written by
`wai-pack` under `compose/policy.toml`: the lead `v`, a test pattern of 320 × 180 (the fixture
`compose-01`) with an integer base and a supplied AV1 floor for every piece; `g`,
`vector-conformance`'s title card and credit roll; `t`, three captions; `s`, a voiced score
of two channels at 48 kHz with a FLAC floor of each piece's mixdown, which the page decodes
in WebAssembly where the score is not routed (a floor states no digest, so the page holds it
to none; the page asks the user agent about no audio codec); and `f`, a scoreboard feed. The
corpus check of `wai-pack` packs it again from those sources and requires every byte the same.
A composing page does not read a component's media class for a layer (§14.2); it reads it for
the digest a picture states (packaging §6.5: an image's RGB8 frame, a video's clip).
Beside it, the page plays the staged packages under `wai-web/compose/staged/`. `src/compose_stage.rs`'s
tests seal them with the package's key from objects of the corpora, byte for byte, each entry
stating the prefix digest computed natively and held there to the corpus's own statement, and
require the committed files to be those bytes. Each is one image component on the timebase 1/1,
one unit a piece:
| package | its pieces |
|---|---|
| `fields` | three fields of `volumetric-conformance/fields/`: the orb, its base carrying two of its four levels and its two further levels as layers 1 and 2; `mlp`; `edge` |
| `fields-digest` | `fields`, the orb's layer 2 stating the digest of its stage 1 |
| `cond` | three pictures of `staged-conformance/cond/`, `cond_side_planes`, `cond_dual_gbuffer` and `cond_render`, each a PNG base, its `wai.image.int_cond_refine` layer and its side objects as asset entries (two planes, three G-buffers, a splat set) |
| `cond-not-exact` | `cond_side_not_exact`'s layer over a plane carried as `wai.image.jxl` with a `wai.image.png` fallback (an asset of tier none) |
They are sealed under no policy (their policy digest is zero), and an image lead and asset
entries are not among the shapes of a package's components (packaging §5.8); a page reads them
as it reads any package (§14.1).
### 14.8 Conformance
`wai-web/compose/run.mjs` plays the reference package in browser engines and passes when, in each:
the package opens with every check of §14.4 holding and every component presented, and plays to
its end with frames, overlay frames, cues, state and audio presented; the score's samples,
synthesised in the page in the channels and at the rate each piece's `target` states, have
piece by piece the SHA-256 its index entries state, which
`wai-pack` computed natively; every overlay frame rasterised in the page has the SHA-256
[`vector-conformance/`](../vector-conformance/) states, and each piece's frames the clip
digest its entry states; each text piece holds to its segment's digest and each feed piece to
its seal, chained to the piece before; under a profile without the AV1 decoder each integer
video base decodes in WebAssembly to the clip digest its entry states; under a profile without
the integer rung and the still, every video piece plays its AV1 floor through the user agent,
the base unrouted for want of a WebAssembly decoder (`no_wasm`), and the page says the floor
plays; under a profile without the score, every score piece plays its FLAC floor, decoded in
WebAssembly to the samples whose SHA-256 the score's signed base states for its mixdown, and
the page says the floor plays; in `pack-conformance/multi/multi-opus-01`, under a profile
without FLAC, where the engine's `AudioDecoder` takes Opus every piece of the sound track plays
its Opus floor through it and comes to exactly the piece's samples, and where it does not the
sound track is not presented and the picture is; a receipt signed by another key (`compose/mutations/forged.receipt.json`, which
`wai-pack`'s corpus check writes and holds) is refused `receipt-signature`; and an object with
one byte changed in delivery is refused `object-hash`; each of the last two with nothing
presented. Over the staged packages (§14.7), held to what `volumetric-conformance/fields/` and
`staged-conformance/cond/` state as well as to their signed entries: every field of `fields`
renders in the page at its pinned view to the frame digest its scene's vectors state, read back
so from the drawn picture; with each of the orb's levels held back in delivery and released one
at a time, the coarse field is presented before its first level is asked for, and each level
released presents the next stage, whose digest is the stage hash the corpus states, before the
next level is asked for; in `fields-digest` the orb's stage 2 is computed, refused
`prefix-digest`, and stage 1 stays presented; a byte of the orb's first level changed in delivery
is refused `object-hash`, its second level `layer-gap`, and the coarse field stays presented;
every picture of `cond` presents the stage hash its case states, from two exact planes, three
G-buffers and a splat set's frame rendered in the page; a side object withheld in delivery, and
the splat decoder left out of the profile, each refuse their layer `side-absent` with the base
presented and the page saying why; `cond-not-exact`'s layer is refused `side-not-exact`, its plane
held through its fallback, with the base presented; and a side object with a byte changed in
delivery is refused `object-hash`, never held, and its layer `side-absent`. Over
`pack-conformance/multi/multi-captions-01`, a title card with captions in two languages and a
text floor (§14.9): on this device every graphics piece presents its overlay and no floor;
under a profile without `wai.graphics.vector` every graphics piece presents its floor, each
floor's transcript the one this script reads from the package's own object, the base unrouted
(`no_wasm`), no overlay frame drawn, and the page says the floor plays; at every frame of the
programme the text the page shows from the floor is the title wherever its block is active
and no caption or subtitle, while the text layer shows each caption's cue over the block's
milliseconds, so the words are on the page once; with `captions=fr` the layer shows the French
component's cues and with `captions=off` none, the floor's text unchanged; a byte of a floor
object changed in delivery is refused `object-hash`, and the floors of two pieces exchanged in
delivery `object-hash`, each with nothing presented; and the bundle's reader, given in the page
one piece's floor and another piece's base, refuses `floor_base`. It exits 0 only when
every check passes in every engine named, and fails on a page error, on an engine that cannot
be launched or runs fewer checks than it makes, and on an error of its own.
### 14.9 A graphics component's text floor
A graphics component may carry, for each piece, a floor of `wai.text.zstd`: the piece's
transcript in a frame that has one encoding ([graphics-vector](graphics-vector.md) §6.3). It is
an alternative of its piece (§14.1). A page that reads it lists `wai.text.zstd` in its report's
`wasm` leg; the base and the floor then both rank `classical` (§4.7), and the base, listed
first, is chosen wherever it has a route. A page presents a piece's floor, as text, where the
piece's base has no route on this device, so that §4.7 chooses the floor: a device whose report
holds no decoder of `wai.graphics.vector`.
A piece presents its overlay or its floor and never both, and a piece with neither is not
presented (§14.3). A base that is routed and then refused, by graphics-vector §2.7 when its
payload is loaded or for frames that do not hold their signed clip digest, refuses the package
(§14.4): in this revision its floor does not stand in for it. The reference is `wai-rs`'s `browser_sink::floor`,
which the `compose` bundle calls (`TextFloor`).
**Reading the floor.** After the object's checks against its index entry (§14.4), the page
holds, in this order, and the first that fails refuses the package with its code:
1. the manifest's `derived_from` names the piece's base: its `motif` is the content hash of the
piece's base entry, its `motif_capability` is `wai.graphics.vector` and its `renderer` is
`wvg1-transcript/1` (`floor_base`). The index says only that a floor carries a
`derived_from` (packaging §6.3); this check holds it to this piece, so a floor of another
piece or of another document, consistent in every other respect, is not presented in this
piece's place;
2. the payload is the raw frame of graphics-vector §6.3 and no other Zstandard frame: the magic
number, the frame header descriptor `A0`, the content's length in four bytes, raw blocks of
which every one before the last holds 2^17 bytes and the last, with its last-block bit set,
from 1 to 2^17, and nothing after it (`floor_frame`). The frame has one encoding for each
content, and a page that read another would present bytes no package verifier compared;
3. the manifest's `target` states `bytes`, the content's length, and `tb`, an integer from 1 to
2^32 − 1 (`floor_target`);
4. the content is a transcript in the JSON of graphics-vector §5.4, in that section's encoding
and no other: one array of block objects with that section's members in its order; every
integer at most 2^53 − 1; each `dir` 0 or 1, each `mode` 0 to 2, each `group` at most 65 535
and each `purpose` from 1 to 255 and never 13; the indices ascending; each active set
ascending, with no interval of no ticks and no two intervals sharing a tick or an end; and
the blocks, written again in that encoding, are the content byte for byte
(`floor_transcript`).
A floor's entry states no decoded-output digest (packaging §6.5). What holds the words is the
object's content hash, which the signed index states and the page has checked, and the
`derived_from` of step 1.
**Presenting it.** At clock time `t` (§14.6) the page presents the floor of the piece the clock
is in, at the media tick `τ = ⌊t · tb⌋`, as graphics-vector §6.3 has a sink do: every block
whose active set holds `τ` (`on ≤ τ < off`), in block order, as text through the page's own
text rendering, each with its language, its direction and its writing mode. Two kinds of block
are not presented from the floor, and the page records why:
- a block of purpose `caption` or `subtitle`, always (`caption_track`). Graphics-vector §6.4
holds the package to a text component in the block's language whose cues carry the block's
string over the same milliseconds, and the page's text layer presents those cues, or does
not, as the viewer has set it (graphics-vector §5.7). A viewer who has turned captions off
gets none from the floor;
- a block of purpose `forced_narrative`, while the page's text layer shows a cue of the block's
language with the block's string (`forced_in_cue`): a cue of the text component the layer
presents whose language, in ASCII lower case, is the block's in ASCII lower case, whose text
is the block's string byte for byte (its lines separated by U+000A,
[timed-text](timed-text.md) §3.2), and whose interval holds the tick's millisecond, `start ≤
⌊1000 · τ / tb⌋ < end`. Graphics-vector §6.4 has that component carry such a cue over the
block's milliseconds, so the words are on the page once: from the cue while the layer
presents that component, and from the floor while it presents none or one in another
language, since a forced narrative is shown whether or not captions are (graphics-vector
§5.7). The page compares the cue it is showing, not the package rule, so no block is withheld
for a cue that is not on the page.
The strings are inserted as text and never as markup, and the blocks are in the page's
document, where the platform's accessibility interface reads them (graphics-vector §5.5). The
page announces a block, politely and once for the interval that holds `τ`, only where its
purpose is one graphics-vector §5.2 has announced by default (`title`, `lower_third`,
`forced_narrative`, `legal`, `alert`) and that interval is at least a second long (`off − on ≥
tb`); it makes no assertive announcement. Where the words appear, their size and their face are
the page's, and no digest covers them.
| code | given when |
|---|---|
| `floor_base` | reading, step 1: the package is refused |
| `floor_frame` | reading, step 2: the package is refused |
| `floor_target` | reading, step 3: the package is refused |
| `floor_transcript` | reading, step 4: the package is refused |
| `caption_track` | presenting: a caption or a subtitle is left to the text layer |
| `forced_in_cue` | presenting: a forced narrative is on the page in a cue |
**The text layer's component.** A package may hold several text components, one a language
(packaging §5.8). The text layer presents one of them or none: the one in the language the
viewer chose, else the first the receipt lists, and none where the viewer turned captions off.
Every text component's segments are checked (§14.4) whether or not the layer presents it. The
reference page takes the choice as `captions=<language>` or `captions=off`.
**Conformance.** [`live-conformance/browser-floor/`](../live-conformance/browser-floor/) holds
the corpus, `vectors.json`, written by `wai_browser_floor_vectors gen`: floor objects that read
(graphics-vector §5.4's worked example, a programme of ten purposes, a transcript of no blocks)
and, each a control of one defect, floors refused for their base, their frame, their target
and their transcript; and presentations, each a floor, a tick and the cues of the text
component presented, with the blocks presented and those withheld: a window's first tick, its
last and the tick it ends at; a caption and a subtitle with and without a text component; a
forced narrative in two languages with no text component, with its cue shown, with a cue in
another language, of another string, ended and not begun, and with a language tag in another
case; and the announcement of each purpose over intervals shorter than, equal to and longer
than a second. `wai_browser_floor_vectors verify` and the test `tests/browser_floor.rs` check
that the file regenerates byte for byte, that `wai-rs` gives every case its outcome, that every
code of the table above is given by a case, that the table lists exactly the reference's codes,
and that the floor objects of `pack-conformance/multi/multi-captions-01`, which `wai-pack`
wrote, read against the bases they name. `live-conformance/browser_floor_verify.py` is the
second implementation, written from this text: it shares no code with `wai-rs`, gives every
case the same outcome, requires every mutated copy of a floor to be refused with the reason its
defect has and every mutated copy of the corpus to fail, and drops each of its own checks in
turn and requires the corpus to catch it (one check, that a frame ends with a block flagged
last, can differ from the others only in a frame of 2^17 bytes, and is caught by frames the
verifier writes itself). `wai-web`'s native test `compose::floor_tests` holds
the same package's floors to `wai-vector`: each is the transcript `wai-vector` computes from
the piece's base, in the frame it writes. In browser engines, `compose/run.mjs` plays
`multi-captions-01` (§14.8).