# YT Dimmer

> Bright scenes? Dimmed 😎

Source: https://www.harxit.com/products/yt-dimmer

- Author: [Harshit Sharma](https://www.harxit.com/about)
- Published: January 1, 2025
- Technologies: TypeScript, Chrome Extension, Canvas API
- Website: [Open project](https://chromewebstore.google.com/detail/yt-dimmer/gfhiofmhoajnkaogkaiccddegolcmnkj)

I watch videos at night with the room lights off. The problem is when a dark video suddenly cuts to a white slide, website, or background.

Lowering the display brightness fixes that scene but makes the rest of the video too dark. I wanted the video to dim only when its content became bright, then restore itself when the scene changed.

![YT Dimmer automatically reducing the brightness of a white scene in a YouTube video.](https://www.harxit.com/products/yt-dimmer/yt-dimmer-poster.webp)

[Watch video](https://www.harxit.com/products/yt-dimmer/yt-dimmer-demo.mp4)

_The video is dimmed. The rest of YouTube is left alone._

## What I found before building

At the time, the extensions I found either dimmed the page around the player or applied a fixed filter to the video.

A page dimmer does not help when the bright area is inside the video. A fixed filter helps with bright scenes but makes already-dark scenes worse. Neither reacts to what is playing.

That gave me a narrow requirement: inspect the video itself, detect when it becomes bright, and adjust only the video without asking the viewer to do anything.

## The first detector

The MVP reduced each frame to one number.

It copied the frame into a hidden 64 × 36 canvas, sampled a fraction of the pixels, and calculated their average luminance. When the average crossed a threshold, the extension applied a CSS brightness filter to the video.

The detector ran about 30 times per second. Everything happened in the browser; no frames or screenshots were uploaded anywhere.

The idea worked, but using one average exposed several problems:

- A small white region could be very bright without moving the frame average enough.
- A sudden white frame and a stable bright scene produced similar values but needed different responses.
- Values near the threshold could make the filter move back and forth.
- A permanent timer kept doing work even when the video was paused.

Those problems became the plan for version 2.

## Replacing one threshold with three signals

The current detector measures three properties for every sampled frame:

- Mean luminance for the overall scene
- Peak luminance for the brightest sampled area
- The percentage of sampled pixels that are already very bright

It compares those values with the previous frame and a slowly adapting baseline. A sharp increase is treated as a flash and receives a fast, strong response. A stable white background receives proportional dimming without being counted as a new flash on every frame.

Each frame is reduced to a 48 × 27 analysis canvas, and only every second pixel is sampled. The detector gets enough information without processing the video at its original resolution.

I moved the algorithm into a pure state machine instead of leaving it mixed with the browser code. This made the behaviour deterministic and easy to test. The tests cover full-frame flashes, small bright regions, gradual fades, stable bright scenes, rapid cuts, and recovery after dimming.

## Fast dimming, slow recovery

Detection is only half of the interaction. The filter also has to move at the right speed.

Slow dimming means the bright frame has already been shown. Equally fast recovery makes the video flicker between brightness levels. YT Dimmer uses an asymmetric response:

- Dimming starts with a 40 ms transition.
- Recovery uses a 620 ms eased transition.
- A ten-frame hold stops the filter from immediately bouncing back.
- Video brightness never drops below 16%.

It reacts quickly when brightness jumps and returns to normal gradually.

## Analysing only the video that matters

Modern pages can contain several video elements: the main player, previews, ads, and hidden players left behind during navigation.

YT Dimmer selects the largest visible video that is currently playing. It follows decoded frames through `requestVideoFrameCallback` when the browser supports it and uses a timed fallback otherwise. Analysis stops when there is no eligible video to inspect.

Each player keeps separate detector state. If YouTube replaces the video element during navigation, the extension finds the new one. Existing inline filters and transitions are preserved and restored instead of being overwritten permanently.

This removed the MVP's permanent 30 FPS loop and tied the work to actual video playback.

## When the browser blocks frame access

Some protected cross-origin videos cannot be read through a canvas. There is no reliable brightness value in that case.

YT Dimmer does not guess and apply a filter blindly. It leaves the player unchanged, reports that analysis is unavailable, and retries later in case the video source changes.

## From my browser to the Chrome Web Store

The first version only needed a threshold and a dimming level. Once other people started using it, the extension needed better controls around the detector.

Version 2 added Gentle, Balanced, and Maximum presets, per-site pause, a keyboard shortcut, scheduled night boost, calibration, and settings import/export. The popup also shows when protection is active and which video is being analysed.

All processing and settings remain local. There is no account, backend, or video collection.

I shared the extension while building it. It reached [40 users](https://x.com/itsharxit/status/1969975264768938001), became [Featured on the Chrome Web Store](https://x.com/itsharxit/status/1970691477392236679), and later launched on [Product Hunt](https://x.com/itsharxit/status/1970750583561683113).

YT Dimmer has 3,000+ lifetime installs. One user described the exact use case in a review:

> “No more squinting or covering my eyes during action scenes.” — Sujal

## Result

The canvas and CSS filter were straightforward. Most of the work was deciding what counts as a bright scene, separating a sudden change from sustained brightness, and making the response quick without making the video flicker.

The current detector handles sudden flashes and sustained bright scenes differently, follows actual video playback, and leaves the player alone when it cannot inspect a frame. It solves the original problem without keeping every video permanently dimmed.

If bright video scenes bother you at night, [YT Dimmer is available on the Chrome Web Store](https://chromewebstore.google.com/detail/yt-dimmer/gfhiofmhoajnkaogkaiccddegolcmnkj).
