• English
  • ConfettiLayer

    Celebration particles, with no character attached.

    import { useRef } from 'react';
    import { ConfettiLayer, CharacterMark, type ConfettiHandle } from '@aora-bot/react';
    
    export default function Demo() {
      const confetti = useRef<ConfettiHandle>(null);
    
      return (
        <div className="site-demo site-demo--center">
          <div style={{ position: 'relative', width: 140, height: 140 }}>
            <CharacterMark shape="blob" color="green" state="celebrate" sizePx={140} />
            <ConfettiLayer ref={confetti} />
          </div>
          <div className="site-controls">
            <button className="site-btn" onClick={() => confetti.current?.burst(18)}>
              burst(18)
            </button>
            <button className="site-btn" onClick={() => confetti.current?.burst(60)}>
              burst(60)
            </button>
            <button className="site-btn" onClick={() => confetti.current?.clear()}>
              clear()
            </button>
          </div>
        </div>
      );
    }

    Shares the engine's ticker

    The layer uses the same particle system the character does and subscribes to the same shared requestAnimationFrame, so adding celebration to a page costs no additional animation loop.

    The physics

    Pieces launch from a ring at the body radius with an upward bias, then fall under gravity with per-frame damping. Lifetime is between 0.45 and 0.85 seconds. Roughly one in six is a star; the rest are dots or streaks that elongate along their velocity.

    Opacity is not linear — a quick fade in over the first tenth of the lifetime, then a power curve out, which reads as pieces receding rather than dissolving.

    Reduced motion

    burst() is a no-op when the reader prefers reduced motion. Particles radiating outward from a centre point are a vestibular trigger, so this is suppressed rather than merely slowed.

    API

    PropTypeDefaultDescription
    classNamestring——
    radiusnumber114Radius the confetti launches from, in viewBox units rather than pixels — the layer scales with its box, so this is a proportion of the character it sits over and does not change when the box does. Deliberately not `radiusPx`.
    refRef<ConfettiHandle>——
    styleCSSProperties——

    Next