Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
1476a0d
refactor(element): remove legacy architecture
lnikell Jul 17, 2026
07226ff
docs: design kinetic orb interactive demo
lnikell Jul 17, 2026
dff1969
docs: plan kinetic orb interactive demo
lnikell Jul 17, 2026
313b01a
feat: add kinetic orb interactive demo
lnikell Jul 17, 2026
c28dc93
fix: error state issues
lnikell Jul 17, 2026
b45edf1
docs: design rapid-hover playback stability
lnikell Jul 17, 2026
9b7e796
docs: harden hover stability architecture
lnikell Jul 17, 2026
1e5a656
docs: require engagement convergence
lnikell Jul 17, 2026
958e51a
fix(element): stabilize rapid interaction playback
lnikell Jul 17, 2026
1c4f110
docs: design concurrent route decoding
lnikell Jul 17, 2026
5b96e6b
fix(element): decode route candidates concurrently
lnikell Jul 17, 2026
b760738
docs: specify concurrent decoder ownership cleanup
lnikell Jul 17, 2026
af67115
fix(element): harden concurrent route decoding
lnikell Jul 17, 2026
8b56d23
refactor: improve architecture
lnikell Jul 18, 2026
f9f3827
docs: redesign kinetic orb continuity proof
lnikell Jul 18, 2026
f9f8c62
docs: plan calibration ball rebuild
lnikell Jul 18, 2026
3cfe357
feat: rebuild kinetic orb as continuity proof
lnikell Jul 18, 2026
a602f42
docs: design rolling browser compatibility
lnikell Jul 18, 2026
9e5989a
docs: plan browser compatibility diagnostics
lnikell Jul 18, 2026
27474e3
feat(element): establish consumer playback boundary
lnikell Jul 18, 2026
ed8a1f3
feat(element): preserve decoder worker failures
lnikell Jul 18, 2026
fa944f9
fix(element): terminate playback with one retained error
lnikell Jul 18, 2026
60c4b30
feat(element): retain bounded decoder diagnostics
lnikell Jul 18, 2026
350e623
fix(element): accept decoder messages across realms
lnikell Jul 18, 2026
0eb69e5
feat: certify consumer-owned playback failures
lnikell Jul 19, 2026
d779be5
docs: plan startup codec failover
lnikell Jul 19, 2026
d87cf26
docs: refine startup qualification design
lnikell Jul 19, 2026
0213581
feat: qualify authored codec sources at startup
lnikell Jul 19, 2026
468520b
docs: document startup codec qualification
lnikell Jul 19, 2026
1851fd6
docs: extend browser compatibility matrix
lnikell Jul 19, 2026
a605734
docs: define Firefox WebCodecs floor
lnikell Jul 19, 2026
08fc061
docs: plan cross-browser playback reliability
lnikell Jul 19, 2026
fec5900
fix(element): preserve browser playback failure evidence
lnikell Jul 19, 2026
c0e0a4d
fix(element): preserve renderer failure evidence
lnikell Jul 19, 2026
7860c87
fix(playback): certify rolling browser compatibility
lnikell Jul 19, 2026
3ec4693
fix(element): convert frame ticks for WebCodecs
lnikell Jul 20, 2026
581c3be
fix(element): repair Safari HEVC duration elision
lnikell Jul 20, 2026
8694df0
docs: freeze Windows Canvas2D renderer branch
lnikell Jul 20, 2026
113f465
fix(element): add bounded Canvas2D renderer fallback
lnikell Jul 20, 2026
a2d0e69
docs: specify browser output qualification
lnikell Jul 20, 2026
8e8c978
docs: plan browser output qualification
lnikell Jul 20, 2026
53b2d7e
test: lock Firefox WebCodecs floor
lnikell Jul 20, 2026
1fa3c7b
fix(element): accept semantic BT709 transfer normalization
lnikell Jul 20, 2026
4022861
refactor(element): use canonical render geometry
lnikell Jul 20, 2026
f316d64
docs: harden output qualification design
lnikell Jul 20, 2026
4724924
feat(format): add canonical decoder color classifier
lnikell Jul 20, 2026
f7b6569
refactor(element): type provisional fallback and renderer snapshots
lnikell Jul 20, 2026
72f8e24
refactor(runtime): share decoded color classification
lnikell Jul 20, 2026
9e0ce8e
feat(format): add packed-alpha output witnesses
lnikell Jul 20, 2026
09c3379
feat(compiler): verify packed-alpha output witnesses
lnikell Jul 20, 2026
565e73f
refactor(element): share bounded RGBA materialization
lnikell Jul 20, 2026
45f9dd7
feat(element): qualify decoded packed-alpha samples
lnikell Jul 20, 2026
d9e5d27
docs: specify qualified wire format 1.1
lnikell Jul 20, 2026
d6474a4
feat(element): orchestrate provisional output qualification
lnikell Jul 20, 2026
d6a9e3a
refactor(element): isolate WebGL renderer backend
lnikell Jul 20, 2026
afcb3e9
refactor(element): harden renderer qualification path
lnikell Jul 20, 2026
bda368c
feat(element): qualify decoded output before readiness
lnikell Jul 20, 2026
1cba657
fix(playback): accept exact browser color normalizations
lnikell Jul 20, 2026
da217de
chore(examples): regenerate qualified codec bundles
lnikell Jul 20, 2026
aad712e
fix(renderer): keep packed alpha on rgba uploads
lnikell Jul 20, 2026
0dd58b7
fix(examples): preserve automatic codec ladder
lnikell Jul 20, 2026
faf7512
fix(renderer): preserve Android canvas transparency
lnikell Jul 20, 2026
0c84a17
fix(certification): use qualified playback fixtures
lnikell Jul 20, 2026
b49695f
chore: updates
lnikell Jul 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
173 changes: 173 additions & 0 deletions .github/workflows/brave-windows-compatibility.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
name: brave-windows-compatibility
run-name: brave-windows-${{ inputs.session_id }}

