WebberUI

Grain Overlay

A canvas grain overlay that flickers frame by frame, in fine / coarse / vintage textures, adding film-like noise to gradients and images.

Loading preview…
npx shadcn@latest add https://webberui.com/r/grain-overlay.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

0.22
1
0.55
24
<GrainOverlay />

Installation

npx shadcn@latest add https://webberui.com/r/grain-overlay.json

Or, once registries are configured in components.json, install it as @webberui/grain-overlay.

Usage

GrainOverlay is one absolutely positioned layer: drop it into any relative container and it covers the whole background. Adding isolate to the container keeps the blend mode confined to the container instead of bleeding through to the page behind it.

import { GrainOverlay } from "@/components/ui/grain-overlay";

<div className="relative isolate overflow-hidden rounded-xl bg-gradient-to-br from-indigo-500 to-pink-500">
  <GrainOverlay variant="coarse" />
  {/* your content */}
</div>

Props

PropTypeDefaultDescription
variant"fine" | "coarse" | "vintage""fine"Grain texture preset; any parameter you do not override follows this preset
intensitynumberPer variantOverall opacity of the overlay (0–1)
grainSizenumberPer variantEdge length of a single grain (CSS px, integer)
contrastnumberPer variantGrain contrast (0–1) — the light/dark amplitude of each grain
blendModeGrainBlendModePer variantBlend mode (overlay / soft-light and so on)
fpsnumberPer variantFlicker update rate (1–60)
animatedbooleantrueWhether to flicker frame by frame; when false it is a single static frame
frameCountnumber8Number of noise frames pre-generated and cycled (1–24)

The defaults for the three variant presets: fine (grainSize 1, delicate and understated), coarse (grainSize 3, high contrast, rough), vintage (soft-light, slow flicker, imitating old film stock). Passing any numeric prop explicitly overrides that preset's value.

How it works

  • The grain is drawn on canvas frame by frame: frameCount independent low-resolution grayscale noise frames are pre-generated, and each frame — throttled by fps — picks one at random and scales it up to fill the layer, producing a convincing film flicker.
  • Grain brightness is centered on mid-gray (128) with contrast setting the amplitude; paired with the overlay blend mode, mid-gray leaves the underlying color untouched and only the light and dark grains are layered on.
  • grainSize is measured in CSS pixels, and image smoothing is turned off (blocky upscaling) to keep the grains sharp and consistently sized even on high-DPR displays.
  • Adjusting intensity and blendMode is a pure styling change and does not rebuild the noise; only changes to grainSize, contrast, fps, and the like regenerate the frames.
  • A ResizeObserver watches the container size, and rebuilding the noise is debounced; the rAF, timers, and observers are all cleaned up on unmount.

Accessibility

  • The overlay carries aria-hidden and pointer-events-none — decorative only, so it interferes with neither assistive technology nor interaction with the layer beneath.
  • When the user has "reduce motion" enabled at the system level, the frame-by-frame flicker stops automatically and a single static noise frame is rendered instead.

On this page