Installation
npx expo install @shopify/react-native-skia react-native-reanimated react-native-worklets
2 Run a development build (Expo Go can't load Skia)
npx expo run:ios # or: npx expo run:android
Usage
Each look is one provider around your screen. Wrap a view in Surface (or Spot for butterflies) and the effect lands on it. These are the four scenes in the video.
Clear Sky
Butterflies fly around the screen and settle on the views you mark. Touch one and they take off.
import { StyleSheet , Text , View } from ' react-native ' ;
import {
ButterflyProvider ,
Spot ,
} from ' @/components/ambient-weather/butterfly ' ;
export function ClearSky () {
return (
< ButterflyProvider preset = " confetti " style = { styles . screen} >
< Spot butterflies = { 3 } radius = { 16 } on = { [ ' top ' ] } interactive >
< View style = { styles . card} >
< Text style = { styles . reading} > 26° </ Text >
</ View >
</ Spot >
</ ButterflyProvider >
) ;
}
const styles = StyleSheet . create ({
screen : { flex : 1 , justifyContent : ' center ' , padding : 24 } ,
card : { borderRadius : 16 , padding : 20 , backgroundColor : ' #fff ' } ,
reading : { fontSize : 48 , fontWeight : ' 600 ' } ,
}) ;
Autumn
Leaves grow at the tips of a tree picture, let go one at a time, land on your cards and blow away in the gusts.
import { Image , StyleSheet , Text , View } from ' react-native ' ;
import { LeafProvider , LeafTree } from ' @/components/ambient-weather/leaves ' ;
import { Surface } from ' @/lib/motion ' ;
// Twig tips as x,y,angle in the tree picture's own pixels.
const TWIGS = ' 708,41,-83 647,47,-98 580,88,-135 ' ;
const TREE_SIZE = { width : 1024 , height : 1536 } ;
export function Autumn () {
return (
< LeafProvider
leaf = { require ( ' ./leaf.png ' ) }
gusts = " windy "
style = { styles . screen}
>
< LeafTree twigs = { TWIGS } viewBox = { TREE_SIZE } style = { styles . tree} >
< Image source = { require ( ' ./tree.png ' ) } style = { styles . fill} />
</ LeafTree >
< Surface radius = { 16 } interactive >
< View style = { styles . card} >
< Text > Breezy all afternoon </ Text >
</ View >
</ Surface >
</ LeafProvider >
) ;
}
const styles = StyleSheet . create ({
screen : { flex : 1 , justifyContent : ' flex-end ' , padding : 24 } ,
tree : { position : ' absolute ' , top : 0 , right : 0 , width : 260 , height : 390 } ,
fill : { width : ' 100% ' , height : ' 100% ' } ,
card : { borderRadius : 16 , padding : 20 , backgroundColor : ' #fff ' } ,
}) ;
Blossom
Butterflies perch along the branches of a tree, traced as lines in an SVG, while a breeze carries glittering dust across the screen.
import { Image , StyleSheet } from ' react-native ' ;
import { Breeze } from ' @/components/ambient-weather/breeze ' ;
import {
ButterflyProvider ,
Spot ,
} from ' @/components/ambient-weather/butterfly ' ;
// The branches as lines, in the tree picture's own pixels.
const BRANCHES = ` <svg viewBox="0 0 1024 1536">
<polyline points="801,1250 761,966 702,764 656,561 592,441" />
<polyline points="691,752 609,710 486,663 335,619 256,592" />
</svg> ` ;
export function Blossom () {
return (
< ButterflyProvider preset = " monarch " mood = " calm " style = { styles . screen} >
< Spot
butterflies = { 12 }
size = { 18 }
svg = { BRANCHES }
on = { [ ' top ' , ' sides ' ] }
interactive
style = { styles . tree}
>
< Image source = { require ( ' ./tree.png ' ) } style = { styles . fill} />
</ Spot >
< Breeze
wind = { - 30 }
dust = { { density : 0.7 , vibrancy : 0.6 } }
style = { StyleSheet . absoluteFill}
/>
</ ButterflyProvider >
) ;
}
const styles = StyleSheet . create ({
screen : { flex : 1 } ,
tree : { position : ' absolute ' , top : 40 , right : 0 , width : 260 , height : 390 } ,
fill : { width : ' 100% ' , height : ' 100% ' } ,
}) ;
Rain
Rain collects as water along the top of your cards and drips off the edges. Lightning flashes in the sky behind them.
import { StyleSheet , Text , View } from ' react-native ' ;
import { Lightning , RainProvider } from ' @/components/ambient-weather/rain ' ;
import { Surface } from ' @/lib/motion ' ;
export function Monsoon ( { onThunder }: { onThunder : () => void }) {
return (
< RainProvider preset = " storm " onStrike = { onThunder } style = { styles . screen} >
< View style = { styles . sky} >
< Lightning />
</ View >
< Surface radius = { 16 } interactive >
< View style = { styles . card} >
< Text style = { styles . text} > Heavy rain until 9 pm </ Text >
</ View >
</ Surface >
</ RainProvider >
) ;
}
const styles = StyleSheet . create ({
screen : { flex : 1 , padding : 24 , backgroundColor : ' #11151B ' } ,
sky : { height : 280 } ,
card : { borderRadius : 16 , padding : 20 , backgroundColor : ' #232A33 ' } ,
text : { color : ' #ECF0F5 ' } ,
}) ;
How it works
Each effect runs on one frame loop on the UI thread and draws with Skia, so nothing re-renders in React while it moves. A shared scene keeps one list of the views you mark: rain fills a pool of water along each top edge, leaves lie on a ledge until a gust takes them, and butterflies pick landing spots from the view's box, outline or SVG. Particles are parked and reused rather than freed, and the clock stops while paused, in the background or under Reduce Motion.
More once you own it
Examples, the full list of props and every advanced option for Ambient Weather are here for owners.