Shapes#
All shapes are centred on their position and accept material, color, onPress, visible, name, castShadow, receiveShadow, ref and children.
import { Box, Capsule, Cone, Cylinder, Plane, Scene, Sphere, Torus } from "@apricot/react";
export function Shapes() {
return (
<Scene style={{ flex: 1 }} camera={{ position: [0, 2, 9] }}>
<Box size={[1, 1, 1]} radius={0.12} position={[-3, 0, 0]} />
<Sphere radius={0.6} position={[-1.5, 0, 0]} />
<Cylinder radius={0.5} height={1.2} position={[0, 0, 0]} />
<Cone radius={0.6} height={1.2} position={[1.5, 0, 0]} />
<Torus radius={0.5} tube={0.15} position={[3, 0, 0]} />
<Capsule radius={0.3} length={0.6} position={[0, 0, -2]} />
<Plane size={12} rotation={[-Math.PI / 2, 0, 0]} position={[0, -0.8, 0]} material="matte" />
</Scene>
);
}| Shape | Size props (defaults) | Orientation |
|---|---|---|
<Box> |
size (1, or [w, h, d]), radius (0: rounded edges when > 0) |
Centred |
<Sphere> |
radius (0.5), segments (32) |
Centred |
<Cylinder> |
radius (0.5), radiusTop (= radius), height (1), segments (32) |
Along y |
<Cone> |
radius (0.5), height (1), segments (32) |
Points up (+y) |
<Plane> |
size (1, or [w, h]) |
Faces +z. Rotate [-Math.PI / 2, 0, 0] for a floor |
<Torus> |
radius (0.5), tube (0.15), segments (48) |
Ring in the x–y plane |
<Capsule> |
radius (0.3), length (0.6, the straight middle), segments (16) |
Along y |
<Mesh> |
geometry: any three.js BufferGeometry you made |
As built |
Changing a size prop rebuilds the shape's geometry; it is cheap but not free, so do not animate sizes through props every frame. Animate scale instead (it accepts a shared value).
Materials#
material takes one of three forms:
import { Box, Scene, Sphere } from "@apricot/react";
export function MaterialForms() {
return (
<Scene style={{ flex: 1 }} look="none" environment="studio">
{/* 1. A preset name */}
<Sphere material="chrome" position={[-1.2, 0, 0]} />
{/* 2. A preset with options */}
<Sphere material={{ preset: "gold", roughness: 0.35 }} position={[0, 0, 0]} />
{/* 3. The color shortcut, on top of any preset */}
<Box material="car-paint" color="#1f4fd1" position={[1.2, 0, 0]} />
</Scene>
);
}The third form, a three.js Material object you created, is also accepted (and is yours to dispose). Without material, shapes wear "standard", a light, slightly glossy grey.
Presets#
| Preset | Looks like | Colour | Roughness | Metalness | Notes |
|---|---|---|---|---|---|
standard |
Light grey plastic (the default) | #cfd3d8 |
0.5 | 0 | |
matte |
Flat dark paint | #2b2d32 |
0.7 | 0 | |
clay |
Unglazed clay, a sculpting look | #d9d4cc |
0.9 | 0 | |
plastic |
Dark plastic | #2a2c30 |
0.5 | 0 | |
rubber |
Tyres, grips | #1c1d1f |
0.9 | 0 | |
paint |
Glossy lacquered paint | #1d3f8f |
0.45 | 0 | Clear coat |
gloss-black |
Piano black | #0b0c0e |
0.1 | 0 | Clear coat |
car-paint |
Metallic car paint | #a8102a |
0.32 | 0.55 | Clear coat and flakes |
chrome |
Mirror metal | #eceef1 |
0.04 | 1 | |
aluminium |
Bright metal | #d5d8dc |
0.2 | 1 | |
brushed-metal |
Brushed steel | #c9cdd3 |
0.3 | 1 | Streaks along the shape's U direction |
gold |
Gold | #e9bd63 |
0.18 | 1 | |
rose-gold |
Rose gold | #eab09a |
0.2 | 1 | |
glass |
Clear glass | #f4f8fb |
0.04 | 0 | Transmission |
tinted-glass |
Dark car glass | #ffffff |
0.04 | 0 | Transmission with a dark tint |
light |
Glows by itself (lamps, screens, LEDs) | #ffffff |
0.3 | 0 | Emissive in its own colour |
Options#
A preset with options: { preset, color, roughness, metalness, map, uv, engrave, side, vertexColors, physical, transition }.
| Option | Type | What it does |
|---|---|---|
preset |
string | The preset to start from (required). |
color |
colour | Base colour. Changes animate. |
roughness |
0–1 | 0 is a mirror, 1 is fully diffuse. Changes animate. |
metalness |
0–1 | 1 is metal. Changes animate. |
transition |
seconds | How long colour, roughness and metalness changes take (default 0.4). |
map |
texture | A colour image, multiplied by color. Load it with useTexture. |
engrave |
{ mask, color?, roughness?, metalness? } |
Engraving or printing from a mask image (opaque = engraved). |
side |
"front", "back", "double" |
Which faces show; "double" for thin things seen from both sides. |
vertexColors |
boolean | Multiply by the geometry's own per-vertex colours. |
physical |
object | Any other three.js MeshPhysicalNodeMaterial property, for full control. |
Animating a colour#
Changing color, roughness or metalness animates smoothly without rebuilding the shader:
import { Box, Scene } from "@apricot/react";
import { useState } from "react";
import { Button, View } from "react-native";
const COLORS = ["#a8102a", "#1c4f9c", "#e9eaec", "#111214"];
export function Repaint() {
const [index, setIndex] = useState(0);
return (
<View style={{ flex: 1 }}>
<Scene style={{ flex: 1 }} look="none" environment="studio">
<Box size={1.5} radius={0.2} material={{ preset: "car-paint", color: COLORS[index], transition: 0.6 }} />
</Scene>
<Button title="Next colour" onPress={() => setIndex((i) => (i + 1) % COLORS.length)} />
</View>
);
}Changing anything else (the preset, map, engrave, side, physical) builds a new material, which costs a shader compile.
Images on a shape#
Load a PNG or JPEG with useTexture (from @apricot/react/model) and pass it as map. It returns null while loading.
import { Plane, Scene } from "@apricot/react";
import { useTexture } from "@apricot/react/model";
export function Poster() {
const image = useTexture(require("./poster.jpg"));
return (
<Scene style={{ flex: 1 }} look="flat">
<Plane size={[3, 4]} material={{ preset: "standard", color: "#ffffff", map: image }} />
</Scene>
);
}See Textures for options.
Presets of your own#
Register a preset once (at module level), then use its name anywhere a preset is accepted:
import { definePreset } from "@apricot/react";
definePreset("brand-orange", {
color: "#ff9f59",
roughness: 0.35,
metalness: 0,
clearcoat: 1,
clearcoatRoughness: 0.05,
});import { Sphere } from "@apricot/react";
import "./brand";
export const Ball = () => <Sphere material="brand-orange" />;A preset spec has color, roughness, metalness (required), and optionally clearcoat, clearcoatRoughness, brushed, flakes, transmission, ior, thickness, attenuationColor, attenuationDistance, emissive (a colour, or "color" to glow in its own colour) and emissiveIntensity. An unknown preset name throws an error that lists the known ones.
Materials for your own meshes#
useMaterial(preset, options) returns a shared preset material for a <Mesh> or for three.js code. Components asking for the same preset and options share one material.
import { Mesh, useMaterial } from "@apricot/react";
import { useEffect, useMemo } from "react";
import { TorusKnotGeometry } from "three/webgpu";
export function Knot() {
const geometry = useMemo(() => new TorusKnotGeometry(0.6, 0.2, 128, 16), []);
useEffect(() => () => geometry.dispose(), [geometry]);
const material = useMaterial("rose-gold");
return <Mesh geometry={geometry} material={material} />;
}createMaterial(preset, options) makes a standalone material (not shared, not animated) for code outside React.