<div class="container">
  <section class="section hero"></section>
  <section class="section two"></section>
  <section class="section three"></section>
</div>
body {
  margin: 0;
}

.section {
  min-height: 100vh;
}

.hero {
  position: relative;
  overflow: hidden;
}

.hero > canvas {
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  position: absolute;
}

.two {
  background-color: darkcyan;
}

.three {
  background-color: darkorange;
}
gsap.registerPlugin(ScrollTrigger);

const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
const hero = document.querySelector(".hero");

// PIXI ======================

const glitchFilterHor = new PIXI.filters.GlitchFilter({
  fillMode: PIXI.filters.GlitchFilter.MIRROR,
  slices: 128,
  offset: 0,
  average: true
});

const glitchFilterVert = new PIXI.filters.GlitchFilter({
  fillMode: PIXI.filters.GlitchFilter.MIRROR,
  slices: 64,
  offset: 0,
  direction: 90,
  average: true
});

const RGBSplitFilter = new PIXI.filters.RGBSplitFilter([0, 0], [0, 0], [0, 0]);
RGBSplitFilter.padding = 20;

const blurFilter = new PIXI.filters.BlurFilter(0);
blurFilter.padding = 20;

const app = new PIXI.Application({
  resizeTo: hero,
  transparent: true
});
hero.appendChild(app.view);

const text = new PIXI.Text("This is\na PixiJS text", {
  fontFamily: "Arial",
  fontSize: 48,
  fontWeight: 900,
  fill: 0xffffff,
  align: "center"
});
text.anchor.set(0.5);

text.filters = [RGBSplitFilter, blurFilter];

const background = PIXI.Sprite.from(
  "https://images.unsplash.com/photo-1516125073169-9e3ecdee83e7?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=750&q=80"
);
background.anchor.set(0.5);
background.tint = 0x777777;
background.filters = [glitchFilterVert, glitchFilterHor];

app.stage.addChild(background);
app.stage.addChild(text);

app.ticker.add((delta) => {
  app.resize();
  text.x = app.screen.width / 2;
  text.y = app.screen.height / 2;
  background.x = app.screen.width / 2;
  background.y = app.screen.height / 2;
  const scale = coverImage(
    app.screen.width,
    app.screen.height,
    background.texture.baseTexture.width,
    background.texture.baseTexture.height
  );
  background.scale.set(scale);
});

// GSAP ======================

ScrollTrigger.create({
  trigger: hero,
  start: "top top",
  end: "+=100%",
  pin: true
});

const heroPin = hero.closest(".pin-spacer");

let pSpeed = 0;
let prevProgress = 0;
let currProgress = 0;
gsap.ticker.add(() => {
  pSpeed = clamp(currProgress - prevProgress, -0.01, 0.01) / 0.01;
  prevProgress = currProgress;
});

const simplex = new SimplexNoise();

gsap.to(
  {},
  {
    ease: "none",

    scrollTrigger: {
      trigger: heroPin,
      start: "top top",
      end: "+=200%",
      scrub: 2
      // markers: true
    },

    onUpdate() {
      currProgress = this.progress();
      const distort = simplex.noise2D((pSpeed * 4) % 4, 0);

      RGBSplitFilter.red = [-8 * pSpeed, 0];
      RGBSplitFilter.blue = [6 * pSpeed, 0];

      blurFilter.blur = Math.abs(pSpeed);

      glitchFilterHor.offset = 64 * distort;
      glitchFilterHor.red = [-16 * distort, 0];
      glitchFilterHor.blue = [8 * distort, 0];

      glitchFilterVert.offset = -8 * distort;
    }
  }
);

function coverImage(targetWidth, targetHeight, imageWidth, imageHeight) {
  const rx = targetWidth / imageWidth;
  const ry = targetHeight / imageHeight;
  const scale = Math.max(rx, ry);
  return scale;
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://unpkg.co/gsap@3/dist/gsap.min.js
  2. https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.3.6/pixi.min.js
  4. https://cdn.jsdelivr.net/npm/pixi-filters@latest/dist/pixi-filters.js
  5. https://cdnjs.cloudflare.com/ajax/libs/simplex-noise/2.4.0/simplex-noise.min.js