Skip to main content

Ambient Glow Video

A video with YouTube-style ambient mode: every frame is also drawn onto a tiny 64x36 canvas sitting behind the player, blurred far past recognition, so the surface glows with whatever colors are on screen right now. drawImage runs in the normal paint cycle — no WebGL, no compositing tricks, near-zero cost.

CardReactCanvas 2DTailwind CSS
CSSTailwind

Manual

Create a file and paste the following code into it.

ambient-glow-video.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
"use client";

import { useEffect, useRef } from "react";
import { cn } from "@/lib/cn";

/* ------------------------------------------------------------------ */
/*  AmbientGlowVideo                                                   */
/* ------------------------------------------------------------------ */

interface AmbientGlowVideoProps {
  src: string;
  poster?: string;
  /** Blur radius of the glow canvas in px [Optional, default: 60] */
  blurAmount?: number;
  /** Glow opacity 0-1 [Optional, default: 0.85] */
  intensity?: number;
  autoPlay?: boolean;
  muted?: boolean;
  /** Show native controls [Optional, default: true] */
  controls?: boolean;
  className?: string;
}

/* Tiny canvas — the blur erases any pixel detail anyway, and a 64x36 draw
   per frame costs nearly nothing. */
const CANVAS_W = 64;
const CANVAS_H = 36;

/**
 * A video with YouTube-style ambient mode: every frame is also drawn onto a
 * tiny canvas sitting behind the player, blurred far past recognition, so
 * the page glows with whatever colors are on screen right now. drawImage
 * runs in the normal paint cycle — no WebGL, no compositing tricks.
 * @param {string} src - Video source [Required]
 * @param {number} blurAmount - Glow blur radius [Optional, default: 60]
 */
export function AmbientGlowVideo({
  src,
  poster,
  blurAmount = 60,
  intensity = 0.85,
  autoPlay = false,
  muted = false,
  controls = true,
  className,
}: AmbientGlowVideoProps) {
  const videoRef = useRef<HTMLVideoElement | null>(null);
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const rafRef = useRef(0);

  useEffect(() => {
    const video = videoRef.current;
    const ctx = canvasRef.current?.getContext("2d");
    if (!video || !ctx) return;

    const draw = () => {
      if (!video.paused || video.readyState >= 2) {
        try {
          ctx.drawImage(video, 0, 0, CANVAS_W, CANVAS_H);
        } catch {
          /* cross-origin canvas taint — keep playing without the glow */
        }
      }
      rafRef.current = requestAnimationFrame(draw);
    };
    rafRef.current = requestAnimationFrame(draw);
    return () => cancelAnimationFrame(rafRef.current);
  }, []);

  return (
    <div className={cn("relative w-full", className)}>
      <canvas
        ref={canvasRef}
        width={CANVAS_W}
        height={CANVAS_H}
        aria-hidden
        className="pointer-events-none absolute inset-0 h-full w-full rounded-xl"
        style={{
          filter: `blur(${blurAmount}px)`,
          opacity: intensity,
          transform: "scale(1.08)",
        }}
      />
      <video
        ref={videoRef}
        src={src}
        poster={poster}
        controls={controls}
        playsInline
        preload="metadata"
        autoPlay={autoPlay}
        muted={muted}
        loop
        className="relative z-[1] w-full rounded-xl"
      />
    </div>
  );
}

Update the import paths to match your project setup.

Similar components

MaxNew

Graph Plot

Cascade Card Deck

Pin Dock List

Borealis Card

Install via CLI

Resource details

PublishedJuly 17, 2026
CategoryCard
ReactCanvas 2DTailwind CSS