Recorded on iPhone. The orb pours into the Expo mark, then into a typed "expo". The settings sheet then picks the X logo and tints the particles gold.
Shader
Particle Morph
A glowing orb of 240,000 particles that pours into your logo, a word or an emoji, and back again.
Features
- 240,000 glowing particles that pour into your logo, a word or an emoji
- Tap and the orb becomes your shape. Tap again and it gathers back up
- Drag a finger through it and the particles scatter, then spring back
- Use your brand colour, over black or your own background
- Calm, default or lively, depending on the mood you want
- Works on iPhone and Android from the same code, and looks the same on both
- Runs on the GPU, so the rest of your app stays smooth
Built with
- WebGPU
- WGSL compute shaders
- react-native-webgpu
- Skia
Installation
Then add the plugin to app.json:
Usage
Tap the orb to pour it into your logo, and tap again to bring it back.
Examples
A word instead of a logo
Any line of text or emoji becomes the shape.
Your colour, your background
Tint the grains and let them glow over your own screen.
A splash screen
Pour into the logo as soon as it's ready, then move on.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
svg | DataSourceParam | — | The SVG to pour into: require('./logo.svg') or a URL. Filled shapes read best. |
text | string | "" | A line of text or emoji. While it has any, it is the shape. |
color | string | "#dbe6ff" | Grain colour, as #rrggbb. |
backgroundColor | string | "#000000" | What the grains glow over, or "transparent". Dark reads best. |
preset | "default" | "calm" | "lively" | "default" | The feel: a slow turn and gentle drift, something slower and softer, or fast and restless. |
morphed | boolean | — | true shows the shape, false the orb. Leave it out and taps decide. |
onMorphEnd | (morphed: boolean) => void | — | Called when a pour finishes, with whether it ended on the shape. |
config | Partial<ParticleMorphConfig> | — | Fine tuning. See Advanced. |
style | StyleProp<ViewStyle> | — | Fills its parent by default. |
Advanced
Everything most apps never need to touch.
Every field is optional, overrides the preset, and applies on the next frame.
| Field | Default | Description |
|---|---|---|
brightness | 1.2 | Overall glow, about 0.4–2.5. |
grainSize | 1.5 | Device pixels a grain covers when in focus. |
turbulence | 0.05 | How far the drift carries a grain. 0 is still. |
spinPeriod | 36 | Seconds per full turn of the orb. |
morphDuration | 1800 | Milliseconds the pour takes. |
touchRadius | 0.16 | Reach of a finger through the grains, where the orb's radius is 1. |
touchForce | 4 | How hard a finger shoves the grains. 0 turns the push off but keeps taps. |
shapeSize | 0.85 | The shape's longest side as a share of the orb's width. |
fontFamily | null | A system font for text, such as "Georgia". Fonts bundled with your app aren't visible here. |
fontWeight | 700 | Weight for text, 100–900. |
| Method | Description |
|---|---|
showShape() | Pours into the shape, or into the newest one if it has changed. |
showOrb() | Gathers back into the orb. |
toggle() | Same as a tap. |
| Prop | Default | Description |
|---|---|---|
onMorphedChange | — | Called when the state changes. With morphed set, called with the state a tap asks for. |
onTap | — | Called on every tap. |
interactive | true | Off, taps and touch do nothing; only morphed and the ref move it. |
paused | false | Stops rendering entirely, for example while the screen is hidden. |
count | 240000 | Number of grains. Lower it for older devices; changing it rebuilds the GPU buffers. |
accessibilityLabel | "Particle orb" | Read by screen readers. |
ref | — | A ParticleMorphHandle. See Ref methods. |
- A tap mid-pour turns it around from wherever it is.
- Changing
textorsvgprepares the next shape a little each frame, so nothing stalls. Left to taps, it waits for the next tap; withmorphedon, it pours into the new shape as soon as it's ready. - It sleeps on its own while the app is in the background, and carries on without a jump.
- Works on iOS and Android, not web. Android needs API 26 or later. Without a
fontFamily,textuses each platform's own sans.
- Screen readers announce it as a button when it's interactive, and a double-tap toggles the shape.
- With Reduce Motion on, the drift, spin and swing slow to a gentle breath.
How it works
Every grain has a fixed home in the orb. Each frame a WebGPU compute pass drifts, spins and lights all 240,000 of them and blends each toward its spot on the shape, then a render pass draws them as soft, additive points. Grains are paired with their targets by angle, which is why the orb visibly pours into the shape instead of scattering. Nothing touches React while it animates.