on:
workflow_dispatch:
inputs:
tunnel_url:
description: Exact HTTPS endpoint created for this immutable compatibility run
required: true
type: string
source_commit:
description: Exact full source commit exposed by the endpoint
required: true
type: string
tunnel_created_at:
description: Exact UTC timestamp when the immutable HTTPS endpoint was created
required: true
type: string
session_id:
description: Unique UTC session identifier used in the artifact name
required: true
type: string

concurrency:
group: brave-windows-${{ inputs.session_id }}
cancel-in-progress: false

permissions:
contents: read

jobs:
branded-brave:
runs-on: windows-2025
timeout-minutes: 120
env:
INPUT_TUNNEL_URL: ${{ inputs.tunnel_url }}
INPUT_SOURCE_COMMIT: ${{ inputs.source_commit }}
INPUT_SESSION_ID: ${{ inputs.session_id }}
INPUT_TUNNEL_CREATED_AT: ${{ inputs.tunnel_created_at }}
POLICY_PATH: scripts/browser-compatibility/certification-policy.json
steps:
- name: Validate dispatch inputs before use
shell: pwsh
run: |
if ($env:INPUT_SOURCE_COMMIT -notmatch '^[a-f0-9]{40}$') {
throw 'source_commit must be a full lowercase Git commit'
}
if ($env:INPUT_SESSION_ID -notmatch '^[0-9]{8}T[0-9]{6}Z(?:-[a-z0-9][a-z0-9-]{0,47})?$') {
throw 'session_id must be a bounded UTC identifier'
}
$uri = $null
if (-not [Uri]::TryCreate($env:INPUT_TUNNEL_URL, [UriKind]::Absolute, [ref]$uri) -or
$uri.Scheme -ne 'https' -or $uri.UserInfo -ne '' -or $uri.Fragment -ne '' -or
$uri.Query -ne '' -or $uri.AbsolutePath -ne '/') {
throw 'tunnel_url must be an origin-only HTTPS URL without credentials, query, or fragment'
}
$tunnelCreatedAt = [DateTimeOffset]::MinValue
if (-not [DateTimeOffset]::TryParse($env:INPUT_TUNNEL_CREATED_AT, [ref]$tunnelCreatedAt) -or
$tunnelCreatedAt.Offset -ne [TimeSpan]::Zero -or
$tunnelCreatedAt -gt [DateTimeOffset]::UtcNow) {
throw 'tunnel_created_at must be a valid UTC timestamp no later than dispatch validation'
}
"SAFE_SESSION_ID=$env:INPUT_SESSION_ID" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ inputs.source_commit }}
fetch-depth: 1
persist-credentials: false
- name: Bind checkout to the requested commit
shell: pwsh
run: |
$actual = git rev-parse HEAD
if ($actual -cne $env:INPUT_SOURCE_COMMIT) {
throw "checkout mismatch: $actual"
}
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: 22.12.0
cache: npm
- run: npm ci --ignore-scripts
- name: Verify official Brave resolution still matches checked-in policy
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: >-
node scripts/browser-compatibility/brave/resolve-builds.mjs
--boundary-date 2024-07-19
--policy scripts/browser-compatibility/certification-policy.json
--check
- name: Allocate caller-owned install and evidence roots
shell: pwsh
run: |
$installRoot = Join-Path $env:RUNNER_TEMP "aval-brave-$($env:SAFE_SESSION_ID)"
$runParent = Join-Path $PWD "artifacts/browser-compatibility/runs/$($env:INPUT_SOURCE_COMMIT)"
New-Item -ItemType Directory -Path $runParent -Force -ErrorAction Stop | Out-Null
$runRoot = Join-Path $runParent $env:SAFE_SESSION_ID
New-Item -ItemType Directory -Path $runRoot -ErrorAction Stop | Out-Null
"BRAVE_INSTALL_ROOT=$installRoot" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"BRAVE_RUN_ROOT=$runRoot" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
[pscustomobject]@{
schemaVersion = 1
sourceCommit = $env:INPUT_SOURCE_COMMIT
sessionId = $env:SAFE_SESSION_ID
tunnelOrigin = ([Uri]$env:INPUT_TUNNEL_URL).GetLeftPart([UriPartial]::Authority)
hostOperatingSystem = 'Windows Server 2025'
hostKernelVersion = [System.Environment]::OSVersion.Version.ToString()
runnerImage = $env:ImageOS
runnerImageVersion = $env:ImageVersion
createdAt = [DateTime]::UtcNow.ToString('o')
} | ConvertTo-Json | Out-File -FilePath (Join-Path $runRoot 'brave-workflow-provenance-windows-2025.json') -Encoding utf8
- name: Acquire exact official standalone Brave builds
run: >-
node scripts/browser-compatibility/brave/acquire-builds.mjs
--policy scripts/browser-compatibility/certification-policy.json
--platform windows-x64
--output "$env:BRAVE_INSTALL_ROOT"
- name: Independently verify extracted Brave executables
shell: pwsh
run: |
$executables = @(Get-ChildItem -LiteralPath $env:BRAVE_INSTALL_ROOT -Filter brave.exe -File -Recurse)
if ($executables.Count -ne 2) {
throw "expected exactly two extracted brave.exe files; got $($executables.Count)"
}
$versions = @()
foreach ($executable in $executables) {
$signature = Get-AuthenticodeSignature -LiteralPath $executable.FullName
if ($signature.Status -ne 'Valid' -or
$signature.SignerCertificate.Subject -notmatch '(^|,\s*)CN="?Brave Software, Inc\."?(,|$)') {
throw "invalid Brave signature: $($executable.FullName)"
}
$version = & $executable.FullName --version
if ($LASTEXITCODE -ne 0 -or $version -notmatch '\bBrave(?: Browser)?\b') {
throw "invalid branded version output: $($executable.FullName)"
}
$versions += [pscustomobject]@{
path = $executable.FullName.Substring($env:BRAVE_INSTALL_ROOT.Length + 1).Replace('\', '/')
signer = $signature.SignerCertificate.Subject
versionOutput = [string]$version
}
}
$versions | ConvertTo-Json | Out-File -FilePath (Join-Path $env:BRAVE_RUN_ROOT 'brave-authenticode-windows-2025.json') -Encoding utf8
Copy-Item -LiteralPath (Join-Path $env:BRAVE_INSTALL_ROOT 'manifest.json') -Destination (Join-Path $env:BRAVE_RUN_ROOT 'brave-acquisition-copy-windows-2025.json')
if (@(Get-ChildItem -LiteralPath $env:BRAVE_INSTALL_ROOT -Filter '*Setup.exe' -File -Recurse).Count -ne 0) {
throw 'standalone installers must not be retained after extraction'
}
- name: Run every demo, interaction, codec mode, and soak
shell: pwsh
run: |
node scripts/browser-compatibility/brave/run-matrix.mjs `
--policy $env:POLICY_PATH `
--platform windows `
--install-root $env:BRAVE_INSTALL_ROOT `
--base-url $env:INPUT_TUNNEL_URL `
--run-root $env:BRAVE_RUN_ROOT `
--source-commit $env:INPUT_SOURCE_COMMIT `
--session-id $env:SAFE_SESSION_ID `
--tunnel-created-at $env:INPUT_TUNNEL_CREATED_AT
- name: Remove acquired browsers and clean profiles
if: always()
shell: pwsh
run: |
if (Test-Path -LiteralPath $env:BRAVE_INSTALL_ROOT) {
Remove-Item -LiteralPath $env:BRAVE_INSTALL_ROOT -Recurse -Force
}
Get-ChildItem -LiteralPath $env:RUNNER_TEMP -Directory -Filter 'aval-brave-profile-*' -ErrorAction SilentlyContinue |
Remove-Item -Recurse -Force
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: always()
with:
name: brave-windows-${{ env.SAFE_SESSION_ID }}
path: artifacts/browser-compatibility/runs/${{ inputs.source_commit }}/${{ env.SAFE_SESSION_ID }}
if-no-files-found: error
include-hidden-files: false
retention-days: 30
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,29 @@ jobs:
- run: npx playwright install --with-deps chromium firefox webkit
- run: npm run test:browser:production

kinetic-orb:
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- run: npm ci --ignore-scripts
- run: npx playwright install --with-deps chromium
- run: npm run test:kinetic-orb
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
if: failure()
with:
name: kinetic-orb-playwright
path: test-results
if-no-files-found: warn
include-hidden-files: false
retention-days: 30

package:
runs-on: ubuntu-24.04
timeout-minutes: 45
Expand Down
101 changes: 68 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ loops, named application states, authored triggers, bounded transitions,
reversals, and packed transparency.

One logical animation is published as a codec bundle. Each codec gets its own
AVAL 1.0 file—AV1, VP9, H.265/HEVC, or H.264—and the browser selects the first
ordered `<source>` with a supported rendition. The state graph and authored
timing are identical in every file.
AVAL wire 1.1 file—AV1, VP9, H.265/HEVC, or H.264—and the browser selects the
first ordered `<source>` that decodes and passes pre-readiness output
qualification. The state graph and authored timing are identical in every
file.

## Five-minute start

Expand All @@ -22,8 +23,8 @@ npm run dev

Here `npx avl` resolves the `avl` executable from the compiler package
installed on the preceding line. The generated starter contains its RGBA
frames, project 1.0 file, four ordered encoding policies, fallback markup, and
watch workflow.
frames, project 1.0 file, four ordered encoding policies, consumer-owned error
handling, and watch workflow.

For a normal build, the compiler publishes a directory rather than a single
output file:
Expand All @@ -47,39 +48,70 @@ Use literal direct-child sources in preference order. The exact codec strings
come from `build.json`; the values below are illustrative.

```html
<aval-player width="320" height="320">
<source
src="/motion/av1.avl"
type='application/vnd.aval; codecs="av01.0.00M.10.0.110.01.01.01.0"'
>
<source
src="/motion/vp9.avl"
type='application/vnd.aval; codecs="vp09.00.10.08.01.01.01.01.00"'
>
<source
src="/motion/h265.avl"
type='application/vnd.aval; codecs="hvc1.1.6.L93.B0"'
>
<source
src="/motion/h264.avl"
type='application/vnd.aval; codecs="avc1.640028"'
>
<img slot="fallback" src="/motion.png" alt="">
</aval-player>
<div class="motion-shell">
<aval-player id="motion" width="320" height="320">
<source
src="/motion/av1.avl"
type='application/vnd.aval; codecs="av01.0.00M.10.0.110.01.01.01.0"'
>
<source
src="/motion/vp9.avl"
type='application/vnd.aval; codecs="vp09.00.10.08.01.01.01.01.00"'
>
<source
src="/motion/h265.avl"
type='application/vnd.aval; codecs="hvc1.1.6.L93.B0"'
>
<source
src="/motion/h264.avl"
type='application/vnd.aval; codecs="avc1.42E01E"'
>
</aval-player>
<img id="motion-unavailable" src="/motion.png" alt="" hidden>
</div>

<script type="module" src="/motion.js"></script>
```

```js
// motion.js, resolved by a package-aware web build
import { defineAvalElement } from "@pixel-point/aval-element";
import {
AvalPlaybackError,
defineAvalElement
} from "@pixel-point/aval-element";

const motion = document.querySelector("#motion");
const unavailable = document.querySelector("#motion-unavailable");
function revealPlaybackUnavailable(failure) {
const diagnostics = motion.getDiagnostics();
if (
motion.readiness === "error" &&
diagnostics.lastFailure !== null &&
failure === diagnostics.lastFailure
) {
unavailable.hidden = false;
}
}
motion.addEventListener("error", (event) => {
if (event.detail.fatal) revealPlaybackUnavailable(event.detail.failure);
});
motion.addEventListener("readinesschange", () => {
if (motion.readiness === "interactiveReady") unavailable.hidden = true;
});
defineAvalElement();

try {
await motion.prepare();
} catch (error) {
if (!(error instanceof AvalPlaybackError)) throw error;
revealPlaybackUnavailable(error.failure);
}
```

The `<aval-player>` host does not carry `src`; URLs belong to each codec
candidate. If no candidate is supported, the author-owned fallback remains
visible. Applications can select any authored state without media seeking
code:
candidate. AVAL raises `AvalPlaybackError` when playback cannot run. The
application decides whether to show its sibling image, another renderer, text,
or nothing. Applications can select any authored state without media seeking:

```js
const motion = document.querySelector("aval-player");
Expand Down Expand Up @@ -107,7 +139,7 @@ obligations remain the publisher's responsibility.
## Packages

- `@pixel-point/aval-graph`: deterministic state and route engine.
- `@pixel-point/aval-format`: strict AVAL wire 1.0 parser, validator, and writer.
- `@pixel-point/aval-format`: strict AVAL wire 1.0/1.1 parser, validator, and writer.
- `@pixel-point/aval-compiler`: project 1.0 authoring API and bundle compiler.
- `@pixel-point/aval-player-web`: bounded loader, codec probing, decoder
scheduling, renderer, and page resource management.
Expand All @@ -128,16 +160,19 @@ npm run build
npm run test:browser:reference
```

Browser animation is capability-probed in authored source order. Unsupported
codec candidates fall through to the next `<source>`; when none can run, the
element keeps its optional host-owned fallback slot visible.
Browser animation is qualified in authored source order. A positive WebCodecs
configuration probe remains provisional; unsupported configurations and
codec-specific startup qualification failures fall through to the next
`<source>`. Once `interactiveReady` is published, the selected codec never
hot-switches. When no candidate qualifies, preparation rejects and one fatal
`error` event identifies the failed source generation. AVAL never selects or
reveals alternate application content.

## TODO

- React dedicated component and API.
- More browser tests.
- Render some cool stuff in 3D for the demo instead of that AI-generated loop that I was not able to make look the way I wanted to actually showcase the uninterruptible animation.
- Runtime bundle size optimization

## Documentation

Expand Down
4 changes: 4 additions & 0 deletions apps/playground/fixture-routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/** Public same-origin routes for the playground's distinct asset authorities. */
export const QUALIFIED_FIXTURE_PREFIX = "/__aval_qualified__/";
export const LEGACY_UNSUPPORTED_FIXTURE_PREFIX =
"/__aval_unsupported_v1__/";
Loading
Loading