Recorded on iPhone. Seven posters on a prism, spun and tilted by hand, then bent into a cylinder.
Shader
Geometric Carousel
Your images on the walls of a 3D prism you spin and tilt with a finger, standing on a polished floor.
Features
- Your images on the walls of a 3D prism you spin with a finger
- Drag up or down to tilt it and see over the rim
- Switch to a cylinder and the walls bend into one smooth curve
- A polished floor mirrors it, from a faint sheen to glass
- Five images make a pentagon, six a hexagon
- Flick it and it glides, then settles on the next image
Built with
- Skia runtime shader
- Ray tracing
- Reanimated
- Gesture Handler
Installation
Usage
Give it your images. Each one becomes a wall.
Examples
As a cylinder
The same walls, bent into one round wall. Changing shape while it's on screen morphs between the two.
A wider front image
The front wall takes 70% of the width, and the walls beside it narrow as they turn away.
Driven from outside
Your own buttons, with a counter that follows drags too.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
images | GeometricCarouselImage[] | Required | One per wall: a require(), a URL or { uri }. Fewer than 3 repeat; more than 32 are cut. |
width | points or "%" | "100%" | How wide the whole shape is at rest. |
height | points or "%" | from the images | How tall the front image is. Leave it out and the images keep their own shape. |
shape | "prism" | "cylinder" | "prism" | Flat walls or one round wall. Changing it morphs. |
tilt | number | 16 | Degrees the top leans toward you at rest. Negative looks from below. |
reflection | Partial<GeometricCarouselReflection> | false | — | The floor's mirror image. false turns it off. See Advanced. |
index | number | — | The wall in front. Pass it to drive the carousel. |
onIndexChange | (index: number) => void | — | Called when it settles on a new wall, never on every frame. |
Advanced
Everything most apps never need to touch.
Pass only the fields you want to change.
| Field | Default | Description |
|---|---|---|
reflection.opacity | 0.3 | How strongly the floor mirrors the walls, 0–1. 0.7 looks like glass. |
reflection.blur | 0 | Softness at the reflection's far edge, 0–1. 0 is an exact mirror. |
reflection.spread | 0.5 | How far across the floor it reaches, 0–1. |
shading.sides | 0.55 | Brightness of a wall seen edge-on, 0–1. 1 is no shading. |
shading.inside | 0.4 | Brightness of the inside walls seen over the rim, 0–1. |
| Method | Description |
|---|---|
scrollTo(index, animated?) | Turns to a wall the short way round. |
next() | Turns one wall forward. |
previous() | Turns one wall back. |
Quick repeated calls queue up: each one turns on from the wall the last one was heading to.
| Prop | Default | Description |
|---|---|---|
maxTilt | 45 | Degrees a vertical drag can tilt either way. 0 locks the tilt. |
perspective | 4.5 | Camera distance. 2.6 is wide-angle, 8 is nearly flat. |
centerWidth | — | Share of the width the front wall takes, as a percentage like "80%". Leave it out for equal walls. |
morphDuration | 650 | Milliseconds for the prism to cylinder morph. |
momentum | 0.2 | How far a flick carries before it settles. 0 always lands on the nearest wall. |
style | — | Margins and positioning. |
accessibilityLabel | "Image carousel" | Read before "3 of 7". |
- Works on iOS and Android from the same code. It doesn't run on web.
- The open top, the tilt and the reflection draw outside the box you give it, so leave some room above and below.
- Without a
height, the images keep their own shape and nothing is cropped. With one, they're cropped to fit the walls. - Nothing redraws while it's still, so a resting carousel costs nothing.
- Screen readers read it as "3 of 7", and swiping up or down turns it.
- With Reduce Motion on, it slides to each image instead of springing, and the shape changes instantly instead of bending.
How it works
The whole shape is drawn by one Skia shader that ray traces it: every pixel casts rays from a camera and finds the wall they hit. A flat wall and a round one are the same arc with a different radius, so one number morphs the prism into a cylinder. The images are packed into a single texture once, and rays that miss the walls bounce off an invisible floor to make the reflection. Spin and tilt are driven by the drag on the UI thread, so React never re-renders while it moves.