Skip to main content
Lumidot provides fine-grained control over animation timing and flow direction. You can adjust the speed of animations and control how wave effects propagate across the grid.

Duration Prop

The duration prop controls the animation cycle time in seconds.
  • Type: number
  • Default: 0.7
  • Unit: seconds

How it works

The duration affects the animation in two ways:
  1. Wave delay calculation: Determines the delay between dots in wave patterns
  2. CSS animation duration: Sets the --lumidot-duration CSS variable
From index.tsx:327-328:
'--lumidot-delay': `${on ? (isSync ? 0 : waveDelay(i, waveDir, duration, cols, rows)) : 0}s`,
'--lumidot-duration': `${duration}s`,

Wave delay calculation

The waveDelay function calculates staggered delays for each dot based on its position. From index.tsx:191-224:
function waveDelay(
  index: number,
  direction: LumidotDirection | LumidotWaveDirection,
  duration: number,
  cols: number,
  rows: number,
): number {
  const col = index % cols;
  const row = Math.floor(index / cols);
  const step = duration / Math.max(5, cols + rows - 2);
  const maxCol = cols - 1;
  const maxRow = rows - 1;

  switch (direction) {
    case 'ltr':
      return (col + row) * step;
    case 'rtl':
      return (maxCol - col + row) * step;
    case 'ttb':
      return (row + col) * step;
    case 'btt':
      return (maxRow - row + col) * step;
    case 'lr':
      return col * step;
    case 'rl':
      return (maxCol - col) * step;
    case 'tb':
      return row * step;
    case 'bt':
      return (maxRow - row) * step;
    default:
      return (col + row) * step;
  }
}
The step value is calculated as:
const step = duration / Math.max(5, cols + rows - 2);
This ensures the total wave animation completes within the specified duration.

Usage examples

<Lumidot 
  pattern="wave-lr" 
  variant="cyan" 
  duration={0.4} 
/>
For subtle loaders, use longer durations (1.5-3s). For attention-grabbing animations, use shorter durations (0.3-0.5s).

Direction Prop

The direction prop controls the flow direction of wave animations.
  • Type: 'ltr' | 'rtl' | 'ttb' | 'btt'
  • Default: 'ltr'

Direction types

ValueDirectionDescription
ltrLeft to rightDiagonal wave from top-left to bottom-right
rtlRight to leftDiagonal wave from top-right to bottom-left
ttbTop to bottomDiagonal wave from top-left to bottom-right
bttBottom to topDiagonal wave from bottom-left to top-right

Usage examples

<Lumidot 
  pattern="all" 
  variant="blue" 
  direction="ltr" 
/>

Wave Direction Patterns

Some patterns have built-in wave directions that override the direction prop. From index.tsx:44-49 and types.ts:44-49:
export const WAVE_DIRECTIONS = {
  'wave-lr': 'lr',
  'wave-rl': 'rl',
  'wave-tb': 'tb',
  'wave-bt': 'bt',
} as const;
These patterns use specific wave directions:
<Lumidot 
  pattern="wave-lr" 
  variant="cyan" 
  rows={3} 
  cols={7} 
/>
From index.tsx:275:
const waveDir = (WAVE_DIRECTIONS as Partial<Record<LumidotPattern, LumidotWaveDirection>>)[pattern] ?? direction;
For these patterns, the built-in wave direction takes precedence.

Synchronized Patterns

Some patterns animate all dots simultaneously without delays. From types.ts:42:
export const SYNC_PATTERNS = new Set<string>(['corners-sync', 'frame-sync']);
For these patterns, duration still controls the animation speed, but all dots animate together:
<Lumidot 
  pattern="corners-sync" 
  variant="fuchsia" 
  duration={1} 
/>

<Lumidot 
  pattern="frame-sync" 
  variant="violet" 
  rows={5} 
  cols={5} 
  duration={0.5} 
/>

Sequence Patterns

Patterns with multiple frames (like spiral and corners-only) use a different timing mechanism. From index.tsx:281-287:
React.useEffect(() => {
  if (!isSequence) return;
  setFrame(0);
  if (frames.length <= 1 || reduced) return;
  const id = window.setInterval(() => setFrame((prev) => (prev + 1) % frames.length), 1250);
  return () => window.clearInterval(id);
}, [frames, reduced, isSequence]);
These patterns cycle through frames every 1250ms (1.25 seconds), independent of the duration prop. The direction prop affects the order of frames:
// From index.tsx:165-167
case 'spiral': {
  const frames = spiralFrames(rows, cols);
  return rev ? [...frames].reverse() : frames;
}
For sequence patterns like spiral, corners-only, and plus-hollow, the frame transition time is fixed at 1.25 seconds. The duration prop only affects the fade transition between frames (37ms fade-in, 250ms fade-out from index.tsx:276,318).

Combining Duration and Direction

You can create varied effects by combining different durations and directions:
// Fast diagonal wave
<Lumidot 
  pattern="all" 
  variant="cyan" 
  rows={5} 
  cols={5} 
  duration={0.4} 
  direction="ltr" 
/>

// Slow reverse diagonal
<Lumidot 
  pattern="all" 
  variant="purple" 
  rows={5} 
  cols={5} 
  duration={2} 
  direction="btt" 
/>

// Quick horizontal sweep
<Lumidot 
  pattern="wave-lr" 
  variant="emerald" 
  rows={3} 
  cols={9} 
  duration={0.5} 
/>

Accessibility

Lumidot respects the prefers-reduced-motion media query for sequence animations. From index.tsx:226-239:
function useReducedMotion(enabled: boolean): boolean {
  const [reduced, setReduced] = React.useState(false);

  React.useEffect(() => {
    if (!enabled) return;
    const mq = window.matchMedia('(prefers-reduced-motion: reduce);
    setReduced(mq.matches);
    const handler = (e: MediaQueryListEvent) => setReduced(e.matches);
    mq.addEventListener('change', handler);
    return () => mq.removeEventListener('change', handler);
  }, [enabled]);

  return reduced;
}
When users have reduced motion enabled, sequence patterns show all dots simultaneously instead of transitioning between frames.

Next Steps

View the complete API reference

Build docs developers (and LLMs) love