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…).
<Scene style={{ flex: 1 }} look="studio" camera={{ position: [0, 1, 5] }} shadows>
{/* shapes, models, lights, cameras */}
</Scene>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.
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#
- The view lays out and the GPU starts (the canvas is blank).
onCreatedfires. - Shaders for everything compile in the background, including objects out of view. Frames show only the background, for up to 4 s.
- The scene draws its content.
onReadyfires andloadingis 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 |