Backgrounds✨ Premium

Film Noir

Pure-CSS film grain layer: feTurbulence noise flickering in steps, horizontal scanlines and radial dark vignette — no dependency, no JS.

SVGAnimationFilm

You'll get access to the interactive demo with a free account.

This effect is part of Effect.Labs — 811 vanilla effects, some free, some premium. Backgrounds has 50 effects, including 6 free. Explore the category →

3 usage examples

Full-screen hero — Film noir festival — Film Noir example 1

① Full-screen hero — Film noir festival

WhenThe hero section of an event site (festival, screening, film archive) needs a film-stock atmosphere across the full surface, without a background image.
WhyAnimated grain and a strengthened vignette immediately evoke analogue cinema. The title and tagline at centre remain readable on the dark background — the effect contributes without competing with the text.
SettingsBackground #0a0a0a (pure black), gradient layer removed, opacity: 0.35 on .ng-film-grain, vignette boosted to rgba(0,0,0,0.78).
Editorial header — Analogue photography magazine — Film Noir example 2

② Editorial header — Analogue photography magazine

WhenThe header of an article or photo portfolio where the grain texture should evoke the film medium, without a background image.
WhyThe texture overlays any background tint; it reinforces a darkroom atmosphere and gives character to an otherwise flat monochrome header.
SettingsBackground #1a140a (photo brown), gradient swapped for a warm amber radial, opacity: 0.52, scanlines at rgba(0,0,0,0.08), flicker 0.08s steps(4).
Full-screen player — Movie poster before playback — Film Noir example 3

③ Full-screen player — Movie poster before playback

WhenIn a video player or movie detail page, before the user hits play: the poster fills the entire available surface, with animated grain and scanlines across the full screen.
WhyAt full-screen scale, the animated grain and scanlines are immediately legible and set the cinematic mood from the first second — no background image, no JavaScript.
SettingsBackground #0a0a12 (dark blue-black), centred deep-violet radial gradient, opacity: 0.42 on .ng-film-grain, vignette boosted to rgba(0,0,0,0.72) 100%.

How it works

