Morphiq

Components / Animations / Smear Cursor

Smear Cursor

A cursor trail that is motion blur rather than a string of dots — wide and round when the pointer is slow, thin and stretched when it is fast. A camera’s blur is an exposure integral: the same ink spread over more distance. Drawn as a disc swept along the path and filled once, so corners keep their thickness instead of pinching; stop, and it drains into the pointer.

Usage

import SmearCursor from '@/components/smear-cursor'

<SmearCursor width={15} stretch={0.035} color="#e8e4dc" />

Installation

npx shadcn@latest add https://morphiq.prathampatel.design/r/smear-cursor.json

Model

half-width ∝ 1 / (1 + speed·k)

an exposure integral: fast is thin and long, slow is round

Props

PropertyTypeDefaultDescription
samplesnumber26The shutter: how long the ribbon remembers, in 60 fps frames at any refresh rate (26 ≈ 0.43 s).
widthnumber15Half-width at rest, in px. Neither end of the smear is wider than its distance to that end, so it closes onto the pointer when you stop.
stretchnumber0.035How hard speed thins the ribbon — the exposure term k in r ∝ 1 / (1 + speed·k).
smoothingnumber0.32Pointer easing per 60 fps frame, 0..1. Lower lags further behind the real cursor.
colorstring'#e8e4dc'Fill colour of the ribbon — any canvas colour string. With hideNative it is the cursor's colour too, so it has to stand out from whatever it passes over.
blend'lighter' | 'source-over''lighter'How separate strokes combine where they cross: additive light, or plain paint.
hideNativebooleanfalseReplace the system cursor with a disc at the rest width. It gives under a press and steps aside wherever the page shows a cursor of its own (a link, a field). Off by default — it is an accessibility cost. The system cursor is hidden only while the disc is being drawn, so it stays until the pointer has been seen, and always under reduced motion, forced colours or increased contrast. A data-global host is never under the pointer and has no cursor to hide: there the disc shows wherever the page itself sets cursor: none.
touchbooleanfalseLet a finger smear too. Off by default, so touch scrolls and zooms as if the component were not there. On, a drag that sets off sideways smears and one that sets off up or down still scrolls the page (touch-action: pan-y pinch-zoom); lifting throws the head on at the speed it was let go with. Set touch-action: none on the host if every drag should smear.