CSS Distortion Effect — Tutorial & Generator

Learn how to create distortion effects with CSS skew transforms, scale warping, and SVG displacement filters, then see it as a fullscreen scroll transition.

Create a Distortion Effect with CSS

Hover over the preview to trigger the distortion. Adjust the controls to tweak the effect, then copy the generated CSS.

DISTORT
-3030
-3030
0.52.0
Image for skew distortion demo
-3030
-3030
0.52.0
Image inside card for skew distortion demo
-3030
-3030
0.52.0
WARP
0.010.10
050
Image for WebGL distortion demo
01.0
130
0.15.0

CSS Skew Text Distortion

Warp text with CSS skew transforms and scale for a dynamic distortion look

Types of CSS Distortion Effects

The term "distortion effect" covers several distinct techniques. Each suits different use cases:

Glitch text distortion

Duplicates text layers using ::before and ::after pseudo-elements, then offsets and clips them with clip-path or clip. Animating the offsets with @keyframes at irregular intervals produces a digital glitch look. Pure CSS, no JavaScript.

Image hover distortion

Warps an image on :hover using CSS skew() and scale(), or more advanced WebGL shaders that displace pixels based on mouse position. The CSS approach is quick to implement; the WebGL approach (using libraries like curtains.js or OGL) gives per-pixel control.

SVG displacement distortion

Uses an inline SVG <filter> with feTurbulence and feDisplacementMap to spatially shift pixels in any HTML element. Produces organic, noise-based warps that CSS transforms cannot achieve on their own.

Liquid / glass distortion

Combines SVG displacement maps or WebGL fragment shaders to simulate refraction through water or glass. Often used as scroll-triggered transitions between sections or as hover effects on hero images.

Scroll-based text distortion

Ties transform values to scroll position using scroll-timeline, IntersectionObserver, or a scroll library. Text skews, stretches, or warps as the user scrolls past it.

See a Distortion Effect on a Scroll Transition

The techniques above work for element-level interactions. For fullscreen distortion transitions between sections, with per-pixel displacement mapping on the GPU at 60 fps, you need WebGL.

Scroll inside the preview below to see the WebGL displacement transition in action:

Loading preview...

fullPage.js adds

  • 80+ WebGL & CSS transitions
  • Touch & swipe support
  • Scroll snapping
  • Keyboard navigation
  • Anchor links & history
  • Lazy loading
  • 60fps GPU performance
Get fullPage.js →

Scroll inside the preview to see the distortion transition between sections

CSS-Only vs SVG Filters vs WebGL

Approach Best for Limitations
CSS-only Lightweight glitch and skew effects, text distortion, hover transitions Limited to geometric transforms — no per-pixel displacement
SVG filters True displacement/warp effects on any HTML element using noise maps Animating filter attributes requires JavaScript; can be expensive on large elements
WebGL Rich liquid/hover transitions, per-pixel displacement at 60 fps, fullscreen scroll effects Requires a JS library or custom shaders; no fallback without <canvas>

Start with CSS transforms for simple warps. Move to SVG filters when you need noise-based displacement. Use WebGL when you need real-time per-pixel control or fullscreen transitions.

How CSS Distortion Effects Work

Transform functions

transform can rotate, resize, distort, or move elements. For distortion specifically, skew(x, y) shears an element along one or both axes, and it's the simplest native distortion. Combining it with scale() using different X and Y values creates convincing warp effects:

.warp:hover {
  transform: skew(-5deg, 2deg) scale(1.05, 0.96);
  transition: transform 0.4s cubic-bezier(.25,.46,.45,.94);
}

CSS filter effects

