Skip to content
Native Animation
Floating Hearts Stream animation preview

This preview uses fictional demo content. See the props below for the customization options available in this component.

npx nativeanimation add floating-hearts-stream
SkiaGlass

Floating Hearts Stream

A live reaction stream with press-and-hold input and a rolling total, with configurable count, emission rate, reaction colors, and font.

$9 for this animation

Add to your project

Copy the component and its assets into your app with the Native Animation CLI, or paste the agent prompt into Claude Code, Codex or Cursor once the MCP server is connected.

About this animation

A live reaction stream with press-and-hold input and a rolling total, with configurable count, emission rate, reaction colors, and font.

How it works

  1. A single Reanimated frame clock drives the normalized progress of every reaction.
  2. Forty Skia particle slots stay mounted; launching a reaction only updates the next slot's start time, icon, duration, amplitude, and phase.
  3. Each slot derives a sine-wave flight, a 0.4 → 1.1 → 0.9 scale keyframe, rotation, and final-third fade from that clock.
  4. Tapping launches one heart. Holding for 260 ms starts an interruptible stream at the configured rate and releasing stops it immediately.
  5. Ambient reactions reuse the same pool at 70% scale, while local reactions pop the glass control and roll the total.

Why this one

Most floating-reaction examples mount a new component for every tap and are limited to one emoji. This version keeps a fixed pool, mixes four coherent Lucide reactions, supports ambient audience activity, uses a real glass control, and includes reduced-motion behavior without changing the public API.

Icons: Lucide (ISC).

Install

npx expo install @shopify/react-native-skia expo-blur react-native-reanimated react-native-worklets

Usage

import { FloatingHeartsStream } from './FloatingHeartsStream';

export function Reactions() {
  return <FloatingHeartsStream style={{ height: 420, width: 88 }} />;
}

Use the ref and personality knobs for a launch moment:

import { useEffect, useRef } from 'react';
import {
  FloatingHeartsStream,
  type FloatingHeartsStreamRef,
} from './FloatingHeartsStream';

export function Reactions() {
  const ref = useRef<FloatingHeartsStreamRef>(null);

  useEffect(() => {
    const timer = setTimeout(() => ref.current?.burst(18), 350);
    return () => clearTimeout(timer);
  }, []);

  return (
    <FloatingHeartsStream
      ref={ref}
      ambientRate={1.4}
      holdRate={8}
      minDurationMs={2200}
      maxDurationMs={3200}
      reactionColors={{ heart: '#FB7185', sparkles: '#C084FC' }}
      style={{ height: 440, width: 96 }}
    />
  );
}

For Android blur, wrap the background in BlurTargetView and pass its ref through blurTarget.

Props

NameTypeDefaultDescription
initialCountnumber12800Initial reaction total.
countStepnumber17Amount added by each local reaction.
holdRatenumber8Local reactions emitted per second while held.
ambientRatenumber1.1Smaller audience reactions per second; zero disables them.
minDurationMsnumber2200Shortest particle flight.
maxDurationMsnumber3200Longest particle flight.
reactionColorsPartial<Record<FloatingReactionType, string>>Lucide reaction paletteOverrides reaction colors.
monoFontFamilystringSystemTypeface for the rolling total.
blurTargetRefObject<View | null>undefinedAndroid background target used by BlurView.
onReaction(count, type) => voidundefinedCalled after a local reaction changes the total.
accessibilityLabelstring"Send a reaction"Screen-reader label for the control.
styleStyleProp<ViewStyle>undefinedStyle for the full reaction column.
testIDstringundefinedTest identifier for the root.

The ref exposes sendReaction(type?), burst(count?), and resetCount(count?).

Compatibility

  • Expo SDK 58
  • React Native 0.88.0
  • Reanimated 4.7.1
  • React Native Skia 2.14.0
  • iOS, Android, and web: supported
  • Expo Go compatible because all native dependencies are included in SDK 58

Accessibility

The 56-point hit target exposes a button role, an action hint, and a customizable label. With Reduce Motion enabled, a reaction fades in place instead of traveling, hold no longer creates a rapid stream, the pop uses a short fade timing, and imperative bursts collapse to one icon. A tap always remains available as the alternative to holding.