# Quick start

> Build a first Apricot scene step by step, a product card you can tap and swipe, and learn the four ideas you will use everywhere.

This page builds one screen in six steps. Each step is complete code you can paste into an Expo screen. It assumes [Installation](https://matthiasbrandolin.dev/apricot/installation) is done.

## 1. A scene

`<Scene>` is a React Native view that draws 3D. Give it a size like any view.

```tsx title="app/index.tsx"
import { Scene } from "@apricot/react";

export default function Home() {
  return <Scene style={{ flex: 1 }} />;
}
```

An empty scene shows its background (a soft cream, from the default `look="soft"`). The camera sits at `[0, 0, 6]` and looks at the origin.

## 2. A shape with a material

Shapes are centred on their `position`. Sizes are in scene units: think of one unit as one metre.

```tsx title="app/index.tsx"
import { Box, Scene, Sphere } from "@apricot/react";

export default function Home() {
  return (
    <Scene style={{ flex: 1 }}>
      <Box size={1.4} radius={0.15} position={[-1, 0, 0]} material="car-paint" color="#1f4fd1" />
      <Sphere radius={0.7} position={[1.1, 0, 0]} material="chrome" />
    </Scene>
  );
}
```

- `material` is a preset name. There are 16, such as `"chrome"`, `"gold"`, `"glass"`, `"car-paint"`, `"rubber"`. See [Shapes and materials](https://matthiasbrandolin.dev/apricot/guides/shapes-and-materials).
- `color` overrides the preset's colour.

## 3. Better reflections

Metals reflect their surroundings. `environment="studio"` adds a procedural photo studio (no image to load), and `look="none"` removes the default lights so the studio is the only light, which suits shiny objects.

```tsx title="app/index.tsx"
import { Box, Scene, Sphere } from "@apricot/react";

export default function Home() {
  return (
    <Scene style={{ flex: 1 }} look="none" environment="studio" background="#0e0f12">
      <Box size={1.4} radius={0.15} position={[-1, 0, 0]} material="car-paint" color="#1f4fd1" />
      <Sphere radius={0.7} position={[1.1, 0, 0]} material="chrome" />
    </Scene>
  );
}
```

## 4. Motion

Behaviors are components that move their children. `<Spin>` turns them around an axis, in radians per second.

```tsx title="app/index.tsx"
import { Box, Scene, Spin } from "@apricot/react";

export default function Home() {
  return (
    <Scene style={{ flex: 1 }} look="none" environment="studio" background="#0e0f12">
      <Spin speed={0.6}>
        <Box size={1.4} radius={0.15} material="car-paint" color="#1f4fd1" />
      </Spin>
    </Scene>
  );
}
```

The scene renders only while something moves. When nothing changes, it sleeps and costs nothing.

## 5. A swipe that spins it

Animatable props (`position`, `rotation`, `scale`, `speed`…) take a plain value **or a Reanimated shared value**. Shared values are read by the render loop directly, so a gesture never re-renders React.

```tsx title="app/index.tsx"
import { Box, Group, Scene, Spin } from "@apricot/react";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import { cancelAnimation, useDerivedValue, useSharedValue, withDecay } from "react-native-reanimated";

export default function Home() {
  const angle = useSharedValue(0);
  const rotation = useDerivedValue<readonly [number, number, number]>(() => [0, angle.value, 0]);
  const swipe = Gesture.Pan()
    .onBegin(() => cancelAnimation(angle))
    .onChange((e) => {
      angle.value += e.changeX * 0.012; // radians per point
    })
    .onEnd((e) => {
      angle.value = withDecay({ velocity: e.velocityX * 0.012, deceleration: 0.997 });
    });

  return (
    <GestureDetector gesture={swipe}>
      <Scene style={{ flex: 1 }} look="none" environment="studio" background="#0e0f12">
        <Spin speed={0.6}>
          <Group rotation={rotation}>
            <Box size={1.4} radius={0.15} material="car-paint" color="#1f4fd1" />
          </Group>
        </Spin>
      </Scene>
    </GestureDetector>
  );
}
```

`<Spin>` adds a steady turn and the `<Group>` adds the swipe on top: nesting composes motion. The same pattern is packaged as a hook in the [swipe-to-spin recipe](https://matthiasbrandolin.dev/apricot/recipes/swipe-to-spin).

## 6. Taps, colour changes and a loading state

Every shape takes `onPress`. Changing `color` animates the material (over 0.4 s) without rebuilding its shader. `loading` shows your own view until the scene has drawn its first frame.

```tsx title="app/index.tsx"
import { Box, Group, Scene, Spin } from "@apricot/react";
import { useState } from "react";
import { ActivityIndicator, Text, View } from "react-native";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import { cancelAnimation, useDerivedValue, useSharedValue, withDecay } from "react-native-reanimated";

const COLORS = ["#1f4fd1", "#a8102a", "#e3a21a", "#2f6b3a"];

export default function Home() {
  const [color, setColor] = useState(0);
  const angle = useSharedValue(0);
  const rotation = useDerivedValue<readonly [number, number, number]>(() => [0, angle.value, 0]);
  const swipe = Gesture.Pan()
    .onBegin(() => cancelAnimation(angle))
    .onChange((e) => {
      angle.value += e.changeX * 0.012;
    })
    .onEnd((e) => {
      angle.value = withDecay({ velocity: e.velocityX * 0.012, deceleration: 0.997 });
    });

  return (
    <View style={{ flex: 1, backgroundColor: "#0e0f12" }}>
      <GestureDetector gesture={swipe}>
        <Scene
          style={{ flex: 1 }}
          look="none"
          environment="studio"
          background="#0e0f12"
          loading={<ActivityIndicator color="#ffffff" style={{ flex: 1 }} />}
        >
          <Spin speed={0.6}>
            <Group rotation={rotation}>
              <Box
                size={1.4}
                radius={0.15}
                material="car-paint"
                color={COLORS[color]}
                onPress={() => setColor((c) => (c + 1) % COLORS.length)}
              />
            </Group>
          </Spin>
        </Scene>
      </GestureDetector>
      <Text style={{ color: "#8b9099", textAlign: "center", padding: 24 }}>
        Swipe to spin · tap to repaint
      </Text>
    </View>
  );
}
```

## The four ideas behind it

1. **React for structure, shared values for motion.** Mount, unmount and choose things with React state. Animate with shared values, `useFrame` or behaviors. Never set React state every frame. ([Animation](https://matthiasbrandolin.dev/apricot/concepts/animation))
2. **The scene sleeps when nothing changes.** Props, shared values and taps wake it. Code that changes objects directly calls `useScene().invalidate()` or returns `true` from `useFrame`. ([Scenes](https://matthiasbrandolin.dev/apricot/concepts/scenes))
3. **Units are metres, angles depend on the API.** Object rotations are radians; camera views are degrees. ([Coordinates and units](https://matthiasbrandolin.dev/apricot/concepts/coordinates))
4. **Repeated things are one draw call.** Use `<Instances>` for many copies, and keep materials shared. ([Performance](https://matthiasbrandolin.dev/apricot/concepts/performance))

## Next

- [Core concepts](https://matthiasbrandolin.dev/apricot/concepts/scenes) explain each idea in depth.
- [Guides](https://matthiasbrandolin.dev/apricot/guides/shapes-and-materials) cover one task each: models, cameras, lighting, gestures.
- [Recipes](https://matthiasbrandolin.dev/apricot/recipes/swipe-to-spin) are complete screens to copy.
