Components / Thrown Marquee
Thrown Marquee
A marquee you can grab and throw. The usual one is a CSS animation, which cannot be interrupted and has no opinion about where it is; this one integrates velocity with friction proportional to speed, so a hard throw slows dramatically and then creeps. It decays toward the idle drift rather than toward zero, which is what lets the band return to its resting speed without a separate case. Focus pauses it, arrow keys step it item by item, and a sideways wheel scrubs it.
Usage
import ThrownMarquee from '@/components/thrown-marquee'
<ThrownMarquee speed={40} friction={1.6}>
{items}
</ThrownMarquee>Installation
npx shadcn@latest add https://morphiq.prathampatel.design/r/thrown-marquee.jsonModel
v = d + p, p ← p·e^(−λΔt)p the throw, bled by friction proportional to speed · d the drift, a critically damped drive that re-engages from rest
Props
| Property | Type | Default | Description |
|---|---|---|---|
speed | number | 40 | Idle drift, px/s, toward the start of the line (leftward in left-to-right text). Negative runs the other way. The drift re-engages from rest, critically damped, after the band is let go. |
friction | number | 1.6 | Friction λ, per second. A throw's momentum decays as e^(−λt) on top of the idle drift, so it travels v₀/λ. 0 is frictionless. |
throwScale | number | 1 | How much of a flick's measured velocity (a least-squares fit over the last 80 ms) the band takes. |
maxSpeed | number | 4000 | Fastest a throw can go, px/s. |
gap | number | 48 | Space between items, and between copies, px. |
paused | boolean | false | Hold the drift at rest, braking to it and re-engaging from it without a jump. The hook for your own pause control: a paused band can still be dragged, thrown, scrubbed and stepped. |
pauseOnHover | boolean | false | Brake the drift to rest while a pointer hovers the band. A throw still glides out on its own friction. A finger cannot hover, so a touch that ends on the band holds the drift until a pointer comes down somewhere else. |
wheel | boolean | true | Let a horizontal wheel or trackpad scrub the band 1:1. A gesture keeps the axis of its first 8 px, so vertical scrolling always passes through to the page. |
children | ReactNode | — | The items on the band, repeated end to end to fill it. |