Skip to content
Native Animation
Cauldron Reward Burst animation preview

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

npx nativeanimation add cauldron-reward-burst
HalloweenSkiaGlass

Cauldron Reward Burst

A hold-to-charge cauldron reward reveal with configurable reward content, colors, labels, and fonts.

$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 hold-to-charge cauldron reward reveal with configurable reward content, colors, labels, and fonts.

Why this one

The user earns the reveal by controlling its anticipation. Fire, potion, bubbles, tremor, and a continuous progress ring respond to one charge value. The final burst releases four candy shapes and four scalloped bats before the reward rises from the pot.

How it works

  1. A zero-distance Gesture Handler pan charges linearly over 1,800 ms. Releasing early returns charge with an interruptible spring.
  2. Charge controls flame growth, potion height, bubble density, progress, and a deterministic stepped tremor above 60%.
  3. Completion triggers the squash spring and a 500 ms green flash. Seeded initial velocities and angular velocities drive ballistic candies with gravity for 1.6 seconds.
  4. At 380 ms, a glass reward card starts rising. Digit reels count to the reward, with higher digits moving only at their carry threshold.
  5. Collection calls onCollect once and sends the card toward the upper-left corner. reset() fades out, cancels pending transitions, and returns to idle.

Install

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

Use a GestureHandlerRootView at the app root. Expo SDK 58 includes these native modules in Expo Go. For web, configure CanvasKit following Skia's web setup and load this module after CanvasKit is ready. No image assets or external application files are required. Fonts are optional and must be loaded by the host.

Usage

import { useRef } from 'react';
import { View } from 'react-native';
import { CauldronRewardBurst, type CauldronRewardBurstRef } from './cauldron-reward-burst';

function DailyReward() {
  const rewardRef = useRef<CauldronRewardBurstRef>(null);
  return (
    <View style={{ width: 300, height: 400 }}>
      <CauldronRewardBurst
        ref={rewardRef}
        reward={{ amount: 250, label: 'Candy points', prefix: '+' }}
        onCollect={(reward) => saveReward(reward.amount)}
      />
    </View>
  );
}

saveReward is your application's persistence function. The component handles presentation only; the host decides daily eligibility and applies the reward.

<CauldronRewardBurst
  reward={{ amount: 100, label: 'Bonus points', prefix: '+' }}
  holdDuration={1400}
  confettiCount={18}
  cauldronRatio={0.5}
  fontSize={16}
  amountFontSize={38}
  colors={{
    potion: '#73D8E8', potionShadow: '#268DAD',
    fire: '#B89BFF', fireHighlight: '#E9DEFF',
    background: '#101827', meshTop: '#20334D', meshBottom: '#162D36',
    cauldron: '#162131', lip: '#293D52',
    text: '#F8FCFF', muted: '#B6C9D4', highlight: '#DAEFFA',
    candies: ['#73D8E8', '#B89BFF'], bats: '#BED2E5',
    surface: 'rgba(25,42,60,0.82)', border: '#577181', buttonText: '#101827',
  }}
  labels={{ hold: 'Hold for your bonus', almost: 'Almost ready…', collect: 'Add points' }}
/>

Props

PropTypeDefaultPurpose
reward{ amount, label, prefix? }250 / Candy points / +Whole-number reward and copy. Negative/nonfinite amounts become zero.
holdDurationnumber1800Charge time in ms, minimum 200.
onCharged() => void—Once per completed charge.
onCollect(reward) => void—Once per collection; use to update balance.
onReveal() => void—When the card becomes visible.
onInteraction() => void—Real touch/accessibility interaction, for stopping autoplay.
colorsPartial<CauldronColors>Collection night paletteComplete surface, ink, mesh, potion, fire, candy, bat, border, and button palette.
labels{ hold?, almost?, collect? }Hold to brew / Almost… / CollectAll interaction text.
confettiCountnumber26Seeded candies; clamped to 0–80.
renderReward({ reward, collect }) => ReactNodeBuilt-in cardReplace card contents while retaining its material and reveal/exit.
cauldronRationumber0.46Pot width / shorter container dimension, clamped to 0.2–0.7.
fontSizenumber15UI copy size.
amountFontSizenumber42Reward digit size; compact layout caps it at 32.
cardMaxWidthnumber280Card width limit in points.
fontFamily, monoFontFamilystringSystemHost-loaded UI and digit fonts.
accessibilityLabelstringHold to brew a rewardAccessible brew action.
formatAnnouncement(reward) => stringPrefix, amount, labelScreen-reader reveal announcement.
styleStyleProp<ViewStyle>—Root style; defaults to flex 1.
testIDstringcauldron-reward-burstRoot ID; controls append -brew / -collect.

The ref exposes brew(), collect(), and reset(). Calls during an inapplicable phase do nothing. Reset is available during every phase.

Layout and demo

The host owns safe areas. The component measures its own box; the pot is 46% of the shorter side. Reward contents switch to a compact layout below 300 pt of available component height. The standard target embed is 300×400. Large custom copy or content may require a larger host box.

The separate Ember & Oat demo uses Geist, Instrument Serif, and Geist Mono from the host theme. Autoplay brews, leaves the reward visible for two seconds, collects, pauses, fades back, and repeats. Any touch stops the loop. The balance begins at 1,240 and increases on collection. A landscape composition keeps campaign copy beside the scene.

Accessibility

The cauldron is a button with an activate accessibility action that runs brew() without a sustained hold. The collect control has a 48 pt minimum height and its own activate action. The reveal is announced via AccessibilityInfo. Hidden card controls are excluded from the accessibility tree. Reduced motion removes particles, flights, tremor, flame motion, and blur; the card and numeric changes fade over 200 ms.

Compatibility

Targets Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, Worklets 0.13.0, Skia 2.14.0, and Gesture Handler 3.3.0. Android glass uses a local BlurTargetView and dimezisBlurViewSdk31Plus; older Android versions retain the tinted surface fallback. Supports iOS, Android, and web. The entry remains unregistered for the catalog reviewer.

The candy and flame interface glyphs use Lucide paths (ISC license, copyright Lucide Contributors). All cauldron, candy-particle, and bat illustrations are original geometry.