About this animation
A premium multi-layer sound mixer whose slider thumbs shed and reclaim liquid droplets at speed.
Why this one
Most liquid sliders stop at a soft blob or a decorative wave. This one connects the interaction to the material: velocity pulls a daughter droplet from the thumb, a real blurred alpha threshold fuses the forms, values roll in sync, and release energy returns as a restrained ripple. The included Tidal demo places the control in a credible soundscape product with responsive atmosphere instead of presenting it as an isolated experiment.
How it works
- Each row owns one continuous normalized
progressvalue; thumb position, fill length, and displayed percentage all derive from it. - Gesture Handler updates progress directly on the UI thread and passes release velocity into an interruptible spring.
- Skia draws the fill, thumb, and velocity-reactive daughter droplet into a layer with blur and an alpha-threshold
ColorMatrix, producing a genuine metaball merge. - A short release value expands a fine ring around the thumb, while a layout-synchronized text transition rolls the numeric value.
- The imperative API coordinates the three launch animations without adding demo concerns to the reusable component.
Icons: Lucide (ISC).
Install
npx expo install @shopify/react-native-skia react-native-reanimated react-native-worklets react-native-gesture-handlerOn web, follow Expo's Skia setup so CanvasKit is loaded before this component is rendered.
Usage
import GooeyMergeSlider from './GooeyMergeSlider';
export function Mixer() {
return <GooeyMergeSlider />;
}import { useRef } from 'react';
import GooeyMergeSlider, {
type GooeyMergeSliderRef,
} from './GooeyMergeSlider';
export function CustomMixer() {
const ref = useRef<GooeyMergeSliderRef>(null);
return (
<GooeyMergeSlider
ref={ref}
rowGap={18}
trackColor="rgba(255,255,255,0.1)"
onValueChangeEnd={(index, value) => {
// Persist the settled value for the selected layer.
}}
/>
);
}Props
| Name | Type | Default | Description |
|---|---|---|---|
items | GooeyMergeSliderItem[] | Rain, Ocean, and Wind | Labels, Lucide icons, colors, and target values. |
onValueChange | (index, value) => void | — | Runs when a rounded value changes. |
onValueChangeEnd | (index, value) => void | — | Runs after a drag or track tap settles. |
fontFamily | string | System | Label font family. |
monoFontFamily | string | System | Numeric value font family. |
trackColor | string | rgba(255,255,255,0.08) | Unfilled track color. |
rowGap | number | 22 | Vertical space between rows. |
accessibilityLabel | string | Soundscape | Prefix for each adjustable control label. |
style | StyleProp<ViewStyle> | — | Outer-container styling. |
testID | string | — | Test identifier. |
The ref exposes playIntro(), setValue(index, value), and reset().
Compatibility
- Expo SDK 57
- React Native 0.86
- Reanimated 4.5
- React Native Worklets 0.10
- React Native Skia 2.6
- iOS: unverified
- Android: unverified
- Web: verified by export and bundle checks; visual review pending
- Expo Go: supported because every native dependency is included in this project's Expo Go runtime
Accessibility
Every row exposes the adjustable role, current value, and increment/decrement actions. The full 54-point track is tappable as an alternative to dragging. Reduce Motion places the sliders at their final values, removes the velocity trail and ripple, and keeps value changes immediate.




