# Lighting, environments and shadows

> Light a scene with a look preset, add procedural reflections with an environment, place your own lights, run a day–night cycle, and turn on shadows and fog.

Lighting in Apricot has three layers. Pick what the scene needs:

1. **A look** (`<Scene look>`): ready-made lights and a background. Most scenes need nothing else.
2. **An environment** (`<Scene environment>` or `<Environment>`): what metals and glossy materials reflect, plus soft ambient light.
3. **Your own lights**: light components, or `<Daylight>` for a sun that follows the time of day.

## Looks

| `look` | Background | Lights | Good for |
| --- | --- | --- | --- |
| `"soft"` (default) | warm cream `#f6efe6` | warm sky light, warm key from the top right, cool fill | Toys, emoji-style objects, friendly UI |
| `"studio"` | cool grey `#eceff3` | neutral sky light, key and rim light | Product viewers |
| `"flat"` | white `#ffffff` | even ambient light, no shading or tone mapping | Cartoon or UI-like flat colour |
| `"none"` | none | none | Lighting it yourself: an environment, `<Daylight>` or light components |

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

export const Soft = () => (
  <Scene style={{ flex: 1 }}>
    <Box radius={0.15} color="#ff9f59" />
  </Scene>
);

export const Studio = () => (
  <Scene style={{ flex: 1 }} look="studio">
    <Box radius={0.15} material="paint" />
  </Scene>
);
```

`look` is read once, when the scene mounts.

## Environments: reflections

An environment is a procedural photo studio evaluated in the shader: a ceiling-to-floor gradient plus soft boxes of light. Metals and glossy paint reflect it, and it lights everything softly. There is no image to load or generate, so it costs nothing at startup.

| `environment` | Mood |
| --- | --- |
| `"none"` (default) | No reflections |
| `"studio"` | Dark studio with bright soft boxes: metals and cars |
| `"soft"` | Light and even: pale products, white phones, soft paint |
| `"sunset"` | A low warm sun, an orange horizon, a cool sky |
| `"night"` | Dark, with blue and pink neon strips and a thin light above |

For shiny objects, let the environment be the only light:

```tsx title="Chrome.tsx"
import { StudioFloor } from "@apricot/assets";
import { Scene, Sphere } from "@apricot/react";

export function Chrome() {
  return (
    <Scene style={{ flex: 1 }} look="none" environment="studio" background="#0e0f12">
      <StudioFloor radius={6} colors={["#2a2d33", "#0e0f12"]} />
      <Sphere radius={0.8} position={[0, 0.8, 0]} material="chrome" />
    </Scene>
  );
}
```

### Changing the environment at runtime

`<Scene environment>` is fixed at mount. To switch, fade, dim or turn the studio, use `<Environment>` inside the scene. Everything about it is live and free per frame: switching names fades between them, and `rotation` and `intensity` take shared values.

```tsx title="TurnTheLights.tsx"
import { Environment, Scene, Sphere } from "@apricot/react";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import { useSharedValue } from "react-native-reanimated";

export function TurnTheLights({ night }: { night: boolean }) {
  const turn = useSharedValue(0);
  const start = useSharedValue(0);
  const drag = Gesture.Pan()
    .onBegin(() => {
      start.value = turn.value;
    })
    .onUpdate((e) => {
      turn.value = start.value + e.translationX / 120; // radians
    });

  return (
    <GestureDetector gesture={drag}>
      <Scene style={{ flex: 1 }} look="none" background="#0e0f12">
        <Environment name={night ? "night" : "studio"} rotation={turn} transition={0.8} />
        <Sphere radius={0.8} material="car-paint" />
      </Scene>
    </GestureDetector>
  );
}
```

### Your own studio

Describe one with `sky` (ceiling, horizon and floor colours) and `boxes`. Each box is `{ direction, size, intensity, color }`: `direction` points from the object towards the light, `size` is the half width and half height in radians, and `intensity` above 1 makes highlights.

```tsx title="CustomStudio.tsx"
import { Environment, Scene, Sphere } from "@apricot/react";

