Fuzzy Text
Canvas-rendered text with a per-row glitch displacement that intensifies on hover. The fuzz never stops — just dials between baseline noise and full glitch.
Customize
Intensity
0.18
0.5
Geometry
30
Mode
Installation
npx shadcn@latest add https://craftbits.dev/r/fuzzy-text.jsonUsage
import { FuzzyText } from "@craft-bits/core";
<FuzzyText fontSize="clamp(3rem, 8vw, 6rem)" fontWeight={800}>
hover me
</FuzzyText>Understanding the component
- Two canvases. An offscreen canvas paints the text once cleanly. The visible canvas re-renders every frame by slicing the offscreen and offsetting each row (or column) by a random amount.
- Intensity drives the fuzz. Each frame multiplies a random value by
currentIntensity * fuzzRange. At intensity 0 the text reads cleanly; at 1 it's barely legible. - Hover region matches text bounds. The component computes the text's exact bounding box so hover detection feels precise — outside the glyphs is outside the interactive zone.
- Direction modes.
horizontalshifts rows left/right (the canonical CRT glitch).verticalshifts columns up/down.bothdoes horizontal then vertical, twice as expensive. - Reduced motion short-circuits. When
prefers-reduced-motion: reduceis set, the component paints once with zero fuzz and never starts the RAF loop. No listeners attached.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | required | The text to render. |
fontSize | number | string | clamp(2rem, 10vw, 10rem) | CSS font-size (string) or pixels (number). |
fontWeight | string | number | 900 | CSS font-weight. |
fontFamily | string | "inherit" | "inherit" picks up the canvas's CSS font. |
color | string | "currentColor" | Any CSS color. Resolves from the canvas's CSS. |
enableHover | boolean | true | Intensify the fuzz when the cursor is over the text. |
baseIntensity | number | 0.18 | Idle fuzz (0–1). |
hoverIntensity | number | 0.5 | Fuzz on hover (0–1). |
fuzzRange | number | 30 | Maximum pixel displacement. |
direction | 'horizontal' | 'vertical' | 'both' | 'horizontal' | Axis of displacement. |
clickEffect | boolean | false | Briefly snap to full intensity on click. |
glitchMode | boolean | false | Periodically pulse to full intensity. |
gradient | string[] | null | null | Horizontal gradient for the fill (≥2 stops). |
Accessibility
- The rendered text lives on a canvas — invisible to screen readers. Wrap the component in a visually-hidden text node (or set
aria-labelon the wrapper) so assistive tech can announce it. - Animation is fully disabled when
prefers-reduced-motion: reduceis set — text paints once cleanly, no RAF loop, no event listeners. - Pauses the animation when the document is hidden (browser tab switch) to save CPU.
Credits
- Extracted from:
terminal-dreams(src/components/interactions/FuzzyText.tsx).