webdevpuneet.com

Canvas Noise Texture Background — Free Animated Film Grain Overlay

Canvas Noise Texture Background · Animations · Plain HTML, CSS & JS · Live preview

CategoryAnimations

Copy & paste

Source Code

<section class="cnt-wrap">
  <canvas class="cnt-canvas" id="cntCanvas"></canvas>
  <div class="cnt-content">
    <span class="cnt-tag">canvas 2d · imagedata grain</span>
    <h1>Studio Nocturne</h1>
    <p>A subtle film-grain layer generated by writing raw pixel data every few frames.</p>
    <button class="cnt-btn">Book a session</button>
  </div>
</section>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Direct ImageData writes
Random grayscale bytes written straight to the pixel buffer.
No shape-drawing overhead
putImageData blits the whole frame in one call.
Deliberately low resolution
35% backing-store size, upscaled by CSS for free.
Throttled refresh rate
12fps regeneration reads as texture, not flicker.
Blend-mode compositing
overlay blend interacts with underlying colors and gradients.
Reduced-motion aware
A static frame replaces the interval when requested.
Click-through overlay
pointer-events: none keeps content beneath fully interactive.
Zero dependencies
Pure Canvas 2D and vanilla JS.

About this UI Snippet

Canvas Noise Texture Background — Raw Pixel Grain Over Dark Sections

Screenshot of the Canvas Noise Texture Background snippet rendered live

Film grain is the texture that makes a flat dark section feel like it has depth and material to it — the difference between a plain black background and one that looks like it was shot on film. This snippet generates that grain by writing random grayscale values directly into a canvas ImageData buffer, rather than drawing thousands of individual shapes, and throttles the refresh rate so it reads as texture instead of strobing static.

Writing pixels directly instead of drawing shapes

paintNoise() creates an ImageData object sized to the canvas and walks its underlying Uint8ClampedArray four bytes at a time — red, green, blue, alpha — setting R, G, and B to the same random 0-255 value (grayscale) and alpha to fully opaque. ctx.putImageData() then blits that entire buffer to the canvas in one call. This is dramatically cheaper than the equivalent ctx.fillRect() per pixel would be, because it bypasses the canvas's normal shape-drawing pipeline entirely and writes memory directly.

Small canvas, upscaled by CSS

Grain doesn't need to be high-resolution to read as grain — random noise looks like random noise at almost any pixel density. resize() deliberately sizes the canvas backing store to 35% of the wrapper's dimensions, and CSS stretches it to fill the section. That keeps the ImageData allocation and putImageData write small and fast, since both scale with total pixel count.

Throttled refresh, not 60fps

Regenerating fresh noise on every requestAnimationFrame tick would refresh 60 times a second — fast enough to read as flickering static rather than texture, and wasteful for a background element. Instead, setInterval(paintNoise, 1000 / 12) refreshes at a deliberately low 12fps, which is fast enough to feel alive and un-static but slow enough to stay a background texture rather than a distraction.

Compositing, not just opacity

The CSS sets mix-blend-mode: overlay alongside a moderate opacity, so the grayscale noise blends with whatever color is beneath it — lightening dark areas and darkening light ones — rather than simply sitting on top as a flat gray haze. That combination is what makes the effect read as texture *in* the section rather than a layer *over* it. It also respects prefers-reduced-motion, painting a single static frame and skipping the interval entirely when that preference is set. Pair it with a noise background variant or a canvas mesh gradient for a more colorful companion texture.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA subtle grain layer overlays the dark hero section immediately.
  2. 2
    Look closely at the textureGrain refreshes about 12 times per second, not every frame.
  3. 3
    Check the blend modemix-blend-mode: overlay lets grain interact with colors beneath it.
  4. 4
    Resize the windowThe small backing-store canvas rescales and keeps refreshing.
  5. 5
    Enable reduced motionThe interval is skipped; one static grain frame renders instead.
  6. 6
    Tune the densityChange the 0.35 resolution factor or the FPS constant.

Real-world uses

Common Use Cases

Dark hero sections
Add tactile film grain behind a headline, writing random grayscale bytes straight into an ImageData buffer.
Editorial and photography brands
Use a literal grain motif for film or photo studios, refreshed at 12 fps so it reads as texture, not flicker.
Mesh gradient layering
Layer over a canvas mesh gradient to give smooth colour fields a printed, material feel with real depth.
Album and release pages
Give a music landing page a moody backdrop, with the canvas drawn at 35% resolution and upscaled by CSS for free.
Static grain comparison
Compare with the static noise background when no animation is needed and a single image is enough.
Related: Canvas Wave Background
See the Canvas Wave Background for a related animations pattern worth pairing with this one.
Related: Text Scatter Assemble
See the Text Scatter Assemble for a related animations pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

ctx.createImageData gives direct access to the canvas's underlying Uint8ClampedArray, so setting a pixel is just writing four numbers (R, G, B, A) to an array index, and ctx.putImageData blits the entire buffer to the canvas in a single call. Issuing a fillRect (or arc) per pixel instead would go through the canvas's full shape-drawing and compositing pipeline for every single pixel, which is dramatically slower at any meaningful resolution.

Regenerating fresh random noise 60 times a second (once per requestAnimationFrame tick) reads as flickering static rather than a settled texture, and does so while burning CPU for no additional visual benefit as a background element. Throttling the regeneration to roughly 12fps via setInterval keeps the grain feeling alive without the strobing quality of a full-rate refresh, and uses a fraction of the CPU.

Random noise looks like random noise at essentially any pixel density, so there is no visual benefit to generating it at full viewport resolution. Sizing the canvas backing store to about 35% of the wrapper and letting CSS stretch the element to fill it keeps both the ImageData allocation and the putImageData write small, since their cost scales directly with total pixel count.

Opacity alone would just lay a flat translucent gray haze on top of the section, which reads as a layer sitting over the content rather than texture within it. overlay blend mode instead combines the grayscale noise with whatever color is directly beneath each pixel — lightening darker areas and darkening lighter ones — so the grain appears to be part of the surface itself rather than an added film on top of it.

Move the resize() call and the paintNoise() interval setup into a mount effect, storing the interval id so it can be cleared in the cleanup function on unmount. Since the effect draws to a canvas ref rather than relying on requestAnimationFrame, there's no animation frame to cancel — just make sure the interval is torn down so it doesn't keep running against a canvas element that no longer exists in the DOM.