The CSSWG resolved that a media element skipped by content-visibility should be muted
and should not fire its media events while it stays skipped. This sampler exercises the parts of
that behavior that are observable from script, so the same page can be loaded on a stock browser
and on a build with the feature enabled and the two results compared directly.
Stock behavior (feature off) - just load this page and press Run all cases.
Feature on:
chrome --enable-blink-features=ContentVisibilityMediaPlayback \
https://static.januschka.com/i-40781006/
# or, for everything experimental:
chrome --enable-experimental-web-platform-features
The media is sample-5s.mp4 (5 s, H.264 + AAC), fetched once and reused as a blob URL by every case, so the page needs network access on first load. Cases A-E are automatic. Cases F-H need a click or headphones, because the signal they check (actual audibility, picture-in-picture) is not readable from script.
| Case | Feature off (stock) | Feature on |
|---|---|---|
A - content-visibility: hidden ancestor | immediate | deferred |
B - content-visibility: auto, scrolled out | immediate | deferred |
| C - flush on reveal | n/a (never deferred) | event arrives on reveal, in order |
D - flush on currentTime read | n/a | event arrives while still skipped |
| E - event coalescing | 3 events | 1 event (last value wins) |
| F - audibility | audible while skipped | silent while skipped |
| G - autoplay interaction | blocked as usual | observe whether reveal produces sound |
| H - picture-in-picture | audible | audible (PiP is exempt) |
Script cannot read the effective volume, so the mute half of the behavior is only checkable by ear (case F) or via the tab audio indicator. The event half is fully scriptable, which is what cases A-E rely on.
Case E documents that same-type events are collapsed while skipped, so intermediate values are not delivered and the relative order against other deferred event types changes.
Case G covers the interaction with the autoplay policy: while the element is treated as
effectively silent, setVolume() and setMuted() take the muted path, so
it is worth confirming what happens to audibility at the moment the element is revealed.
Case D exists because the patch flushes deferred events from the currentTime
getter, which makes a read of a hot attribute observable as an event-timing change.