How are glitch, VHS and distortion effects made on the web?

Distortion effects deliberately corrupt an image or a title to create a retro or digital mood. Glitches use offset copies cut with clip-path and tinted with text-shadow, CRT and VHS looks add scanlines and noise layers, and true warping uses SVG filters such as feTurbulence with feDisplacementMap, or WebGL.

How to choose, performance and accessibility
  • Reserve them for moments that carry the brand, such as a music or gaming landing page, a 404 page or a hero title, with RGB Split, VHS Effect or CRT Screen, and keep body text clean.
  • Fast flicker can cause discomfort: limit intensity, never flash more than three times per second, and turn the animation off for prefers-reduced-motion.
  • RGB Split, Holographic Card, CRT Monitor Effect and Synthwave Scene are free with their code; the other effects come with Premium.
GLITCH

RGB Split

Chromatic separation with a corruption style.

CSS Glitch Text
View source code
HTML
<div class="demo-preview">
  <div class="rgb-split" data-text="GLITCH">GLITCH</div>
</div>
CSS
.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
  box-sizing: border-box
}

.demo-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 280px;
  overflow: hidden;
  position: relative;
  background: rgb(10, 10, 15);
  color: rgb(255, 255, 255);
  font-family: sans-serif;
}

.rgb-split {
  font-size: 3rem;
  font-weight: 900;
  position: relative;
  z-index: 0;
  color: white;
  cursor: pointer;
}

.rgb-split::before,
.rgb-split::after {
  content: attr(data-text);
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

.rgb-split::before {
  color: #ff0000;
  z-index: -1;
  animation: rgbShiftR 0.15s infinite;
}

.rgb-split::after {
  color: #00ffff;
  z-index: -1;
  animation: rgbShiftC 0.15s infinite;
}

@keyframes rgbShiftR {

  0%,
  100% {
    transform: translate(-3px, 0);
  }

  50% {
    transform: translate(-5px, 2px);
  }
}

@keyframes rgbShiftC {

  0%,
  100% {
    transform: translate(3px, 0);
  }

  50% {
    transform: translate(5px, -2px);
  }
}
JavaScript
/* Pure CSS — no JavaScript required */

VHS Effect

Retro VHS cassette effect with scanlines.

CSS Retro Noise

Pixelate Effect

8-bit style pixelation on hover.

CSS JS Retro

Melting Text

Text that melts and deforms.

CSS JS Text

Wave Distortion

Text wave and twist distortion.

CSS JS Wave

Noise Overlay

Animated film-style grain texture.

CSS Film Texture

CRT Screen

Retro cathode ray tube screen effect.

CSS CRT Terminal

Glitch Blocks

Random corruption blocks.

CSS JS Glitch

Broken Glass

Broken glass effect with shards and cracks.

CSS Overlay Texture

Static Noise

Analog-style static TV interference.

CSS JS Canvas
HOLO CARD

Holographic Card

Rainbow reflections on hover, Pokemon card style.

CSS Hover Premium
View source code
HTML
<div class="demo-preview">
  <div class="holo-card">HOLO CARD</div>
</div>
CSS
.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
  box-sizing: border-box
}

.demo-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 280px;
  overflow: hidden;
  position: relative;
  background: rgb(10, 10, 15);
  color: rgb(255, 255, 255);
  font-family: sans-serif;
}

.holo-card {
  width: 280px;
  height: 180px;
  background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
  border-radius: 16px;
  position: relative;
  overflow: hidden;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 1.25rem;
}

.holo-card::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(125deg,
      transparent 0%,
      rgba(255, 0, 128, 0.3) 15%,
      rgba(0, 255, 255, 0.3) 30%,
      rgba(255, 255, 0, 0.3) 45%,
      rgba(0, 255, 128, 0.3) 60%,
      rgba(128, 0, 255, 0.3) 75%,
      transparent 100%);
  background-size: 200% 200%;
  opacity: 0;
  transition: opacity 0.3s;
  mix-blend-mode: color-dodge;
}