filter processes an element before display. blur(), contrast(), and url() (pointing to an SVG filter) can all contribute to a distortion look. The url(#filter-id) syntax bridges CSS and SVG, letting you apply complex displacement filters to any HTML element:

/* Apply an SVG displacement filter via CSS */
.distort {
  filter: url(#turbulence-warp);
}

SVG feDisplacementMap

feDisplacementMap spatially displaces pixels using another image or generated noise as a map. Paired with feTurbulence (which generates Perlin noise), it produces organic warps. The scale attribute controls intensity; baseFrequency controls the coarseness of the noise pattern:

<feTurbulence baseFrequency="0.03" numOctaves="3" result="noise"/>
<feDisplacementMap in="SourceGraphic" in2="noise"
  scale="20" xChannelSelector="R" yChannelSelector="G"/>

Pseudo-element layering for glitch effects

Glitch distortion duplicates content into ::before and ::after layers, offsets them with translate or clip-path, and animates each layer independently with @keyframes. The stacked, out-of-sync layers create the characteristic digital glitch look.

CSS Text Distortion Effects

Text distortion is one of the most searched variations of this effect. The main approaches:

Skew-based text warp

Apply transform: skew() on hover or scroll to shear headline text. Combine with transition and cubic-bezier easing for smooth warps:

h1 {
  transition: transform 0.5s cubic-bezier(.25,.46,.45,.94);
}
h1:hover {
  transform: skewX(-8deg);
}

This is what the generator above produces.

Glitch text with pseudo-elements

Create two copies of the text using ::before and ::after with content: attr(data-text). Position them absolutely, offset with translate, and animate with staggered @keyframes. Add clip-path: inset() keyframes to randomly slice sections for a stronger glitch:

.glitch {
  position: relative;
}
.glitch::before,
.glitch::after {
  content: attr(data-text);
  position: absolute;
  top: 0; left: 0;
}
.glitch::before {
  animation: glitch-1 0.3s infinite linear alternate;
  clip-path: inset(20% 0 40% 0);
  color: cyan;
}
.glitch::after {
  animation: glitch-2 0.3s infinite linear alternate;
  clip-path: inset(60% 0 10% 0);
  color: magenta;
}
@keyframes glitch-1 {
  0%   { transform: translate(-2px, -1px); }
  100% { transform: translate(2px, 1px); }
}
@keyframes glitch-2 {
  0%   { transform: translate(2px, 1px); }
  100% { transform: translate(-2px, -1px); }
}

SVG filter on text

Apply an SVG feDisplacementMap filter to a heading element for organic, noise-based text warping:

h1 {
  filter: url(#distort);
  transition: filter 0.3s;
}
h1:hover {
  filter: url(#distort-strong);
}

Use the SVG Filter demo above to dial in the turbulence frequency and displacement scale, then copy the code.

CSS Image Distortion Hover Effects

Image hover distortion is a major use case. Portfolio grids, galleries, and hero sections all benefit from it.

CSS-only image distortion

Apply transform: skew() and scale() to an image on :hover. Wrap in an overflow: hidden container to keep the warped image within bounds:

.img-distort {
  overflow: hidden;
}
.img-distort img {
  transition: transform 0.5s cubic-bezier(.25,.46,.45,.94);
}
.img-distort:hover img {
  transform: skew(-3deg, 1deg) scale(1.08, 0.95);
  filter: blur(0.5px);
}

WebGL image distortion

For per-pixel displacement on hover (the liquid ripple effect common in agency portfolios), you need a WebGL library. The WebGL demo in the generator above uses curtains.js with a custom fragment shader that displaces UV coordinates based on mouse distance. Other options include OGL, Three.js, or PixiJS.

SVG Displacement Map Effects

SVG displacement is the middle ground between CSS transforms and full WebGL. It gives real pixel-level distortion without needing a canvas or shader code.

How feDisplacementMap works

The filter takes two inputs: the source graphic and a displacement map. For each pixel, it reads the R and G channels of the map and uses those values to shift the source pixel's position. feTurbulence generates the map procedurally, so no external image is needed:

<svg style="position:absolute;width:0;height:0">
  <filter id="distort">
    <feTurbulence type="turbulence"
      baseFrequency="0.03"
      numOctaves="3"
      result="noise"/>
    <feDisplacementMap
      in="SourceGraphic"
      in2="noise"
      scale="20"
      xChannelSelector="R"
      yChannelSelector="G"/>
  </filter>
</svg>

<!-- Apply to any element -->
<h1 style="filter: url(#distort)">Warped</h1>

Animating SVG displacement

CSS cannot animate feTurbulence attributes directly. Use JavaScript to update baseFrequency or scale on hover or scroll:

element.addEventListener('mousemove', function(e) {
  var turbulence = document.getElementById('turb');
  var val = 0.01 + (e.clientX / window.innerWidth) * 0.04;
  turbulence.setAttribute('baseFrequency', val);
});

Alternatively, use SMIL <animate> tags inside the filter for a no-JS option (supported in Chrome, Firefox, and Safari).

Browser support

feTurbulence and feDisplacementMap are supported in all modern browsers (97%+ global coverage), including Chrome, Firefox, Safari, and Edge. The filter: url(#id) syntax for applying SVG filters to HTML elements is universally supported.

Liquid / Glass Distortion Effects

Liquid and glass effects simulate refraction, where pixels shift as if viewed through water or frosted glass. These are popular for scroll transitions, hero backgrounds, and image reveal animations.

SVG-based liquid distortion

Use feTurbulence with low baseFrequency (0.01–0.03) and high scale values for broad, fluid warps. Animate the seed attribute to make the noise pattern shift over time:

<feTurbulence baseFrequency="0.015" seed="0" result="noise">
  <animate attributeName="seed" from="0" to="100"
    dur="3s" repeatCount="indefinite"/>
</feTurbulence>
<feDisplacementMap in="SourceGraphic" in2="noise" scale="40"/>

The changing seed shifts the noise pattern each frame, creating a flowing liquid effect.

WebGL liquid transitions

For fullscreen liquid transitions between pages or sections, WebGL shaders use a displacement texture (a grayscale image) to control how pixels transition from one image to another. The displacement map defines the shape of the warp, whether circular, diagonal, noisy, etc. fullPage.js with the Cinematic extension uses this technique for scroll-triggered section transitions.

Glass/frosted effect

Combine backdrop-filter: blur() with an SVG displacement filter on a pseudo-element overlay:

.glass {
  position: relative;
}
.glass::before {
  content: '';
  position: absolute;
  inset: 0;
  backdrop-filter: blur(12px);
  filter: url(#distort);
}

The blur handles the frost; the displacement adds the warped refraction. This is CSS + SVG only, no JavaScript needed.

Accessibility & Reduced Motion

Distortion effects, especially glitch animations with rapid flashing, can be problematic for users with vestibular disorders or photosensitive conditions. Use prefers-reduced-motion to disable or tone down the effect:

@media (prefers-reduced-motion: reduce) {
  .glitch::before,
  .glitch::after {
    animation: none;
  }
  .distort:hover {
    transform: none;
    filter: none;
  }
}

This respects the user's OS-level motion preference. The effect still works for users who haven't opted out, while avoiding triggering symptoms for those who have.

FAQ

Is CSS distortion effect the same as glitch effect?
Not exactly. Glitch is one type of distortion that uses duplicated pseudo-element layers with offset animations. Distortion is a broader category that also includes skew/scale warps, SVG displacement filters, and WebGL-based liquid effects. A glitch effect is always a distortion, but a distortion effect is not always a glitch.
Can I make distortion effects with pure CSS only?
Yes, for geometric distortions. skew(), scale(), rotate3d(), and pseudo-element glitch techniques are pure CSS. For noise-based organic warps you need SVG filters (still no JavaScript, but technically SVG, not CSS). For per-pixel displacement on images, you need WebGL.
When do I need SVG filters?
When you need true pixel displacement, meaning warping that shifts individual pixels rather than transforming the whole element as a box. feDisplacementMap with feTurbulence produces organic, noise-driven distortion. Use it for text or UI elements where WebGL would be overkill.
When do I need JavaScript or WebGL?
For mouse-follow distortion on images, fullscreen scroll transitions, or any effect that needs real-time per-pixel displacement at 60 fps. WebGL shaders running on the GPU handle this. Libraries like curtains.js, OGL, and Three.js simplify the setup. fullPage.js with the Cinematic extension handles scroll-triggered WebGL distortion out of the box.
Are distortion effects bad for accessibility?
They can be if they flash or move rapidly. Glitch effects with fast clip-path animations are the most problematic. Always wrap distortion animations in a @media (prefers-reduced-motion: reduce) query that disables or reduces the motion. Subtle hover warps without flashing are generally fine.
What's the best distortion effect for hero text vs images?
For hero text, CSS pseudo-element glitch or SVG feDisplacementMap give the best results with minimal overhead. For hero images, WebGL provides the smoothest per-pixel displacement on hover or scroll. CSS skew() works for quick, lightweight image warps where you don't need pixel-level control.

Make Your Website Stand Out with Fullscreen Effects

Add scroll transitions, parallax and animations to your site with fullPage.js. One component, 80+ effects.

Brandire
New World of Work
OW Consulting

Powering fullscreen design for Google, Sony, BBC, eBay & more

Get fullPage.js
+ Suggest Effect