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.json

Usage

import { FuzzyText } from "@craft-bits/core";
 
<FuzzyText fontSize="clamp(3rem, 8vw, 6rem)" fontWeight={800}>
  hover me
</FuzzyText>

Understanding the component

  1. 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.
  2. 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.
  3. 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.
  4. Direction modes. horizontal shifts rows left/right (the canonical CRT glitch). vertical shifts columns up/down. both does horizontal then vertical, twice as expensive.
  5. Reduced motion short-circuits. When prefers-reduced-motion: reduce is set, the component paints once with zero fuzz and never starts the RAF loop. No listeners attached.

Props

PropTypeDefaultDescription
childrenReactNoderequiredThe text to render.
fontSizenumber | stringclamp(2rem, 10vw, 10rem)CSS font-size (string) or pixels (number).
fontWeightstring | number900CSS font-weight.
fontFamilystring"inherit""inherit" picks up the canvas's CSS font.
colorstring"currentColor"Any CSS color. Resolves from the canvas's CSS.
enableHoverbooleantrueIntensify the fuzz when the cursor is over the text.
baseIntensitynumber0.18Idle fuzz (0–1).
hoverIntensitynumber0.5Fuzz on hover (0–1).
fuzzRangenumber30Maximum pixel displacement.
direction'horizontal' | 'vertical' | 'both''horizontal'Axis of displacement.
clickEffectbooleanfalseBriefly snap to full intensity on click.
glitchModebooleanfalsePeriodically pulse to full intensity.
gradientstring[] | nullnullHorizontal 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-label on the wrapper) so assistive tech can announce it.
  • Animation is fully disabled when prefers-reduced-motion: reduce is 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).