Animated Noise Grain
Film grain that boils, without generating noise per frame. Install with npx remotion-ui@latest add animated-noise-grain.
PrimitivePrimitives
$ npx remotion-ui@latest add animated-noise-grainAn emulsion pass for anything that looks too clean.
import { AnimatedNoiseGrain } from "@/remotion/primitives/animated-noise-grain";
<AnimatedNoiseGrain opacity={0.18}>
<YourScene />
</AnimatedNoiseGrain>Pass children to grain a subtree, or drop it into a stack with no children to
grain everything beneath it.
How the noise is made once
The noise itself is a single stitched feTurbulence tile baked into a data URI.
The browser rasterises that URL the first time it decodes it and then caches it
like any other image — and because the URL depends only on props, never on
frame, a 300-frame render rasterises it once.
What changes every step is where the tile is sampled from and which way it is flipped: a hashed sub-tile offset plus one of four mirrorings. The tile stitches, so the offset wraps invisibly, and the flip breaks the translation so the eye reads new grain rather than a sliding texture.
What that saves
Rendering this page's 120-frame preview at 960x540:
| Variant | Wall clock | CPU |
|---|---|---|
| No grain | 7.5s | 20s |
| Grain, cached tile (shipped) | 15.7s | 78s |
Grain, feTurbulence re-seeded every frame | 27.8s | 144s |
Two things to read out of that. Regenerating the noise per frame nearly doubles the render again — which is the whole reason for the cached tile. And grain is not free even when it is cached: it takes the encoder from a 206 kB file to a 15.1 MB one, because noise is incompressible. Most of the gap between the first two rows is H.264, not rendering. Budget for the bitrate, not just the frames.
Blend mode matters more than opacity
overlay is the default and the right choice over footage — it pivots around
mid grey and leaves the extremes alone. Over a near-black plate it does nothing
at all, because there is nothing to modulate. Use screen there: it lifts the
blacks, which is both what makes the grain visible and what grain does in the
shadows of a real print.
This is worth knowing because the failure is silent. The first cut of this page's preview measured as a completely static frame while the component was working perfectly.
Stock
holdInFrames is the film-stock control. Grain that changes on every frame of a
30fps render looks like video noise; holding each pattern for two frames gives
the coarser chatter of film shot at 15fps.
size and density set the grain's coarseness. vignette adds a second,
edge-masked pass, because real film grain is heaviest away from the centre — an
even field of grain reads as a digital filter.
For phosphor rather than emulsion, see scanline-crt. The two stack.
The noise is one stitched `feTurbulence` tile baked into a data URI, so the browser rasterises it once for the whole render. Per-frame variation comes from a hashed sub-tile offset plus one of four mirrorings — never from regenerating noise. Rendering the preview costs about 3% more per frame than the same frame without it.
Agent notes
Install first, then import the copied source component locally. AI guide →
@/remotion/primitives/animated-noise-grain- Use when: frame-level motion primitives and reusable animation wrappers.
- Customize: opacity, size, density, detail, plus copied source for timing, layout, colors, and typography.
- Rule: do not import this component from the
remotion-uinpm package; it is copied into your project.
Usage
import { AnimatedNoiseGrain } from "@/remotion/primitives/animated-noise-grain";
<AnimatedNoiseGrain opacity={0.18}>
<YourScene />
</AnimatedNoiseGrain>API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | - | Content the grain sits over. Omit to use it as a bare overlay. |
| opacity | number | 0.18 | Grain strength. Film sits around 0.12-0.25; above 0.4 reads as damage. |
| size | number | 220 | Tile size on screen, in px. Larger grain is coarser and cheaper. |
| density | number | 0.85 | Noise frequency inside the tile. Higher is finer. |
| detail | number | 3 | Octaves of noise. 1 is smooth, 4 is gritty. |
| holdInFrames | number | 2 | Frames each pattern is held. 2 gives the 15fps chatter of film. |
| blendMode | CSS mix-blend-mode | "overlay" | How the grain composites. Use `screen` over a near-black plate — overlay pivots on mid grey and does nothing to blacks. |
| colored | boolean | false | Keep the noise coloured instead of desaturating it to silver. |
| vignette | number | 0.35 | Extra grain in the corners, where film grain actually lives. |
| seed | number | 1 | Changes the pattern without changing any other prop. |