Glitch VFX Playground

Neon city scene routed through DigitalGlitch shader with burst, pulse, and wild distortion modes.

What this code does

- DigitalGlitch Shader: post-process pass distorting the rendered frame with RGB split, offsets, and scanline tear.
- Burst Scheduling: sporadic mode triggers short glitch bursts using configurable interval ranges.
- Pulse & Wild Modes: continuous sine-driven pulses or permanent wild glitching demonstrate different looks.
- Neon City Scene: emissive cubes, holographic panels, and moving lights accentuate the distortion artifacts.
- GUI Controls: tune strength, chromatic split, distortion axes, angle randomness, and glitch timing envelopes.

JavaScript (plain)

const scene = new THREE.Scene()
scene.background = new THREE.Color(0x050509)
const camera = new THREE.PerspectiveCamera(70, width / height, 0.1, 80)
camera.position.set(0, 1.8, 7)

const renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true })
renderer.setPixelRatio(Math.min(window.devicePixelRatio || 1, 2))
renderer.setSize(width, height)

document.querySelector('#app').appendChild(renderer.domElement)

const composer = new EffectComposer(renderer)
composer.setSize(width, height)

composer.addPass(new RenderPass(scene, camera))
const glitchPass = new GlitchPass()
glitchPass.goWild = false
composer.addPass(glitchPass)

const glitchSettings = {
  mode: 'sporadic',
  amount: 0.18,
  baseAmount: 0.04,
  distortionX: 1.1,
  distortionY: 0.9,
  chromatic: 0.85,
  angleRange: 0.6,
  seedRange: 0.9,
  pulseSpeed: 1.4,
  intervalMin: 0.55,
  intervalMax: 1.35
}
const glitchUniforms = glitchPass.uniforms
const glitchState = { cooldown: 1.0, burst: 0 }

// Build a neon city scene
const cubes = []
const cubeGeometry = new THREE.BoxGeometry(0.7, 0.7, 0.7)
for (let i = 0; i < 12; i++) {
  const material = new THREE.MeshStandardMaterial({
    color: new THREE.Color().setHSL(0.55 + i / 16, 0.7, 0.55),
    emissive: new THREE.Color().setHSL(0.55 + i / 16, 1.0, 0.35),
    emissiveIntensity: 1.2
  })
  const cube = new THREE.Mesh(cubeGeometry, material)
  cube.position.set((Math.random() - 0.5) * 6, Math.random() * 3, (Math.random() - 0.5) * 6)
  cube.userData = { baseY: cube.position.y, offset: Math.random() * Math.PI * 2 }
  scene.add(cube)

  cubes.push(cube)
}

const ambient = new THREE.AmbientLight(0x223355, 0.6)
scene.add(ambient)

const point = new THREE.PointLight(0x2f89ff, 2.6, 18, 2)
point.position.set(-4, 3.6, -4)
scene.add(point)

const clock = new THREE.Clock()
let elapsed = 0

function applyGlitch(strength) {
  if (!glitchUniforms) return
  if (strength <= 0.0001) {
    glitchUniforms.byp.value = 1
    return
  }
  glitchUniforms.byp.value = 0
  glitchUniforms.amount.value = strength
  glitchUniforms.angle.value = THREE.MathUtils.randFloat(-glitchSettings.angleRange, glitchSettings.angleRange)
  glitchUniforms.seed.value = Math.random()
  glitchUniforms.seed_x.value = THREE.MathUtils.randFloat(-glitchSettings.seedRange, glitchSettings.seedRange)
  glitchUniforms.seed_y.value = THREE.MathUtils.randFloat(-glitchSettings.seedRange, glitchSettings.seedRange)
  glitchUniforms.distortion_x.value = THREE.MathUtils.randFloat(glitchSettings.distortionX * 0.4, glitchSettings.distortionX)
  glitchUniforms.distortion_y.value = THREE.MathUtils.randFloat(glitchSettings.distortionY * 0.4, glitchSettings.distortionY)
  glitchUniforms.col_s.value = glitchSettings.chromatic
}

function updateGlitch(delta) {
  let strength = glitchSettings.amount
  switch (glitchSettings.mode) {
    case 'sporadic':
      glitchState.cooldown -= delta
      if (glitchState.cooldown <= 0) {
        glitchState.cooldown = THREE.MathUtils.randFloat(glitchSettings.intervalMin, glitchSettings.intervalMax)
        glitchState.burst = 0.2 + Math.random() * 0.2
      }
      if (glitchState.burst > 0) {
        glitchState.burst -= delta
      } else {
        strength = 0
      }
      break
    case 'pulse':
      const cycle = (Math.sin(elapsed * glitchSettings.pulseSpeed) + 1) * 0.5
      strength = THREE.MathUtils.lerp(glitchSettings.baseAmount, glitchSettings.amount, cycle)
      break
    case 'wild':
      strength = glitchSettings.amount * 1.3
      break
    case 'off':
    default:
      strength = 0
  }
  applyGlitch(strength)
}

function animate() {
  requestAnimationFrame(animate)
  const delta = clock.getDelta()
  elapsed += delta
  cubes.forEach((cube) => {
    cube.rotation.x += 0.25 * delta
    cube.rotation.y -= 0.35 * delta
    cube.position.y = cube.userData.baseY + Math.sin(elapsed * 1.2 + cube.userData.offset) * 0.35
  })
  updateGlitch(delta)
  composer.render()
}

animate()