.holo-card::after {
  content: '';
  position: absolute;
  inset: 0;
  background: repeating-linear-gradient(0deg,
      transparent,
      transparent 2px,
      rgba(255, 255, 255, 0.03) 2px,
      rgba(255, 255, 255, 0.03) 4px);
}

.holo-card:hover::before {
  opacity: 1;
  animation: holoShift 3s linear infinite;
}

@keyframes holoShift {
  0% {
    background-position: 0% 0%;
  }

  100% {
    background-position: 200% 200%;
  }
}
JavaScript
/* Pure CSS — no JavaScript required */

Iridescent Surface

Shifting surface like mother of pearl or soap bubble.

CSS Animation Organic

Prismatic Text

Text with chromatic aberration and dispersion.

CSS Text Glitch

Chrome Reflection

Chrome metallic surface with reflection.

CSS Metal Shine

Oil Slick

Oil puddle effect with multicolor reflections.

CSS Organic Blur

Hologram Projection

Floating holographic projection with scanlines.

CSS Sci-Fi Float

Rainbow Refraction

Light refracted through a prism.

CSS Rainbow Spin

Crystal Effect

Crystal gem with internal reflections.

CSS 3D Luxury

Holographic Badge

Sticker badge with rotating holographic foil effect.

CSS Badge Foil

Laser Beam

Animated laser beam with rainbow refraction.

CSS Animation Sci-Fi
> SYSTEM BOOT OK
> LOADING DATA...
> READY_

CRT Monitor Effect

CRT monitor effect with scanlines, screen curvature and green phosphor glow.

CSS Pseudo-elements Retro
View source code
HTML
<div class="demo-preview">
  <div class="crt-monitor">
    <div class="crt-content">
      <span>> SYSTEM BOOT OK</span><br>
      <span>> LOADING DATA...</span><br>
      <span>> READY_</span>
    </div>
  </div>
</div>
CSS
.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
  box-sizing: border-box
}

.demo-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 280px;
  overflow: hidden;
  position: relative;
  background: rgb(10, 10, 15);
  color: rgb(255, 255, 255);
  font-family: sans-serif;
}

.crt-monitor {
  background: #0a0a0a;
  border: 8px solid #2a2a2a;
  border-radius: 20px;
  padding: 24px;
  position: relative;
  overflow: hidden;
  width: 280px;
  min-height: 160px;
  box-shadow: inset 0 0 60px rgba(0, 255, 0, 0.1), 0 0 20px rgba(0, 0, 0, 0.5);
}

.crt-monitor::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: repeating-linear-gradient(0deg,
      transparent,
      transparent 2px,
      rgba(0, 0, 0, 0.15) 2px,
      rgba(0, 0, 0, 0.15) 4px);
  pointer-events: none;
  z-index: 2;
}

.crt-monitor::after {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: radial-gradient(ellipse at center, transparent 60%, rgba(0, 0, 0, 0.5) 100%);
  pointer-events: none;
  z-index: 3;
}

.crt-content {
  color: #33ff33;
  font-family: 'Courier New', monospace;
  font-size: 14px;
  line-height: 1.6;
  text-shadow: 0 0 8px rgba(51, 255, 51, 0.6);
  position: relative;
  z-index: 1;
}

.crt-content span {
  animation: crtFlicker 4s infinite;
}

@keyframes crtFlicker {

  0%,
  100% {
    opacity: 1;
  }

  92% {
    opacity: 1;
  }

  93% {
    opacity: 0.7;
  }

  94% {
    opacity: 1;
  }

  96% {
    opacity: 0.8;
  }

  97% {
    opacity: 1;
  }
}
JavaScript
/* Pure CSS — no JavaScript required */

VHS Tracking

Animated VHS tracking distortion with horizontal displacement and color bleeding.

CSS Animation VHS

ASCII Art Renderer

Text displayed in ASCII characters with monospace font and green-on-black terminal look.

CSS Monospace Terminal

Pixel Dissolve

Element that pixelates and dissolves on hover with stepped CSS transitions.

CSS Hover Filter

LED Matrix Display

Text rendered as LED dot-matrix display with red glow and dot grid.

CSS Text-shadow LED

Retro Terminal

Green-on-black terminal with typing animation, blinking cursor and CRT scanlines.

