25 CSS Spinners 21 / 25

Retro TV Static Noise Loader

A miniature CRT television screen filled with flickering monochrome pixel tiles simulates analog static noise, complete with scanline overlay and vignette darkening.

Pure CSS MIT licensed
Live Demo Open in tab

This is a full-page demo — interact inside the frame above, or open it in the playground for the full-screen experience.

Open in playground

The code

<div class="sp-21">
  <div class="sp-21__tv">
    <div class="sp-21__static">
      <div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div>
      <div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div>
      <div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div>
      <div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div>
      <div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div>
      <div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div>
      <div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div>
      <div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div>
      <div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div><div class="sp-21__pixel"></div>
    </div>
    <div class="sp-21__scanline"></div>
    <div class="sp-21__glow"></div>
  </div>
</div>
.sp-21,.sp-21 *,.sp-21 *::before,.sp-21 *::after{box-sizing:border-box;margin:0;padding:0}
.sp-21{
  --bg:#0a0a0a;
  display:flex;
  align-items:center;
  justify-content:center;
  min-height:100vh;
  background:var(--bg);
}
.sp-21__tv{
  position:relative;
  width:120px;
  height:90px;
  background:#111;
  border-radius:8px;
  border:3px solid #333;
  overflow:hidden;
  box-shadow:0 0 20px rgba(0,0,0,0.8),inset 0 0 10px rgba(0,0,0,0.5);
}
.sp-21__static{
  position:absolute;
  inset:0;
  display:grid;
  grid-template-columns:repeat(12,1fr);
  grid-template-rows:repeat(9,1fr);
  gap:0;
}
.sp-21__pixel{
  background:#fff;
  animation:sp-21-flicker 0.08s steps(1) infinite;
}
.sp-21__pixel:nth-child(4n+1){animation-delay:0s;opacity:0.9}
.sp-21__pixel:nth-child(4n+2){animation-delay:0.02s;opacity:0.4}
.sp-21__pixel:nth-child(4n+3){animation-delay:0.04s;opacity:0.7}
.sp-21__pixel:nth-child(4n){animation-delay:0.06s;opacity:0.2}
.sp-21__pixel:nth-child(7n){animation-duration:0.12s}
.sp-21__pixel:nth-child(11n){animation-duration:0.06s}
.sp-21__pixel:nth-child(3n){background:#888}
.sp-21__scanline{
  position:absolute;
  inset:0;
  background:repeating-linear-gradient(
    to bottom,
    transparent 0px,
    transparent 3px,
    rgba(0,0,0,0.3) 3px,
    rgba(0,0,0,0.3) 4px
  );
  pointer-events:none;
}
.sp-21__glow{
  position:absolute;
  inset:0;
  background:radial-gradient(ellipse,transparent 40%,rgba(0,0,0,0.5) 100%);
  pointer-events:none;
}
@keyframes sp-21-flicker{
  0%,100%{opacity:var(--o,0.9)}
  50%{opacity:0}
}
@media (prefers-reduced-motion: reduce){
  .sp-21__pixel{animation:none}
}

How this works

A 12×9 grid of small div elements fills the TV screen area. Each cell runs sp-21-flicker — a very fast 0.08s steps(1) animation that snaps opacity between the cell's base value and zero. The steps(1) timing ensures a binary on/off flicker with no interpolation, exactly like analog noise. Four groups of cells (via 4n+1 through 4n) share different base opacities (0.9, 0.4, 0.7, 0.2) and delays, creating varied intensity across the grid.

Two overlay divs add the CRT effect: a repeating linear-gradient scanline pattern at 4px intervals, and a radial vignette gradient darkening the corners. The TV outer frame uses a subtle inset box-shadow for the recessed screen bezel look.

Customize

  • Change the noise colour from white/grey to green by setting --c:#00ff41 and --dim:#003b10 for a classic night-vision monitor look.
  • Increase the grid to 16×12 columns/rows and reduce cell size proportionally for finer, more realistic noise grain.
  • Add a CSS animation on the entire .sp-21__tv that occasionally shifts translateX by 2-3px for a signal distortion effect.
  • Remove the scanline overlay div and replace the background:#fff cells with background:var(--c) to create a coloured noise channel.
  • Add a filter:contrast(1.5) brightness(0.8) on the TV element to punch up the noise contrast.

Watch out for

  • steps(1) at 0.08s creates very frequent repaint events — 108 grid cells each repainting independently can easily overwhelm the paint budget on mobile; reduce cell count or increase animation duration for mobile-first contexts.
  • The animation-duration variation via 7n and 11n selectors only works as intended when those indices exist in the DOM; the grid must have enough cells for the pattern to be visible.
  • opacity animations create stacking context changes at the composited layer level — 108 simultaneous stacking context creations may cause rendering lag; consider Canvas or WebGL for high-quality static noise at scale.

Browser support

ChromeSafariFirefoxEdge
60+ 12+ 60+ 60+

CSS grid and steps() timing widely supported; performance-sensitive on mobile — test cell count.

Search CodeFronts

Loading…