Building a pixel-dissolve marquee
Every logo marquee on the internet fades its edges the same way: a
linear-gradient mask that ramps opacity to zero. It's fine. It's also
everywhere. I wanted the edge to actually feel like something, so I made the
content disintegrate into pixels instead.
One ramp isn't enough
A gradient fade changes exactly one thing across the edge: opacity. The result is soft, but it's flat. The content just gets quieter. Real disintegration changes a few things at once. As a logo travels toward the edge it should get chunkier, blurrier, and fainter, all together.
So instead of a single mask, I overlay a stack of vertical bands on each edge.
Each band is a plain absolutely-positioned div, and the further out it sits,
the harder it dissolves whatever's behind it.
Three ramps, one band
For each band I compute its outwardness t (1 at the outermost slice, trending
to 0 at the solid interior) and drive three properties off it.
const t = 1 - (k + 0.5) / bands; // 1 (outer) → 0 (inner)
const blur = t * maxBlur; // backdrop blur grows
const pixel = base * 2 ** Math.floor(t * 4); // pixel blocks grow
const fadePct = Math.pow(t, 1.35) * 96; // fades into the surfaceThe pow(t, 1.35) curve on the fade keeps the inner bands nearly solid and
makes the falloff bite right at the edge, so content stays legible until it's
genuinely on its way out.
Where the "pixels" come from
The blocky look is cheap. Each band paints a 1px mortar grid over its blurred
backdrop, and the grid cell size is the band's pixel value.
band.style.backdropFilter = `blur(${blur}px)`;
band.style.backgroundColor =
`color-mix(in srgb, ${surface} ${fadePct}%, transparent)`;
band.style.backgroundImage =
`linear-gradient(${line} 1px, transparent 1px),
linear-gradient(90deg, ${line} 1px, transparent 1px)`;
band.style.backgroundSize = `${pixel}px ${pixel}px`;backdrop-filter blurs whatever's scrolling behind the band, and the grid chops
that blur into cells. Since each band has its own cell size, the blocks visibly
grow as you move outward: small near the content, coarse at the edge.
color-mix handles the fade without needing the surface color pre-split into an
rgba.
Keeping the grid seamless
The trap with stacked bands is the lattice jumping phase between zones, which
reads as broken squares or stray lines. Two invariants fix it. Each band's
column width is a whole multiple of the largest cell, and the pixel sizes are a
doubling sequence (base, 2·base, 4·base, and so on). So a coarse band's
grid lines always land exactly on the finer band's lattice, and the squares
stay square all the way out.
The thing I'd carry to other effects: a convincing dissolve ramps several
properties together, not just opacity. The mortar-grid-over-backdrop-filter
trick fakes pixelation with no per-frame work, the bands compute once with
useMemo, and passing the real surface color is what lets the edges melt into
the actual backdrop instead of a guess.
The full, prop-driven component lives on the
Pixelated Marquee page. Drag the sliders to feel
how pixelSize, maxBlur, and edgeWidth change the dissolve.
Ask your agent to implement this
Read the full writeup at https://seangeng.com/writing/building-a-pixel-dissolve-marquee.md and implement it in my project.
It covers: Building a pixel-dissolve marquee. Why I replaced the soft gradient fade on a logo marquee with edges that disintegrate into graduated pixel blocks: bigger, blurrier, and fainter the further out they go.
Requirements:
- Follow the technique/approach exactly as described in the writeup.
- Adapt names, colors, and styling to my project's existing conventions.
- If it's a component, make it reusable with sensible props and TypeScript types.
- Keep it accessible: semantic HTML, keyboard support, and respect prefers-reduced-motion.
- When done, tell me which files you created or changed and how to use it.Paste into Claude Code, Codex, Cursor, or any agent. view raw .md download source .zip