Files
Portfolio/docs/accessibility/flash-analysis.md

58 lines
4.9 KiB
Markdown
Raw 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.
# Flash analysis evidence
Status date: 2026-09-02
## Shipped surfaces in scope
- Procedural journey scene in its default and scroll-accelerated states.
- Static and `prefers-reduced-motion` poster states.
- Project media: **not applicable yet**. The public asset manifest is intentionally empty while final derivatives and imagery rights await Luca's approval. This record must be extended before any video or animated project asset is published.
## Automated method
`tests/browser/visual-regression.spec.ts` captures the real `.journey-world` at 10 frames per second. The accelerated sequence receives two production wheel inputs before a two-second capture. Static and reduced-motion sequences are captured separately.
Each frame is downsampled to 160×90 RGB pixels. Between adjacent frames, a pixel is classed as a general-flash luminance change when:
1. relative luminance changes by at least 0.1; and
2. the darker state is below 0.8.
A transition is conservatively treated as large-area when one direction affects at least 25% of the sampled viewport. Within every rolling one-second window, opposing large-area transitions are paired into flashes. The test fails above three flashes per second. JSON event evidence is attached to the Playwright result for the live, Static, and reduced-motion sequences.
This deliberately conservative area rule does not use the WCAG small-area exemption. It checks luminance flash, not red-flash chromaticity in isolation; the shipped scene contains no full-field saturated-red strobe, and the manual review below remains required.
## Automated result
| Engine/state | Result | Evidence |
| -------------------------------------------------------- | -------------------------------------------------------------- | ---------------------------------------------------------- |
| Chrome for Testing 151.0.7922.34, accelerated live scene | **PASS — no rolling one-second window exceeded three flashes** | `live-scene-flash-analysis.json` Playwright attachment |
| Chrome for Testing 151.0.7922.34, Static | **PASS — zero qualifying flashes** | `static-flash-analysis.json` Playwright attachment |
| Chrome for Testing 151.0.7922.34, reduced motion | **PASS — zero qualifying flashes** | `reduced-motion-flash-analysis.json` Playwright attachment |
These results came from Playwright 1.62.1 and were reconfirmed in the final successful complete browser run (157 passed, 41 deliberate skips):
```text
npm run test:browser -- tests/browser/visual-regression.spec.ts
```
## Required human/platform evidence — pending, not passed
- [ ] Review the accelerated scene at normal speed and during repeated direction changes; record reviewer, date, display refresh rate, and result.
- [ ] Review every final encoded project video/animation after its exact approved bytes are bound into the public manifest.
- [ ] Run the automated capture in current Firefox and WebKit/Safari-capable infrastructure and attach the corresponding JSON.
- [ ] Run the automated capture in current Edge and the previous stable desktop majors named by the release matrix.
- [ ] Review on the physical iPhone 13 and Pixel 6 during `physical-drive.v1`.
No unsupported engine, physical device, or unpublished project-media result is represented as passed.
## Underpass shade (chapter boundaries)
Every chapter boundary is an overpass deck the car drives beneath (`src/scene/world/underpass.ts`, pure rules in `src/journey/underpass.ts`). While the deck is overhead it dims the sky, the fog and the sky-lit road and terrain terms. By construction:
- **One dark-light cycle per boundary.** The shade follows the deck's distance from the camera: it rises once as the deck arrives and falls once as it leaves. Scrolling back reverses the same single cycle.
- **Maximum dip 35%.** The shade multiplier never goes below 0.65 (`SHADE_PEAK = 0.35`). The headlight throw, lamps and HUD are not dimmed.
- **Edges of at least 300 ms.** The shade is slew-limited: 0 to the 35% peak, or back, takes at least 300 ms (`SHADE_EDGE_SECONDS`), however fast the scroll.
- **A latch of at least 1 s.** Whether a pass shades at all is decided once, as the deck enters the band, and a pass starting within 1 s of the last shaded pass gets no shade (`SHADE_SPACING_MS`). Brisk scrolling (drive speed above 0.5, the exit sign's own gate) and explicit navigation never shade: the deck goes by without dimming or sound.
The worst case is therefore one shade cycle per second, at most **1 Hz**, well under the three-per-second limit, and each cycle is a 35% dip with soft edges, below the general-flash luminance pair the automated method counts at large area. The low tier draws the deck without any shade. Static, reduced-motion and data-saver visits get no scene, so nothing is added there.