distortion
Skill7 starsChanged 10 months agoMITView on GitHub
Distortion effects including warps, glitches, waves, and digital artifacts. Use when creating glitch art, VHS effects, ripple animations, or visual disruption. Provides intensity levels, wave parameters, and glitch configurations.
In this repository87 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- motion-designSkill
- 3d-transformsSkill
- accessibilitySkill
- android-nativeSkill
- animation-recipesSkill
- anticipationSkill
- arc-motionSkill
- attention-choreographySkill
- attentionSkill
- audio-reactiveSkill
- blurSkill
- brand-animationSkill
- cameraSkill
- characterSkill
- choice-resolutionSkill
- cinematicSkill
- code-generationSkill
- colorSkill
- component-animationsSkill
- compositingSkill
- consequenceSkill
- container-transformSkill
- css-advancedSkill
- data-visualizationSkill
- depthSkill
- dramaturgySkill
- durationSkill
- easingSkill
- emotional-journeySkill
- emotion-mappingSkill
- emotion-selectionSkill
- empty-statesSkill
- entrance-patternsSkill
- exaggerationSkill
- exit-patternsSkill
- exportSkill
- figma-integrationSkill
- follow-throughSkill
- frame-timingSkill
- gesture-motionSkill
- glow-shadowSkill
- intentSkill
- ios-nativeSkill
- loading-statesSkill
- loopingSkill
- lottieSkill
- masksSkill
- microstateSkill
- morphingSkill
- motion-auditSkill
- motion-blurSkill
- motion-principlesSkill
- motion-specsSkill
- narrative-arcSkill
- notificationsSkill
- onboardingSkill
- opacitySkill
- orchestratorSkill
- particlesSkill
- The first 60 shown.
The file
Files
Distortion
Warp, glitch, and deformation effects.
Standards Reference
| Standard | Documentation |
| W3C DTCG | designtokens.org |
| SVG Filters | developer.mozilla.org/SVG/Element/filter |
| CSS Filters | developer.mozilla.org/CSS/filter |
Quick Reference
Load from: tokens/distortion.tokens.json
Intensity Levels
| Token | Value | Use |
subtle | 5% | Barely perceptible |
medium | 25% | Clear effect |
strong | 50% | Pronounced warp |
extreme | 80% | Heavy deformation |
Glitch Parameters
| Token | Value | Use |
rgb-offset-subtle | 2px | Slight color fringing |
rgb-offset-strong | 10px | Obvious separation |
slice-count-few | 3 | Minimal disruption |
slice-count-many | 15 | Heavy fragmentation |
Selection Guide
What effect?
│
├── WAVE/RIPPLE → Fluid distortion
│ ├── Wave → continuous oscillation
│ ├── Ripple → point-based, decaying
│ └── Use SVG displacement filter
│
├── GLITCH → Digital artifacts
│ ├── RGB split → chromatic aberration
│ ├── Slice → horizontal displacement
│ ├── Noise → static overlay
│ └── Combine for full effect
│
├── WARP → Spatial distortion
│ ├── Bulge → expand from point
│ ├── Pinch → contract to point
│ ├── Twirl → spiral distortion
│ └── Use canvas or SVG filters
│
├── VHS/RETRO → Analog artifacts
│ ├── Scanlines → horizontal lines
│ ├── Color bleed → color smearing
│ ├── Tracking → horizontal bands
│ └── Combine with noise
│
└── CHROMATIC ABERRATION → Lens effect
└── RGB channel offsetPatterns
Glitch Effect
Digital glitch with RGB split and slices.
CSS (RGB Split):
.glitch {
position: relative;
}
.glitch::before,
.glitch::after {
content: attr(data-text);
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.glitch::before {
left: 2px; /* {motion.distortion.glitch.rgb-offset-subtle} */
text-shadow: -2px 0 #ff0000;
clip-path: inset(0 0 0 0);
animation: glitch-clip 2s infinite linear alternate-reverse;
}
.glitch::after {
left: -2px;
text-shadow: 2px 0 #00ffff;
animation: glitch-clip 3s infinite linear alternate-reverse;
}
@keyframes glitch-clip {
0% { clip-path: inset(40% 0 61% 0); }
20% { clip-path: inset(92% 0 1% 0); }
40% { clip-path: inset(43% 0 1% 0); }
60% { clip-path: inset(25% 0 58% 0); }
80% { clip-path: inset(54% 0 7% 0); }
100% { clip-path: inset(58% 0 43% 0); }
}JavaScript (Advanced Glitch):
class GlitchEffect {
constructor(element) {
this.element = element;
this.canvas = document.createElement('canvas');
this.ctx = this.canvas.getContext('2d');
this.sliceCount = 8; // {motion.distortion.glitch.slice-count-medium}
this.rgbOffset = 5; // {motion.distortion.glitch.rgb-offset-medium}
}
apply(intensity = 0.5) {
const { width, height } = this.element.getBoundingClientRect();
this.canvas.width = width;
this.canvas.height = height;
// Draw original
// ... capture element to canvas
// Apply glitch slices
const sliceHeight = height / this.sliceCount;
for (let i = 0; i < this.sliceCount; i++) {
if (Math.random() < intensity) {
const offset = (Math.random() - 0.5) * 30 * intensity;
const y = i * sliceHeight;
// Displace slice
this.ctx.drawImage(
this.canvas,
0, y, width, sliceHeight,
offset, y, width, sliceHeight
);
}
}
// RGB channel separation
this.applyRGBSplit(intensity);
}
applyRGBSplit(intensity) {
const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
const data = imageData.data;
const offset = Math.floor(this.rgbOffset * intensity);
// Shift red channel left, blue right
for (let i = 0; i < data.length; i += 4) {
const x = (i / 4) % this.canvas.width;
if (x > offset) {
data[i] = data[i - offset * 4]; // Red
}
if (x < this.canvas.width - offset) {
data[i + 2] = data[i + offset * 4 + 2]; // Blue
}
}
this.ctx.putImageData(imageData, 0, 0);
}
}Wave Distortion
Continuous wave effect using SVG filter.
SVG Filter:
<svg>
<defs>
<filter id="wave-filter">
<feTurbulence
type="fractalNoise"
baseFrequency="0.01 0.05"
numOctaves="2"
result="noise"
>
<animate
attributeName="baseFrequency"
dur="10s"
keyTimes="0;0.5;1"
values="0.01 0.05;0.02 0.04;0.01 0.05"
repeatCount="indefinite"
/>
</feTurbulence>
<feDisplacementMap
in="SourceGraphic"
in2="noise"
scale="20"
xChannelSelector="R"
yChannelSelector="G"
/>
</filter>
</defs>
</svg>
<style>
.wavy {
filter: url(#wave-filter);
}
</style>Ripple Effect
Point-based expanding ripple.
Canvas:
class RippleEffect {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.ripples = [];
}
addRipple(x, y) {
this.ripples.push({
x, y,
radius: 0,
maxRadius: 150, // {motion.distortion.ripple.radius-medium}
wavelength: 40, // {motion.distortion.ripple.wavelength-normal}
amplitude: 20,
decay: 0.98 // {motion.distortion.ripple.decay-medium}
});
}
apply(sourceCanvas) {
const { width, height } = this.canvas;
const sourceCtx = sourceCanvas.getContext('2d');
const sourceData = sourceCtx.getImageData(0, 0, width, height);
const destData = this.ctx.createImageData(width, height);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
let dx = 0, dy = 0;
// Calculate displacement from all active ripples
for (const ripple of this.ripples) {
const dist = Math.hypot(x - ripple.x, y - ripple.y);
if (dist < ripple.radius && dist > ripple.radius - ripple.wavelength * 3) {
const phase = (dist - ripple.radius) / ripple.wavelength;
const wave = Math.sin(phase * Math.PI * 2) * ripple.amplitude;
const factor = wave * (1 - dist / ripple.maxRadius);
dx += (x - ripple.x) / dist * factor;
dy += (y - ripple.y) / dist * factor;
}
}
// Sample displaced position
const sx = Math.round(Math.max(0, Math.min(width - 1, x + dx)));
const sy = Math.round(Math.max(0, Math.min(height - 1, y + dy)));
const si = (sy * width + sx) * 4;
const di = (y * width + x) * 4;
destData.data[di] = sourceData.data[si];
destData.data[di + 1] = sourceData.data[si + 1];
destData.data[di + 2] = sourceData.data[si + 2];
destData.data[di + 3] = sourceData.data[si + 3];
}
}
this.ctx.putImageData(destData, 0, 0);
}
update() {
for (let i = this.ripples.length - 1; i >= 0; i--) {
const r = this.ripples[i];
r.radius += 3;
r.amplitude *= r.decay;
if (r.radius > r.maxRadius || r.amplitude < 0.5) {
this.ripples.splice(i, 1);
}
}
}
}VHS Effect
Retro video distortion.
CSS:
.vhs {
position: relative;
}
/* Scanlines */
.vhs::before {
content: '';
position: absolute;
inset: 0;
background: repeating-linear-gradient(
to bottom,
transparent 0px,
transparent 2px,
rgba(0, 0, 0, 0.3) 2px,
rgba(0, 0, 0, 0.3) 4px
);
pointer-events: none;
}
/* Noise overlay */
.vhs::after {
content: '';
position: absolute;
inset: 0;
background: url('noise.png');
opacity: 0.05;
animation: noise 0.2s steps(4) infinite;
pointer-events: none;
}
@keyframes noise {
0% { transform: translate(0, 0); }
25% { transform: translate(-5%, -5%); }
50% { transform: translate(5%, 5%); }
75% { transform: translate(-5%, 5%); }
100% { transform: translate(5%, -5%); }
}JavaScript (VHS tracking):
function vhsTracking(ctx, width, height) {
// Random horizontal bands
const bandCount = 3 + Math.floor(Math.random() * 3);
for (let i = 0; i < bandCount; i++) {
const y = Math.random() * height;
const bandHeight = 5 + Math.random() * 20;
const offset = (Math.random() - 0.5) * 30;
// Get band data
const bandData = ctx.getImageData(0, y, width, bandHeight);
// Clear and redraw offset
ctx.clearRect(0, y, width, bandHeight);
ctx.putImageData(bandData, offset, y);
}
}Chromatic Aberration
Color fringing lens effect.
CSS:
.chromatic-aberration {
position: relative;
}
.chromatic-aberration::before {
content: '';
position: absolute;
inset: 0;
background: inherit;
mix-blend-mode: screen;
transform: translateX(-3px); /* {motion.distortion.chromatic-aberration.offset-medium} */
filter: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='r'><feColorMatrix values='1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0'/></filter></svg>#r");
}
.chromatic-aberration::after {
content: '';
position: absolute;
inset: 0;
background: inherit;
mix-blend-mode: screen;
transform: translateX(3px);
filter: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg'><filter id='b'><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0'/></filter></svg>#b");
}Bulge/Spherize
Expand or contract from point.
Canvas:
function bulge(ctx, cx, cy, radius, strength) {
const imageData = ctx.getImageData(0, 0, ctx.canvas.width, ctx.canvas.height);
const data = imageData.data;
const destData = ctx.createImageData(imageData);
for (let y = 0; y < ctx.canvas.height; y++) {
for (let x = 0; x < ctx.canvas.width; x++) {
const dx = x - cx;
const dy = y - cy;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < radius) {
const factor = Math.pow(dist / radius, strength);
const sx = cx + dx * factor;
const sy = cy + dy * factor;
const si = (Math.round(sy) * ctx.canvas.width + Math.round(sx)) * 4;
const di = (y * ctx.canvas.width + x) * 4;
destData.data[di] = data[si];
destData.data[di + 1] = data[si + 1];
destData.data[di + 2] = data[si + 2];
destData.data[di + 3] = data[si + 3];
} else {
const i = (y * ctx.canvas.width + x) * 4;
destData.data[i] = data[i];
destData.data[i + 1] = data[i + 1];
destData.data[i + 2] = data[i + 2];
destData.data[i + 3] = data[i + 3];
}
}
}
ctx.putImageData(destData, 0, 0);
}Reduced Motion
For prefers-reduced-motion: reduce:
| Full Motion | Reduced Alternative |
| Animated glitch | Static or disabled |
| Wave/ripple | Static or very subtle |
| VHS effects | Static scanlines only |
| Chromatic aberration | Reduced or disabled |
Implementation:
@media (prefers-reduced-motion: reduce) {
.glitch::before,
.glitch::after {
animation: none;
}
.wavy {
filter: none;
}
.vhs::after {
animation: none;
}
}Cross-References
- See
blur/for combining blur with distortion - See
procedural/for noise-based distortion - See
glow-shadow/for combining glow with glitch - See
opacity/for distortion fade effects
References
references/glitch-effects.md— Advanced glitch implementations
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

