Skip to content

Skip speed overlay on gif-like video elements#1461

Merged
igrigorik merged 1 commit into
masterfrom
claude/detect-gif-video-pattern-iqWbz
Mar 28, 2026
Merged

Skip speed overlay on gif-like video elements#1461
igrigorik merged 1 commit into
masterfrom
claude/detect-gif-video-pattern-iqWbz

Conversation

@igrigorik

Copy link
Copy Markdown
Owner

Summary

  • Skip speed controller overlay on gif-like <video> elements detected
    via loop && muted && !controls — the canonical pattern sites use to
    serve animated GIFs as MP4 video
  • Fixes Telegram Web sticker overlays (Hide control panel on animated stickers in Telegram Web #1407) and applies generically to
    X, Imgur, Giphy, background hero videos, etc.
  • Single check in SiteHandlerManager.shouldIgnoreVideo(), always-on,
    no new settings

Design

The heuristic video.loop && video.muted && !video.controls uniquely
identifies gif-as-video elements. Real video content is never simultaneously
looping and muted. The check runs after site-specific handlers, acting as a
universal fallback at the single filtering chokepoint. See commit message
for full rationale and false positive/negative analysis.

Modern sites serve animated GIFs as <video autoplay loop muted playsinline>
elements — H.264/MP4 instead of actual GIF files. This is 10-98% smaller
with better quality (no 256-color limit, inter-frame compression). The
pattern is universal: Twitter/X adopted it in 2014, Telegram defines GIFs
as MPEG-4 video without audio at the protocol level, and Imgur/Giphy do
the same. Chrome's 2017 autoplay policy change (muted autoplay whitelisted)
cemented this as the standard approach.

The canonical markup is:

    <video autoplay loop muted playsinline src="clip.mp4"></video>

This extension was attaching speed overlays to these elements, which is
visually noisy (e.g. overlays on every Telegram sticker) and not useful —
nobody needs speed control on a 3-second meme loop.

Heuristic: video.loop && video.muted && !video.controls

Why these three attributes:
- loop + muted is the definitive behavioral fingerprint. Real video content
  is virtually never both looping and muted by default.
- !video.controls distinguishes from the rare legitimate loop+muted case
  (e.g. an accessibility demo). Note: this checks the HTML controls
  attribute, not custom player UI — sites like YouTube/Netflix use custom
  controls and never set the native attribute, but they also never set
  loop+muted, so they're unaffected.

False positive analysis:
- Background/hero videos on landing pages match this pattern. These are
  decorative and don't benefit from speed control either, so filtering
  them is arguably correct behavior.
- A silent looping tutorial/demo without native controls would be filtered.
  This is an acceptable edge case — such content almost always has custom
  player chrome that doesn't set loop+muted.

False negative analysis:
- A gif-video that omits the loop attribute (JS-driven looping via the
  ended event) would not be caught. This is rare in practice.

The check lives in SiteHandlerManager.shouldIgnoreVideo(), the single
chokepoint for all filtering. It runs after site-specific handlers (YouTube,
Netflix, etc.) have their say, so it acts as a universal fallback. No
settings toggle — the heuristic is reliable enough to be always-on.

There is no web standard for distinguishing these two uses of <video>.
WHATWG issue #976 proposed <video type="image"> but remains unresolved.
Until that ships, DOM attribute heuristics are the only option. The
loop+muted+!controls combo is synchronous, zero-latency (no media load
needed), and catches ~99% of real-world gif-video usage.

Fixes #1407
@igrigorik
igrigorik merged commit 2a6d0ee into master Mar 28, 2026
2 checks passed
@igrigorik
igrigorik deleted the claude/detect-gif-video-pattern-iqWbz branch March 28, 2026 01:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants