Velocity marquee

A ticker that always drifts, speeds up with your scroll and flips direction when you scroll back.

How it works

Each strip moves by a base speed every frame. Scroll velocity is added on top, and its sign sets the direction, which persists after you stop. The text repeats twice and wraps with a modulo so the loop never shows a seam.

Full file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Velocity marquee</title>
<style>
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; }
body { font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; -webkit-font-smoothing: antialiased; }

body { background: #f4f1ff; color: #2b1b6e; overflow-x: hidden; }
.pad { height: 70vh; display: grid; place-content: center; text-align: center; }
.pad h1 { font-size: 3rem; margin: 0; }
.band { background: #2b1b6e; color: #f4f1ff; padding: 18px 0; overflow: hidden; transform: rotate(-3deg); margin: 4vh -20px; }
.band.alt { background: #c9f31d; color: #2b1b6e; transform: rotate(2deg); }
.strip { white-space: nowrap; font: 900 clamp(2.5rem, 7vw, 5rem)/1 system-ui, sans-serif; text-transform: uppercase; will-change: transform; }
</style>
</head>
<body>
<div class="pad"><h1>Velocity marquee</h1><p>Scroll up and down.</p></div>
<div class="band"><div class="strip"></div></div>
<div class="band alt"><div class="strip"></div></div>
<div class="band"><div class="strip"></div></div>
<div class="pad"></div>
<script>
const phrase = 'Keep scrolling ✦ Faster ✦ Slower ✦ Reverse ✦ ';
const strips = document.querySelectorAll('.strip');
strips.forEach(s => { s.textContent = phrase.repeat(6) + phrase.repeat(6); });
let x = 0, dir = 1, lastY = scrollY;
function loop() {
  const v = scrollY - lastY;
  lastY = scrollY;
  if (v !== 0) dir = Math.sign(v);
  x += dir * (1.2 + Math.abs(v) * 0.6);
  strips.forEach((s, i) => {
    const half = s.scrollWidth / 2;
    const sign = i % 2 ? 1 : -1;
    let pos = ((x * sign) % half + half) % half;
    s.style.transform = 'translate3d(' + (-pos) + 'px,0,0)';
  });
  requestAnimationFrame(loop);
}
loop();
</script>
</body>
</html>

More text effects