# Recipe: falling pile

> Copy-paste component that drops 3D objects over any existing screen for a few seconds and piles them up at the bottom, while the screen below stays fully interactive.

Objects drop over an existing screen for a few seconds and pile up at the bottom, without leaving the screen. Use it for a celebration, a reward or an empty-state moment.

**Copy `FallingPileOverlay.tsx` into your app**, then wrap a screen:

```tsx title="InboxScreen.tsx"
import { Apple } from "@apricot/assets";
import { FallingPileOverlay } from "./FallingPileOverlay";
import { YourScreen } from "./YourScreen";

export function InboxScreen() {
  return (
    <FallingPileOverlay count={45} duration={5} items={<Apple />}>
      <YourScreen />
    </FallingPileOverlay>
  );
}
```

## The component

```tsx title="FallingPileOverlay.tsx"
// Apricot recipe: falling-pile
// Copy this file into your app. Wrap any screen's content; 3D objects drop
// over it for `duration` seconds and pile up at the bottom. Touches go
// through to the screen below, and the 3D layer stops rendering once the pile
// has settled.
import { Pile, type PileProps, Scene } from "@apricot/react";
import type { ReactNode } from "react";
import { StyleSheet, View, type ViewStyle } from "react-native";

export interface FallingPileOverlayProps extends Omit<PileProps, "children"> {
  /** The screen the objects fall over. */
  children: ReactNode;
  /** What to drop, such as <Apple />. */
  items: ReactNode;
  style?: ViewStyle;
  /** Shows the fps / render-time HUD. */
  debug?: boolean;
}

export function FallingPileOverlay({ children, items, style, debug, ...pile }: FallingPileOverlayProps) {
  return (
    <View style={[{ flex: 1 }, style]}>
      {children}
      {/* A narrow field of view keeps objects round near the screen edges. */}
      <Scene
        transparent
        pointerEvents="none"
        style={StyleSheet.absoluteFill}
        camera={{ position: [0, 0, 20], fov: 30 }}
        debug={debug}
      >
        <Pile scale={[0.58, 0.68]} {...pile}>
          {items}
        </Pile>
      </Scene>
    </View>
  );
}
```

It needs only `@apricot/react` (and `@apricot/assets` for the ready-made items).

## Props

Every [`<Pile>` prop](https://matthiasbrandolin.dev/apricot/guides/falling-objects#options) except `children`, plus:

| Prop | Meaning |
| --- | --- |
| `children` | The screen the objects fall over |
| `items` | What to drop: one or more meshes, such as `<Apple />` |
| `style` | Style for the wrapping view (it has `flex: 1`) |
| `debug` | Shows the fps and render-time HUD |

## Notes

- **Over the UI:** the scene is transparent and `pointerEvents="none"`, so the screen underneath stays interactive (scrolling, buttons).
- **Physics:** the objects collide with each other and the screen edges. Each kind of object is one draw call, however many there are.
- **Battery:** once the pile has settled, the scene stops rendering (`onSettled` fires). A layout change, such as a rotation, wakes it so the pile adapts to the new screen.
- **Mix objects:** pass several items, such as `items={<><Apple /><Apple color="#7bc043" /></>}`. Any mesh works; its collision radius is `userData.radius`, or else its bounding sphere.
- **Size:** `scale` is relative to each item's own size; in this setup the screen is about 10 units tall.
- **Only when needed:** mount the overlay when the moment happens (a reward was earned) and unmount it afterwards, or change its `key` to replay.

## Variations

Coins for a payment confirmation:

```tsx title="Paid.tsx"
import { Coin } from "@apricot/assets";
import { FallingPileOverlay } from "./FallingPileOverlay";
import { Receipt } from "./Receipt";

export const Paid = () => (
  <FallingPileOverlay count={36} duration={2.5} items={<Coin />} onSettled={() => console.log("settled")}>
    <Receipt />
  </FallingPileOverlay>
);
```

## Related

- [Falling objects guide](https://matthiasbrandolin.dev/apricot/guides/falling-objects).
- [Assets](https://matthiasbrandolin.dev/apricot/reference/assets).
