SecretoCho
menu

component 21 / 28WORKING

SC Noise

type
Code Component
platform
Framer
license
SecretoCho Design
needs JavaScript

demo · runs with JavaScript

fig 00 · live demo, ported from Noise.tsx

Animated canvas grain: a random grey tile re-rolled every N frames and repeated over the layer, with size, scale and alpha controls.

Noise lays an animated film-grain texture over whatever sits behind it. A small tile of random grey pixels is re-rolled every few frames and repeated across a canvas that fills the layer; tile size, horizontal and vertical scale, refresh interval and pixel alpha are the controls.

  • Visual
  • Effects
  • FX

Main

Overview

Built With
React (hooks), Canvas 2D, requestAnimationFrame, Framer Code Component API
Layout Support
Fixed width and height (intrinsic 400 × 400); the canvas fills the layer
Orientation
Full-layer overlay
Ideal Use Case
Grain over hero sections, dark backgrounds, photo treatments

Tags

  • noise
  • grain
  • canvas
  • texture
  • overlay
  • requestAnimationFrame

Code comment

Noise.tsx has no doc comment: lines 1–6 hold only Framer’s annotations.

Dependencies

Technologies
None (Canvas 2D)
Notes
No libraries; one requestAnimationFrame loop while mounted.

Complexity

Logic Level
Low (random tile, repeat pattern, frame counter)
Runtime Cost
Low–Medium (CPU fills size² pixels per refresh; the rAF loop runs every frame and redraws every Nth)
Component Size
~4 KB TSX (3,978 bytes, 142 lines)
Notes
The loop stops through an isMounted flag on unmount; the resize listener is removed.

Guide

Instructions

  1. Drop Noise over the content it should texture (it is fixed-size: 400 × 400 by default; resize the layer).
  2. Set Size (the random tile, 100–750 px) and Scale X / Y (0.5–4) for grain coarseness.
  3. Set Refresh Interval in frames (lower = faster flicker) and Alpha (0–255) for strength.
  4. Set the layer to ignore pointer events in Framer if content below must stay clickable.

Examples / Presets

  • Default

    The source defaults: a 250 px tile, scale 1, re-rolled every 6 frames at alpha 15.

  • As on the site

    patternSize
    200
    patternRefreshInterval
    50
    patternAlpha
    20 (the old site’s contact and help pages; their layer’s 0.5 opacity is not a component prop)
  • Coarse

    patternScaleX
    3
    patternScaleY
    3
    patternRefreshInterval
    4
    patternAlpha
    40
  • Film

    patternSize
    150
    patternRefreshInterval
    2
    patternAlpha
    28

Mechanics

How it Works

  • Tile: an offscreen canvas of patternSize × patternSize; updatePattern writes a random grey (one value for R, G and B) and patternAlpha into every pixel.
  • Draw: drawGrain clears the visible canvas and fills it with createPattern(tile, "repeat").
  • Loop: requestAnimationFrame runs every frame; when frame % patternRefreshInterval is 0 the tile is re-rolled and redrawn.
  • Resize: the canvas backing store is set to the element size × devicePixelRatio and the context scaled by Scale X / Y; a window resize listener repeats this.
  • Cleanup: an isMounted flag ends the loop and the resize listener is removed when the effect re-runs or the component unmounts.

Performance & Tuning

  • Cost is patternSize² pixels per refresh on the CPU: 250² = 62,500, 750² = 562,500. Keep the tile small and raise the interval on weak devices.
  • The rAF callback runs every frame even between refreshes (it only counts); a long interval still keeps the loop alive.
  • Scale X / Y above 1 enlarge the grain without a bigger tile, the cheaper way to coarser noise. Below 1 the grain covers only that fraction of the canvas (0.5 × 0.5: the top-left quarter), because the clear and the fill run under the scale.

Compatibility & Limits

  • Evergreen browsers with Canvas 2D.
  • Fixed layout size only (@framerSupportedLayoutWidth / Height fixed).
  • No prefers-reduced-motion check in the file.
  • Refresh Interval counts requestAnimationFrame callbacks, so the grain changes twice as fast on 120 Hz screens.
  • Scale X or Y below 1 leaves part of the canvas without grain (above).

Quality

Accessibility

  • Purely decorative: give the layer aria-hidden="true".
  • Constant flicker can bother motion-sensitive visitors; consider a long interval or pausing under prefers-reduced-motion in a fork.

Security & Privacy

  • No network calls, no storage, no permissions, no third-party libraries.
  • Math.random only; one window resize listener, removed on cleanup.

Reference

Props

nametypedefaultdescription
patternSizenumber250Size. Size of the noise pattern tile (in pixels), 100–750.
patternScaleXnumber1Scale X. 0.5–4. Below 1 the grain covers only part of the canvas.
patternScaleYnumber1Scale Y. 0.5–4. Below 1 the grain covers only part of the canvas.
patternRefreshIntervalnumber6Refresh Interval. Frequency of pattern refresh in frames (lower = faster), 1–100.
patternAlphanumber15Alpha. Opacity of the noise pixels (0–255).

Notes & Tips

  • Alpha 10–20 reads as film grain; 60+ as static.
  • The greys (0–255 at one alpha) lighten and darken evenly only over a mid-grey fill; over a dark fill they mostly lighten.

Troubleshooting / FAQ

The grain does not cover the layer after a resize

The canvas resizes on the window resize event only; a layer resized without a window resize keeps the old backing size.

High CPU

Lower Size or raise Refresh Interval; the cost is the tile area per refresh.

Source

Header comment

Noise.tsx has no doc comment: lines 1–6 hold only Framer’s annotations.

props [REF]

Key excerpt

Grain is a tile of random grey pixels at one alpha, re-rolled on the CPU and stamped across the canvas as a repeating pattern.

Noise.tsx · lines 84–102 · tsx
        const updatePattern = () => {            for (let i = 0; i < patternPixelDataLength; i += 4) {                const value = Math.random() * 255                patternData.data[i] = value                patternData.data[i + 1] = value                patternData.data[i + 2] = value                patternData.data[i + 3] = patternAlpha            }            patternCtx.putImageData(patternData, 0, 0)        }        const drawGrain = () => {            ctx.clearRect(0, 0, canvas.width, canvas.height)            const pattern = ctx.createPattern(patternCanvas, "repeat")            if (pattern) {                ctx.fillStyle = pattern                ctx.fillRect(0, 0, canvas.width, canvas.height)            }        }

Credits

Credits

© SecretoCho Design

license
SecretoCho Design

SecretoCho Design

Disclaimer

Proves

Hiring a design engineer?

I’m open to senior design engineer roles: remote from Belgrade, or relocation with visa sponsorship. Write to me, or read the CV first. Client work stays selective, one project at a time.

sc@secretocho.design

read the CV · LinkedIn