The effect is a stack of four position: absolute; inset: 0 layers inside a .ng-film-scene container. The key layer is .ng-film-grain, an empty <div> whose inline style carries filter: url(#ng-film-filter). An SVG filter — <feTurbulence type="fractalNoise" baseFrequency="0.65" numOctaves="3" stitchTiles="stitch">, defined once on the page — generates a fractal noise map (Perlin noise). A <feColorMatrix> converts the noise into semi-transparent white; at opacity: 0.4, it produces the film grain texture.

The @keyframes ngFilmFlicker animation (0.1 s, steps(3)) translates .ng-film-grain without interpolation across three positions: (0,0), (−2 px, 1 px), (1 px, −1 px). steps(3) ensures hard jumps — the grain appears re-seeded each frame while the SVG map remains static, because the crop window is what moves. At 0.1 s × 3 positions, the grain shifts at ≈ 30 fps, mimicking 24–30-frame film stock.

Scanlines are a repeating-linear-gradient(0deg, transparent 2px, rgba(0,0,0,0.05) 4px) on .ng-film-lines — 4 px pitch, opacity 0.05. The vignette is a radial-gradient(transparent 50%, rgba(0,0,0,0.6) 100%) on .ng-film-vignette, darkening the edges without touching the center.

No JavaScript is involved — the effect is 100% CSS + SVG. The .ng-film-gradient layer (two coloured radial gradients) is entirely optional: swap its colours or remove it entirely to change the chromatic mood without touching the grain.

Accessibility

  • No @media (prefers-reduced-motion: reduce) block is present in the provided code: the ngFilmFlicker animation runs continuously. For motion-sensitive users, add .ng-film-grain { animation: none; } inside that media query.
  • The effect is purely decorative: add role="img" and a descriptive aria-label on .ng-film-scene if the block carries visual meaning, otherwise mark it aria-hidden="true".
  • The vignette darkens edges but spares the center: white text centred on #1a1a2e achieves a contrast ratio of ≈ 13:1 (WCAG AA confirmed).
  • No JavaScript, no interaction — no keyboard-navigation issues attributable to the effect itself.
  • Without SVG Filter support (very old environments), .ng-film-grain renders transparent; scanlines and the CSS vignette remain active — graceful degradation with no errors.

Browser compatibility

Requires SVG Filters (feTurbulence) and CSS animations — supported in all modern browsers since 2016. Zero external dependencies, no JS.

Chrome 90+✓ Full
Firefox 88+✓ Full
Safari 14+✓ Full
Edge 90+✓ Full
Mobile iOS 14+✓ Full
Android Chrome✓ Full

Without SVG Filter support, <code>.ng-film-grain</code> renders empty (filter ignored); scanlines and the CSS vignette remain active. No JS errors.

The code

HTML structure to paste into your page (CSS + JS available with a premium account):

index.html — structure
<div class="ng-film-scene" role="img" aria-label="Animated film grain texture — grain, scanlines, vignette">
  <!-- SVG filter (once per page) -->
  <svg style="display:none" aria-hidden="true">
    <filter id="ng-film-filter">
      <feTurbulence type="fractalNoise" baseFrequency="0.65" numOctaves="3" stitchTiles="stitch"/>
      <feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0.3 0.3 0.3 0 0"/>
    </filter>
  </svg>

  <div class="ng-film-gradient" aria-hidden="true"></div>
  <div class="ng-film-grain" style="filter: url(#ng-film-filter)" aria-hidden="true"></div>
  <div class="ng-film-lines" aria-hidden="true"></div>
  <div class="ng-film-vignette" aria-hidden="true"></div>

  <!-- Your overlay content (position: relative; z-index: 2) -->
  <div class="ng-film-content">…</div>
</div>
🔒 Unlock the full code — from €2.99 the first month

Full HTML + CSS + JS, copy-paste ready — with hundreds of premium effects.

Customize

Options passed to the API or data-* attributes:

Option / propertyDefaultEffect
.ng-film-grain — opacity: 0.4 0.4 Grain intensity. 0.2 = subtle fresh stock, 0.6 = worn 16 mm film.
.ng-film-grain — animation-duration: 0.1s 0.1s Flicker speed. 0.05 s = very fast (super 8), 0.2 s = slow (dreamlike mood).
.ng-film-grain — steps(3) 3 Grain positions per cycle. 2 = very jerky, 6 = smoother transitions.
.ng-film-vignette — rgba(0,0,0,.6) in radial-gradient 0.6 Vignette intensity. 0.3 = subtle, 0.85 = full-screen cinematic framing.
.ng-film-vignette — transparent 50% 50% Extent of the unshaded central zone. 30% = tight portrait vignette, 70% = edges barely touched.
.ng-film-lines — rgba(0,0,0,.05) in repeating-linear-gradient 0.05 Scanline opacity. 0 = invisible, 0.15 = strong CRT monitor look.
.ng-film-lines — 4px pitch in repeating-linear-gradient 4px Line density. 4 px = standard-definition TV, 2 px = high-density, 6 px = coarse 1970s look.
.ng-film-scene — background: #1a1a2e #1a1a2e Container background colour. Replace with your tint: pure black, sepia, midnight blue, bordeaux.

FAQ

Yes. The SVG definition is not embedded in the provided CSS. Add this once to your page: <svg style="display:none"><filter id="ng-film-filter"><feTurbulence type="fractalNoise" baseFrequency="0.65" numOctaves="3" stitchTiles="stitch"/><feColorMatrix type="matrix" values="0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0.3 0.3 0.3 0 0"/></filter></svg>. On a multi-scene page, a single definition serves all instances.
Yes. The effect is a stack of position: absolute layers. Add your content in a <div> with position: relative; z-index: 2 inside .ng-film-scene. The vignette darkens the edges — place your text at the centre for optimal contrast.
Yes, CSS @keyframes animations do not pause off-screen. To save CPU on mobile, add an IntersectionObserver in JS that toggles animation-play-state: paused on .ng-film-grain when the scene leaves the viewport, and running when it returns.