# Scene

> Reference for <Scene>: every prop with its type and default, the looks and environments, fog and shadow options, loading and lifecycle callbacks, and the ApricotScene object returned by useScene.

```ts
import { Scene } from "@apricot/react";
```

`<Scene>` is a 3D canvas: a React Native view that hosts a renderer, a camera and lights. Every other Apricot component goes inside one. It also accepts every `ViewProps` prop (`style`, `pointerEvents`, `onLayout`, `testID`…).

```tsx
<Scene style={{ flex: 1 }} look="studio" camera={{ position: [0, 1, 5] }} shadows>
  {/* shapes, models, lights, cameras */}
</Scene>
```

> [!IMPORTANT]
> Every option except `frameloop` is **read once, at mount**. To change one later, remount the scene (change its `key`), or use the component that changes it live: `<Environment>` for reflections, `<Daylight>` for the sun, a camera component for the camera.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `frameloop` | `"demand" \| "always"` | `"demand"` | `"demand"` renders only when something changes; `"always"` renders every frame. Can change at any time |
| `look` | `"soft" \| "studio" \| "flat" \| "none"` | `"soft"` | Lighting preset. `"none"` adds no lights and no background colour |
| `camera` | `CameraOptions` | see below | Camera position, target, field of view and clipping |
| `fog` | `boolean \| { near?, far?, color? }` | none | Fades distant objects into the background. `true` is 30 to 90 units, in the background colour (white if there is none) |
| `environment` | `string \| StudioOptions` | `"none"` | What metals and glossy materials reflect: `"studio"`, `"soft"`, `"sunset"`, `"night"`, a name from `defineEnvironment`, or a studio object |
| `transparent` | `boolean` | `false` | Transparent canvas over the views behind it, for overlays. Combine with `pointerEvents="none"` to let touches through |
| `background` | colour | the look's | Background colour. Shown exactly as given (tone mapping is compensated) |
| `linger` | `number` (seconds) | `0.25` | With `"demand"`, keep rendering this long after the last change, so drags and sensors animate smoothly |
| `shadows` | `boolean \| ShadowOptions` | `false` | Shadows from the main directional light |
| `maxPixelRatio` | `number` | `2` | Caps the device pixel ratio. Lower it to trade sharpness for speed |
| `debug` | `boolean` | `false` | Shows fps, render and present time, and a Share button for the performance report |
| `loading` | `ReactNode` | none | Shown over the scene until it first draws its content (GPU start and shader warm-up) |
| `onCreated` | `(scene: ApricotScene) => void` | | Called once the renderer is ready |
| `onReady` | `(scene: ApricotScene) => void` | | Called once the scene first draws its content |
| `onError` | `(error: Error) => void` | logs | Called if the renderer fails to start |
| `android` | `AndroidCanvasProps` | | Passed to react-native-webgpu's canvas on Android |
| `children` | `ReactNode` | | The scene's content |

### `camera`: `CameraOptions`

| Field | Type | Default |
| --- | --- | --- |
| `position` | `[x, y, z]` | `[0, 0, 6]` |
| `target` | `[x, y, z]`: the point looked at | `[0, 0, 0]` |
| `fov` | degrees, vertical | `50` |
| `near` | units | `0.1` |
| `far` | units. Raise it for big scenes | `200` |

A camera component (`<FitCamera>`, `<CameraRig>`, `<FollowCamera>`) takes over after mount. See [Cameras](https://matthiasbrandolin.dev/apricot/reference/cameras).

### `look`

| Look | Background | Lights | For |
| --- | --- | --- | --- |
| `soft` | `#f6efe6` | Warm hemisphere, warm key, cool fill | Toys, emoji-style objects |
| `studio` | `#eceff3` | Neutral hemisphere, key and rim | Product viewers |
| `flat` | `#ffffff` | Even ambient light, no tone mapping | Cartoon or UI-like objects |
| `none` | black, unless you pass `background` | None | Lighting it yourself: `<Environment>`, `<Daylight>` or light components |

Shiny objects (metal, chrome, glass) look best with `look="none"` and an `environment`.

### `shadows`: `ShadowOptions`

| Field | Default | Description |
| --- | --- | --- |
| `size` | `1024` | Shadow map resolution in texels. 2048 is sharper and costs four times the memory |
| `softness` | `2` | Edge softness, in texels |
| `darkness` | `0.85` | How dark shadows are, 0 to 1 |
| `radius` | fits all casters | Half the width of the shadowed area, centred on what the camera looks at. Set it for big scenes |

Everything casts and receives except floors (which only receive) and see-through objects. `castShadow` / `receiveShadow` on an object override that. The shadow map is redrawn only when something that casts moves. The light used is the look's key light, `<Daylight>`'s sun, or a `<DirectionalLight castShadow>`.

## Lifecycle

1. The view lays out and the GPU starts (the canvas is blank). `onCreated` fires.
2. Shaders for everything compile in the background, including objects out of view. Frames show only the background, for up to 4 s.
3. The scene draws its content. `onReady` fires and `loading` is removed.

Children render once the renderer is ready, so hooks inside them (`useScene`, `useFrame`) always have a scene.

## Taps

A tap is one finger lifted within 10 points and 500 ms of where it went down. The nearest object under the finger with an `onPress` (or `onPartPress`, or `<Instances onPress>`) receives it. Gesture handlers around the scene keep receiving their events.

## `ApricotScene`

`useScene()` (inside the scene), `onCreated` and `onReady` give the `ApricotScene`:

| Member | Description |
| --- | --- |
| `invalidate()` | Requests a frame. Needed in `"demand"` mode after changing three.js objects outside `useFrame` |
| `camera` | The three.js `PerspectiveCamera` |
| `scene` | The three.js `Scene` root |
| `size` | `{ width, height }` in points |
| `onResize(listener)` | Calls `listener(size)` after every resize. Returns the remover |
| `onFrame(callback)` | Like `useFrame`, outside React. Returns the remover |
| `ready` | `true` once the scene draws its content |
| `onReady(listener)` | Calls once when ready (at once if it already is). Returns the remover |
| `press(x, y)` | Simulates a tap at a point in the canvas. Returns the hit |
| `report(extra?)` | A performance report as plain JSON |
| `threeRenderer` | The three.js `WebGPURenderer`. Escape hatch |
| `diagnostics`, `stats` | Performance recorders used by `debug` and `report` |

## Related

- [Scenes and the frame loop](https://matthiasbrandolin.dev/apricot/concepts/scenes).
- [Lighting](https://matthiasbrandolin.dev/apricot/guides/lighting).
- [Hooks reference](https://matthiasbrandolin.dev/apricot/reference/hooks).
