Web InnoventixFreeCode

Rgb Split Effect

Original · free

An RGB channel-split glitch on hover.

byWeb InnoventixReact + Tailwind
rgbsplitimageeffect
Open

Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.

npx shadcn@latest add https://webinnoventix.com/r/imgfx-rgb-split.json
imgfx-rgb-split.tsx
"use client";

export default function ImgfxRgbSplit() {
  const src = "/img/gallery/g07.webp";
  const alt =
    "Featured photograph rendered with a red, green and blue channel-split glitch effect";

  return (
    <section className="relative w-full overflow-hidden bg-slate-50 px-6 py-20 text-slate-900 sm:py-28 dark:bg-slate-950 dark:text-slate-100">
      <style>{`
        .imgfxRgbSplit-r,
        .imgfxRgbSplit-g,
        .imgfxRgbSplit-b {
          position: absolute;
          inset: 0;
          height: 100%;
          width: 100%;
          object-fit: cover;
          mix-blend-mode: screen;
          transform: translate3d(0, 0, 0);
          transition: transform 0.45s cubic-bezier(0.22, 0.68, 0.2, 1);
          will-change: transform;
        }
        .imgfxRgbSplit-r { filter: url(#imgfxRgbSplit-fr); }
        .imgfxRgbSplit-g { filter: url(#imgfxRgbSplit-fg); }
        .imgfxRgbSplit-b { filter: url(#imgfxRgbSplit-fb); }

        .imgfxRgbSplit-stage:hover .imgfxRgbSplit-r,
        .imgfxRgbSplit-stage:focus-visible .imgfxRgbSplit-r {
          animation: imgfxRgbSplit-driftR 2s ease-in-out infinite;
        }
        .imgfxRgbSplit-stage:hover .imgfxRgbSplit-g,
        .imgfxRgbSplit-stage:focus-visible .imgfxRgbSplit-g {
          animation: imgfxRgbSplit-driftG 2s ease-in-out infinite;
        }
        .imgfxRgbSplit-stage:hover .imgfxRgbSplit-b,
        .imgfxRgbSplit-stage:focus-visible .imgfxRgbSplit-b {
          animation: imgfxRgbSplit-driftB 2s ease-in-out infinite;
        }

        @keyframes imgfxRgbSplit-driftR {
          0%   { transform: translate3d(-4.5%, -2%, 0); }
          25%  { transform: translate3d(-6.5%, -1%, 0); }
          50%  { transform: translate3d(-3.5%, -3%, 0); }
          75%  { transform: translate3d(-6.8%, -2.4%, 0); }
          100% { transform: translate3d(-4.5%, -2%, 0); }
        }
        @keyframes imgfxRgbSplit-driftG {
          0%   { transform: translate3d(0.6%, 0.8%, 0); }
          50%  { transform: translate3d(-0.6%, -0.8%, 0); }
          100% { transform: translate3d(0.6%, 0.8%, 0); }
        }
        @keyframes imgfxRgbSplit-driftB {
          0%   { transform: translate3d(4.5%, 2%, 0); }
          25%  { transform: translate3d(6.5%, 1%, 0); }
          50%  { transform: translate3d(3.5%, 3%, 0); }
          75%  { transform: translate3d(6.8%, 2.4%, 0); }
          100% { transform: translate3d(4.5%, 2%, 0); }
        }

        @media (prefers-reduced-motion: reduce) {
          .imgfxRgbSplit-r,
          .imgfxRgbSplit-g,
          .imgfxRgbSplit-b {
            transition: transform 0.2s ease;
          }
          .imgfxRgbSplit-stage:hover .imgfxRgbSplit-r,
          .imgfxRgbSplit-stage:focus-visible .imgfxRgbSplit-r {
            animation: none;
            transform: translate3d(-4%, -1.6%, 0);
          }
          .imgfxRgbSplit-stage:hover .imgfxRgbSplit-g,
          .imgfxRgbSplit-stage:focus-visible .imgfxRgbSplit-g {
            animation: none;
            transform: translate3d(0, 0, 0);
          }
          .imgfxRgbSplit-stage:hover .imgfxRgbSplit-b,
          .imgfxRgbSplit-stage:focus-visible .imgfxRgbSplit-b {
            animation: none;
            transform: translate3d(4%, 1.6%, 0);
          }
        }
      `}</style>

      {/* Channel-isolation filters — reconstruct the full image when the layers align */}
      <svg
        aria-hidden="true"
        focusable="false"
        className="pointer-events-none absolute h-0 w-0"
      >
        <filter id="imgfxRgbSplit-fr" colorInterpolationFilters="sRGB">
          <feColorMatrix
            type="matrix"
            values="1 0 0 0 0  0 0 0 0 0  0 0 0 0 0  0 0 0 1 0"
          />
        </filter>
        <filter id="imgfxRgbSplit-fg" colorInterpolationFilters="sRGB">
          <feColorMatrix
            type="matrix"
            values="0 0 0 0 0  0 1 0 0 0  0 0 0 0 0  0 0 0 1 0"
          />
        </filter>
        <filter id="imgfxRgbSplit-fb" colorInterpolationFilters="sRGB">
          <feColorMatrix
            type="matrix"
            values="0 0 0 0 0  0 0 0 0 0  0 0 1 0 0  0 0 0 1 0"
          />
        </filter>
      </svg>

      <div className="mx-auto flex max-w-md flex-col items-center text-center">
        <p className="text-xs font-semibold uppercase tracking-[0.25em] text-indigo-600 dark:text-indigo-400">
          Image Effect
        </p>
        <h2 className="mt-3 text-2xl font-semibold tracking-tight sm:text-3xl">
          Chromatic channel split
        </h2>
        <p className="mt-3 text-sm leading-relaxed text-slate-500 dark:text-slate-400">
          Red, green and blue are pulled apart into separate light channels,
          then screened back together into one crisp frame.
        </p>

        <figure className="mt-10 w-full">
          <div
            tabIndex={0}
            role="img"
            aria-label={alt}
            className="imgfxRgbSplit-stage group relative isolate aspect-[4/5] w-full overflow-hidden rounded-2xl bg-black shadow-xl shadow-slate-900/10 outline-none ring-1 ring-slate-900/10 transition-shadow duration-300 focus-visible:ring-2 focus-visible:ring-indigo-500 dark:shadow-black/40 dark:ring-white/10"
          >
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img
              src={src}
              alt={alt}
              loading="lazy"
              draggable={false}
              className="imgfxRgbSplit-r"
            />
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img
              src={src}
              alt=""
              aria-hidden="true"
              loading="lazy"
              draggable={false}
              className="imgfxRgbSplit-g"
            />
            {/* eslint-disable-next-line @next/next/no-img-element */}
            <img
              src={src}
              alt=""
              aria-hidden="true"
              loading="lazy"
              draggable={false}
              className="imgfxRgbSplit-b"
            />

            <span className="pointer-events-none absolute left-3 top-3 z-10 rounded-md bg-black/50 px-2 py-1 font-mono text-[10px] font-semibold uppercase tracking-widest text-white/90 backdrop-blur-sm">
              RGB
            </span>
          </div>
          <figcaption className="mt-4 text-xs font-medium uppercase tracking-widest text-slate-400 dark:text-slate-500">
            Hover or focus to fracture the colour channels
          </figcaption>
        </figure>
      </div>
    </section>
  );
}

Dependencies

None (React + Tailwind only).

Licence

Built by Web Innoventix. Free for personal and commercial use, no attribution required.

Built by Web Innoventix

Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.

Get a free quote

Similar components

Browse all →