export function CustomStudio() {
  return (
    <Scene style={{ flex: 1 }} look="none">
      <Environment
        sky={["#3b2f5c", "#1c1630", "#07060b"]}
        boxes={[
          { direction: [0.4, 0.6, 0.7], size: [0.25, 0.12], intensity: 6, color: "#ffd9b0" },
          { direction: [-0.8, 0.1, 0.5], size: [0.06, 0.6], intensity: 4, color: "#9ab8ff" },
        ]}
      />
      <Sphere radius={0.8} material="chrome" />
    </Scene>
  );
}
```

To reuse a studio by name, register it once with `defineEnvironment("brand", { sky, boxes, intensity })`, then use `environment="brand"` or `<Environment name="brand" />`.

## Light components

Add lights on top of a look, or with `look="none"` as the only lights. `intensity`, `position` and `target` accept shared values, so lights can move and dim every frame for free.

| Component | Light | Key props (defaults) |
| --- | --- | --- |
| `<AmbientLight>` | Even light from everywhere | `color` (white), `intensity` (1) |
| `<HemisphereLight>` | Sky colour from above, ground colour from below | `sky` (white), `ground` (warm grey), `intensity` |
| `<DirectionalLight>` | Sunlight: parallel rays | `position` ([3, 5, 4]), `target` ([0, 0, 0]), `intensity`, `color`, `castShadow` |
| `<PointLight>` | A bulb | `position` ([0, 2, 0]), `distance` (0: no limit), `intensity`, `color` |
| `<SpotLight>` | A cone of light | `position`, `target`, `angle` (0.5 rad half-angle), `penumbra` (0.3), `distance` |

```tsx title="Lamp.tsx"
import { DirectionalLight, HemisphereLight, Scene, Sphere } from "@apricot/react";

export function Lamp() {
  return (
    <Scene style={{ flex: 1 }} look="none" background="#ffffff">
      <HemisphereLight sky="#ffe6cc" ground="#c83020" intensity={2} />
      <DirectionalLight position={[-2, 4, 5]} intensity={2.1} color="#ffe8cc" />
      <DirectionalLight position={[4, 1.5, 5]} intensity={0.9} color="#ffdcc0" />
      <Sphere radius={0.8} color="#ff9f59" />
    </Scene>
  );
}
```

## Daylight: a sun that follows the clock

`<Daylight time>` sets the sun, sky light and background for an hour of the day (0–24): night, dawn, noon, dusk. Use it with `look="none"`. Plain values animate there over `transition` (1.5 s); shared values follow exactly, so a slider can scrub the day. With an environment, reflections dim at night too.

```tsx title="DayNight.tsx"
import { Ground, Trees } from "@apricot/assets";
import { Daylight, Scene } from "@apricot/react";

export function DayNight({ hour }: { hour: number }) {
  return (
    <Scene style={{ flex: 1 }} look="none" environment="soft" fog shadows camera={{ position: [0, 12, 30] }}>
      <Daylight time={hour} />
      <Ground />
      <Trees count={80} />
    </Scene>
  );
}
```

Options: `sunrise` (6) and `sunset` (19) hours, `north` (radians; where the sun rises), `background` (true: the sky colour becomes the background and fog colour) and `keys` (replace the day's colours, see `DAY`).

## Shadows

Turn them on with one prop. The main directional light casts them: the look's key light, `<Daylight>`'s sun, or the light marked `<DirectionalLight castShadow>`.

```tsx title="Shadows.tsx"
import { Box, Plane, Scene } from "@apricot/react";

export function Shadows() {
  return (
    <Scene style={{ flex: 1 }} shadows camera={{ position: [3, 3, 5] }}>
      <Plane size={10} rotation={[-Math.PI / 2, 0, 0]} material="matte" color="#e8e1d7" />
      <Box size={1} radius={0.1} position={[0, 0.5, 0]} />
    </Scene>
  );
}
```

- **Who casts and receives:** everything casts and receives by default, except flat things (floors, planes, decals), which only receive, and see-through things, which do neither. Override per object with `castShadow` / `receiveShadow` on shapes, `Mesh`, `Model` and `Instances`.
- **Options:** `shadows={{ size, softness, darkness, radius }}`.
  - `size`: map resolution, 1024 by default. 2048 is sharper and uses four times the memory.
  - `softness`: edge softness in texels, 2 by default.
  - `darkness`: 0 to 1, 0.85 by default.
  - `radius`: for big scenes, the area in metres around what the camera looks at, for example `{ radius: 120 }` for a city.
- **Cost:** the shadow map is redrawn only when something that casts moves. Turning the camera around a still scene costs nothing extra. Each casting mesh is drawn again during a redraw, so instance repeated casters.

## Fog and background

- `fog`: `true` fades objects between 30 and 90 units into the background colour. `{ near, far, color }` sets the range. It hides the edge of a large ground.
- `background`: any colour. Shown exactly as given, so the scene blends into your UI.
- `transparent`: no background at all (overlays).

## Related

- [Lights reference](https://matthiasbrandolin.dev/apricot/reference/lights): every prop, `Daylight`, `Environment`, shadows.
- [Shapes and materials](https://matthiasbrandolin.dev/apricot/guides/shapes-and-materials).
- [Paths and labels](https://matthiasbrandolin.dev/apricot/guides/paths-and-labels): the Metro line scene uses daylight, fog and shadows together.