CSS Animation Terminal

Gameboy Palette

Content rendered in the Gameboy's 4-shade green palette with CSS filter.

CSS Filter Gameboy

Dot Matrix Printer

Text appearing line by line as if printed by a dot matrix printer.

CSS Animation Print

Windows 95 UI Kit

Buttons and window with authentic Win95-style 3D beveled borders.

CSS Borders Win95
RETRO

Synthwave Scene

Animated retro scene with perspective grid, neon sun and pink/cyan/purple palette.

CSS Animation Synthwave
View source code
HTML
<div class="demo-preview">
  <div class="synthwave-scene">
    <div class="synthwave-stars">
      <span></span><span></span><span></span><span></span>
      <span></span><span></span><span></span><span></span>
    </div>
    <div class="synthwave-sun"></div>
    <div class="synthwave-text">RETRO</div>
    <div class="synthwave-grid"></div>
  </div>
</div>
CSS
.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
  box-sizing: border-box
}

.demo-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 280px;
  overflow: hidden;
  position: relative;
  background: rgb(10, 10, 15);
  color: rgb(255, 255, 255);
  font-family: sans-serif;
}

.synthwave-scene {
  width: 300px;
  height: 200px;
  background: linear-gradient(180deg, #0a001a 0%, #1a0033 30%, #2d004d 50%, #ff006e 51%, #ff006e 52%, #0a001a 52%);
  border-radius: 12px;
  position: relative;
  overflow: hidden;
}

.synthwave-sun {
  position: absolute;
  width: 80px;
  height: 80px;
  background: linear-gradient(180deg, #ff9900, #ff006e, #cc00ff);
  border-radius: 50%;
  top: 20%;
  left: 50%;
  transform: translateX(-50%);
  box-shadow: 0 0 40px #ff006e, 0 0 80px #ff006e66;
  clip-path: polygon(0 0, 100% 0, 100% 40%, 0 40%, 0 45%, 100% 45%, 100% 55%, 0 55%, 0 60%, 100% 60%, 100% 100%, 0 100%);
}

.synthwave-grid {
  position: absolute;
  bottom: 0;
  left: -50%;
  width: 200%;
  height: 48%;
  background:
    linear-gradient(90deg, transparent 0%, rgba(255, 0, 110, 0.3) 49.5%, #ff006e 50%, rgba(255, 0, 110, 0.3) 50.5%, transparent 100%),
    repeating-linear-gradient(90deg, transparent, transparent calc(10% - 1px), rgba(255, 0, 110, 0.4) calc(10% - 1px), rgba(255, 0, 110, 0.4) 10%),
    repeating-linear-gradient(0deg, transparent, transparent 20px, rgba(255, 0, 110, 0.3) 20px, rgba(255, 0, 110, 0.3) 21px);
  transform: perspective(200px) rotateX(60deg);
  transform-origin: center top;
  animation: synthGrid 2s linear infinite;
}

.synthwave-text {
  position: absolute;
  top: 8%;
  width: 100%;
  text-align: center;
  font-family: 'Inter', sans-serif;
  font-weight: 900;
  font-size: 20px;
  letter-spacing: 4px;
  text-transform: uppercase;
  background: linear-gradient(180deg, #00ffff, #ff00ff);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  filter: drop-shadow(0 0 10px #ff00ff) drop-shadow(0 0 20px #00ffff);
}

.synthwave-stars {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 52%;
  overflow: hidden;
}

.synthwave-stars span {
  position: absolute;
  width: 2px;
  height: 2px;
  background: #fff;
  border-radius: 50%;
  animation: twinkle 3s ease-in-out infinite;
}

.synthwave-stars span:nth-child(1) {
  top: 10%;
  left: 15%;
  animation-delay: 0s;
}

.synthwave-stars span:nth-child(2) {
  top: 20%;
  left: 75%;
  animation-delay: 0.7s;
}

.synthwave-stars span:nth-child(3) {
  top: 5%;
  left: 45%;
  animation-delay: 1.2s;
}

.synthwave-stars span:nth-child(4) {
  top: 30%;
  left: 85%;
  animation-delay: 0.3s;
}

.synthwave-stars span:nth-child(5) {
  top: 8%;
  left: 60%;
  animation-delay: 1.8s;
}

.synthwave-stars span:nth-child(6) {
  top: 25%;
  left: 25%;
  animation-delay: 2.1s;
}

.synthwave-stars span:nth-child(7) {
  top: 15%;
  left: 90%;
  animation-delay: 0.9s;
}

.synthwave-stars span:nth-child(8) {
  top: 35%;
  left: 10%;
  animation-delay: 1.5s;
}

@keyframes synthGrid {
  0% {
    background-position-y: 0px;
  }

  100% {
    background-position-y: 21px;
  }
}

@keyframes twinkle {

  0%,
  100% {
    opacity: 0.3;
  }

  50% {
    opacity: 1;
  }
}
JavaScript
/* Pure CSS — no JavaScript required */

Cursed Blood Goo

Liquid blood rendered as metaballs: it drips, merges, flows and pools at the bottom. The cursor carries a glob that flings droplets and attracts nearby blood. Fully customizable color (slime, ink, lava…).

💡 Perfect for: Halloween gore hero, bleeding 404 page, horror film intro, event header, premium section background, loading screen.

Canvas Metaballs Liquid Free
View source code
HTML
<div class="demo-preview">
  <div class="goo-scene" data-goo data-color="#c81e2d">
    <canvas class="goo-canvas"></canvas>
    <div class="goo-hint" aria-hidden="true">Sweep the blood…</div>
  </div>
</div>
CSS
.demo-preview,
.demo-preview *,
.demo-preview *::before,
.demo-preview *::after {
  box-sizing: border-box
}

.demo-preview {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 200px;
  position: relative;
  background: rgb(10, 10, 15);
  color: rgb(255, 255, 255);
  font-family: sans-serif;
}

.goo-scene {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  border-radius: 14px;
  overflow: hidden;
  isolation: isolate;
  background: #0b0406;
  cursor: crosshair;
}

.goo-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  filter: blur(7px) contrast(16);
}

.goo-hint {
  position: absolute;
  bottom: 12px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  font-size: 12px;
  color: #ff5a5a;
  opacity: .6;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .goo-scene {
    cursor: auto;
  }
}
JavaScript
(function() {
  /* ============================================================
     JS scopé (IIFE) — Cursed Blood Goo (fx-0803)
     Metaballs de sang sur chaque [data-goo]
  ============================================================ */
  (function() {
    var reduceMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    var MAX = 130;

    document.querySelectorAll('[data-goo]').forEach(function(scene) {
      var canvas = scene.querySelector('.goo-canvas');
      var ctx = canvas.getContext('2d');
      var color = scene.dataset.color || '#c81e2d';
      var dpr = Math.min(devicePixelRatio || 1, 2);
      var W, H, drops = [],
        pool = 0,
        frame = 0;
      var mx = -999,
        my = -999,
        on = false,
        bx = -999,
        by = -999,
        pmx = -999,
        pmy = -999;

      function resize() {
        W = canvas.width = scene.clientWidth * dpr;
        H = canvas.height = scene.clientHeight * dpr;
      }
      resize();
      window.addEventListener('resize', resize);

      scene.addEventListener('pointermove', function(e) {
        var r = scene.getBoundingClientRect();
        mx = (e.clientX - r.left) * dpr;
        my = (e.clientY - r.top) * dpr;
        on = true;
      });
      scene.addEventListener('pointerleave', function() {
        on = false;
        mx = my = -999;
      });

      function blob(x, y, r) {
        ctx.beginPath();
        ctx.arc(x, y, r, 0, 6.2832);
        ctx.fill();
      }

      function render() {
        ctx.fillStyle = '#0b0406';
        ctx.fillRect(0, 0, W, H);
        ctx.fillStyle = color;
        // flaque (surface ondulée)
        var surf = H - pool;
        ctx.beginPath();
        ctx.moveTo(0, H + 2);
        ctx.lineTo(0, surf);
        for (var x = 0; x <= W; x += 18 * dpr) ctx.lineTo(x, surf + Math.sin(x * 0.012 + frame * 0.05) * 6 * dpr);
        ctx.lineTo(W, H + 2);
        ctx.closePath();
        ctx.fill();
        // gouttes
        for (var i = 0; i < drops.length; i++) blob(drops[i].x, drops[i].y, drops[i].r);
        // glob du curseur
        if (on && bx > -500) blob(bx, by, 34 * dpr);
      }

      if (reduceMotion) {
        // scène figée : flaque + quelques gouttes au repos
        pool = H * 0.22;
        for (var i = 0; i < 9; i++) drops.push({
          x: (0.12 + Math.random() * 0.76) * W,
          y: (0.2 + Math.random() * 0.45) * H,
          r: (12 + Math.random() * 14) * dpr
        });
        render();
        return;
      }

      (function loop() {
        requestAnimationFrame(loop);
        frame++;
        // dégoulinade depuis le haut
        if (frame % 11 === 0 && drops.length < MAX) drops.push({
          x: Math.random() * W,
          y: -12 * dpr,
          vx: (Math.random() - 0.5) * 0.5,
          vy: 1 + Math.random() * 1.4,
          r: (9 + Math.random() * 9) * dpr
        });
        var floorY = H - pool;
        for (var i = drops.length - 1; i >= 0; i--) {
          var d = drops[i];
          d.vy += 0.28 * dpr;
          d.vx *= 0.99;
          d.x += d.vx;
          d.y += d.vy;
          if (on) {
            var ax = mx - d.x,
              ay = my - d.y,
              dist = Math.hypot(ax, ay);
            if (dist < 150 * dpr && dist > 1) {
              var f = (1 - dist / (150 * dpr)) * 0.7;
              d.vx += ax / dist * f;
              d.vy += ay / dist * f;
            }
          }
          if (d.y + d.r * 0.4 >= floorY) {
            pool = Math.min(H * 0.5, pool + (d.r * d.r) / W * 0.8);
            drops.splice(i, 1);
            continue;
          }
          if (d.x < -40 * dpr || d.x > W + 40 * dpr) drops.splice(i, 1);
        }
        pool = Math.max(0, pool - 0.14 * dpr); // la flaque s'écoule lentement → équilibre
        if (on) {
          bx += (mx - bx) * 0.3;
          by += (my - by) * 0.3;
          var sp = Math.hypot(mx - pmx, my - pmy);
          if (sp > 13 * dpr && drops.length < MAX) drops.push({
            x: bx + (Math.random() - 0.5) * 22 * dpr,
            y: by + (Math.random() - 0.5) * 22 * dpr,
            vx: (mx - pmx) * 0.07,
            vy: (my - pmy) * 0.07 + 1,
            r: (6 + Math.random() * 6) * dpr
          });
        }
        pmx = mx;
        pmy = my;
        render();
      })();
    });
  })();
})();

Frequently Asked Questions

How do you create a glitch effect in CSS? +

The glitch effect uses pseudo-elements (::before, ::after) absolutely positioned with animated clip-paths. Each pseudo-element shows an offset copy of the text with different colors (red, cyan). The animation moves the clip-paths in a jerky manner.

What is displacement mapping in CSS? +

Displacement mapping distorts an image using a displacement map (a grayscale image). In CSS, this effect is simulated with SVG filters (feTurbulence + feDisplacementMap). In JavaScript, WebGL or canvas is used.

Are distortion effects accessible? +

Distortion effects can cause accessibility issues for people with epilepsy or motion sensitivity. Always respect prefers-reduced-motion and avoid rapid flashing (more than 3 flashes per second, WCAG 2.3.1 rule).

Which distortion effect is the most performant? +

Pure CSS effects (glitch with clip-path, noise with background-image) are the lightest. Canvas/WebGL effects (displacement, pixelation) are heavier but more spectacular. Use requestAnimationFrame and pause when off viewport.

How do you apply a distortion effect to an image? +

Use an SVG filter with feTurbulence to generate noise, then feDisplacementMap to distort the image. Apply the filter with CSS filter: url(#myFilter). Animate the baseFrequency attribute of feTurbulence for a dynamic effect.