Mischief

68 / Scenes

Grain Overlay

Film grain for any positioned box, which incidentally fixes the banding a wide gradient shows on a good monitor.

Reel one, take four

A flat gradient bands on wide screens. The grain hides the steps and gives the panel a surface.

Installation

Copy the source into your project, or keep it behind a package.

npx shadcn@latest add Tinkerers-Labs/mischief-ui/grain-overlay
import { GrainOverlay } from "mischief-ui/grain-overlay"

Or paste it in yourself. The source imports the shared cn helper from @/lib/utils, so point that at your own copy.

registry/default/grain-overlay/grain-overlay.tsx
"use client" import * as React from "react"import { cn } from "@/lib/utils" export type GrainOverlayProps = React.HTMLAttributes<HTMLDivElement> & {  /** Higher is finer. Around 0.65 reads as film, 0.2 as coarse paper. */  frequency?: number  opacity?: number  blend?: "overlay" | "soft-light" | "multiply" | "screen" | "normal"  /** Shifts the grain a few times a second the way projected film does. */  animated?: boolean} 

Usage

export function Panel() {
  return (
    <div className="relative isolate overflow-hidden">
      <div className="bg-gradient-to-br from-primary to-background p-12">
        <h2>Reel one, take four</h2>
      </div>
      <GrainOverlay />
    </div>
  )
}

The practical reason to use it

A gradient across a wide screen has fewer available steps than it has pixels, so it arrives in visible bands. Adding noise breaks the boundary between one step and the next, and the eye stops finding the edges. This is the same trick print has used for a century, and it is the reason to reach for grain even when you do not want the texture.

It sits above the content and ignores the pointer, so it can be dropped into a card or a hero without changing anything underneath it. The parent needs a positioning context and, usually, overflow hidden.

Still by default

Animated grain is a full repaint several times a second for an effect most readers will not consciously notice, so it is off unless you ask. When it is on it stops entirely under reduced motion, because a texture that crawls is exactly the kind of movement that setting exists to remove.

API

frequencynumberHigher is finer. Around 0.65 reads as film and 0.2 as coarse paper. Defaults to 0.65.
opacitynumberStrength of the grain. Defaults to 0.22.
blend"overlay" | "soft-light" | "multiply" | "screen" | "normal"How the grain mixes with what is underneath. Defaults to "overlay".
animatedbooleanShifts between four grains about eight times a second, the way projected film does. Off by default.

Accessibility

Hidden from assistive technology and transparent to the pointer. The animated variant does nothing at all when reduced motion is set.