react-native-particle
v1.0.2
Published
High-performance particle system for React Native — C++ engine via Nitro Modules with zero JS overhead. Runs entirely on the UI thread using Android Canvas & iOS Core Graphics. New Architecture ready.
Maintainers
Readme
react-native-particle
Native 2D particles for React Native with a simple preset-driven API, C++ simulation, and zero JS thread work on the render path.
Platform support: Android and iOS
Why this library
- Native render path with Android Canvas and iOS Core Graphics
- C++ simulation with preallocated buffers and SIMD-accelerated math
- No per-frame particle work on the JS thread
- Preset-driven API that is easy to drop into React screens
- Supports thousands of particles without sending frame data through JS
- Built on Nitro Modules and Fabric-compatible React Native views
- Useful for fire, smoke, sparkles, snow, confetti, and similar FX
When to use each API
| API | Use it when |
| --- | --- |
| NativeParticleSystem | You want to render a particle effect directly in a React Native screen |
| PresetConfig | You want to define how particles spawn, move, change size, change color, and blend |
| layer="background" | The effect should render behind sibling UI |
| layer="foreground" | The effect should render above sibling UI |
If you are building UI in React, start with NativeParticleSystem.
Installation
React Native (bare)
npm install react-native-particle react-native-nitro-modulesThen for iOS:
cd ios && pod installRequirements
- React Native
0.78.0or higher - Fabric enabled
- Node
18+
Quick start
This is the shortest useful flow for most apps:
- Define a
PresetConfig - Render
NativeParticleSystem - Tune
count, position, andemitInterval
import { View } from 'react-native'
import { NativeParticleSystem } from 'react-native-particle'
import type { PresetConfig } from 'react-native-particle'
const fire: PresetConfig = {
velocityX: [-34, 34],
velocityY: [-180, -70],
accelerationY: -8,
dampingVelocity: 0.972,
sizeStart: 22,
sizeEnd: 0,
sizeEase: 'easeOut',
lifetimeMin: 1.0,
lifetimeMax: 2.1,
colorStart: [0.08, 0.03, 0.0, 1.0],
colorMid: [1.0, 0.36, 0.02, 1.0],
colorMidPoint: 0.34,
colorEnd: [1.0, 0.95, 0.28, 0.0],
alphaStart: 0.0,
alphaEnd: 1.0,
alphaEase: 'pulse',
blendMode: 'additive',
emitRadius: 14,
}
export const FireDemo = () => {
return (
<View style={{ flex: 1 }}>
<NativeParticleSystem
preset={fire}
count={400}
x={200}
y={600}
layer="foreground"
loop
emitInterval={200}
/>
</View>
)
}This shows the smallest useful flow. In a real app, you will usually centralize your effect presets and reuse them across screens.
Recommended app-level usage
For apps with multiple screens or repeated effects, define presets once and reuse them instead of building config objects inline everywhere.
import type { PresetConfig } from 'react-native-particle'
export const smokePreset: PresetConfig = {
velocityX: [-18, 18],
velocityY: [-55, -18],
accelerationY: -3,
turbulenceX: 22,
turbulenceY: 12,
dampingVelocity: 0.989,
sizeStart: 18,
sizeEnd: 56,
lifetimeMin: 3.0,
lifetimeMax: 5.2,
colorStart: [0.48, 0.5, 0.54, 0.32],
colorEnd: [0.76, 0.78, 0.82, 0.0],
emitRadius: 10,
}Then screens can focus on layout and timing:
<NativeParticleSystem
preset={smokePreset}
count={20}
x={160}
y={420}
loop
emitInterval={420}
/>For a fuller working example, see the example/ app in this repository:
react-native-particle example
API summary
NativeParticleSystem
Use this component when you want a particle effect rendered natively inside a React Native screen.
Props:
preset: requiredPresetConfigcount?: particles emitted per burstx?,y?: emitter position in logical layout coordinatesloop?: whether the emitter should keep emittingemitInterval?: interval between bursts in millisecondslayer?:'background' | 'foreground'style?: host view override for layout, opacity, or z-order
Notes:
x = 0andy = 0are treated by native as “center me” for fullscreen effectsstyle.zIndexoverrides the default z-order implied bylayer- the host view uses
StyleSheet.absoluteFill, so make sure the parent has layout
PresetConfig
Use PresetConfig to define how particles spawn, move, evolve, and blend.
Motion
velocityX,velocityY: min/max spawn velocity in logical px/saccelerationX,accelerationY: constant accelerationturbulenceX,turbulenceY: per-frame random force for more organic motiondampingVelocity: drag multiplier applied each framelifetimeMin,lifetimeMax: particle lifetime range in seconds
Size
sizeStart,sizeEnd: size over lifetimesizeEase:linear | easeIn | easeOut | pulse
Color and alpha
colorStart,colorEnd: RGBA color trackcolorMid,colorMidPoint: optional 3-stop gradientalphaStart,alphaEnd,alphaEase: optional independent alpha trackrandomColor: random hue per particleblendMode:normal | additive
Emission
emitShape:point | circle | ring | lineemitRadius: radius forcircleandringemitWidth,emitHeight: dimensions forline
Particle shape
shape:circle | rect | linerotationMin,rotationMax: initial angle in degreesspinMin,spinMax: angular velocity in degrees/s
Effect recipes
Soft smoke
const smoke: PresetConfig = {
velocityX: [-18, 18],
velocityY: [-55, -18],
accelerationY: -3,
turbulenceX: 22,
turbulenceY: 12,
dampingVelocity: 0.989,
sizeStart: 18,
sizeEnd: 56,
lifetimeMin: 3.0,
lifetimeMax: 5.2,
colorStart: [0.48, 0.5, 0.54, 0.32],
colorEnd: [0.76, 0.78, 0.82, 0.0],
emitRadius: 10,
}Snow from a line emitter
const snow: PresetConfig = {
velocityX: [-22, 22],
velocityY: [18, 48],
accelerationY: 4,
turbulenceX: 10,
dampingVelocity: 0.998,
sizeStart: 6,
sizeEnd: 4,
lifetimeMin: 5.5,
lifetimeMax: 8.5,
colorStart: [0.96, 0.98, 1.0, 0.9],
colorEnd: [0.96, 0.98, 1.0, 0.0],
emitShape: 'line',
emitWidth: 120,
emitHeight: 20,
}Sparkles
const sparkles: PresetConfig = {
velocityX: [-110, 110],
velocityY: [-110, 110],
accelerationY: 40,
dampingVelocity: 0.92,
sizeStart: 5,
sizeEnd: 0,
sizeEase: 'easeOut',
lifetimeMin: 0.18,
lifetimeMax: 0.45,
alphaStart: 0.0,
alphaEnd: 1.0,
alphaEase: 'pulse',
randomColor: true,
blendMode: 'additive',
emitRadius: 12,
}Foreground confetti
const confetti: PresetConfig = {
velocityX: [-180, 180],
velocityY: [-260, -120],
accelerationY: 240,
turbulenceX: 24,
dampingVelocity: 0.985,
sizeStart: 9,
sizeEnd: 7,
sizeEase: 'pulse',
lifetimeMin: 1.8,
lifetimeMax: 2.8,
randomColor: true,
shape: 'rect',
rotationMin: -180,
rotationMax: 180,
spinMin: -540,
spinMax: 540,
emitShape: 'line',
emitWidth: 80,
emitHeight: 10,
}Platform behavior and limits
Android
- rendering uses
Canvas,Paint, andChoreographer - simulation and draw cadence stay on the native side
- additive blending is supported
iOS
- rendering uses
Core GraphicsandCADisplayLink - simulation and draw cadence stay on the native side
- additive blending is supported
Current limits
- this is a 2D particle engine
- particles are currently rendered as
circle,rect, orline - there is no sprite or texture particle support yet
- the engine is optimized for preset-driven FX, not arbitrary shader-based visuals
Architecture
flowchart TD
A["React Native API<br/>NativeParticleSystem"] --> B["Native platform view"]
B --> C["ParticleEngineCore (C++)"]
C --> D["Shared particle buffer<br/>position, color, size, rotation"]
D --> E["Android renderer<br/>Canvas + Choreographer"]
D --> F["iOS renderer<br/>Core Graphics + CADisplayLink"]The simulation runs in C++. Each platform steps the engine natively, reads the particle buffer directly, and draws using its own renderer. No per-frame particle data is sent through the JS thread.
Roadmap highlights
Upcoming priorities in ROADMAP.md:
- imperative ref API
- chained emitters and child bursts
- sprite or image particles
- expanded per-particle variation
- simple force fields
- preset validation and diagnostics
Troubleshooting
Nothing renders
Check the basics first:
- Fabric is enabled
- the parent view has size
- the emitter coordinates are inside the visible area
presetcontains valid ranges and values
The effect is behind or in front of the wrong UI
Use layer="background" or layer="foreground". If you need tighter control,
pass style={{ zIndex: ... }}.
The motion feels too linear
Increase turbulenceX / turbulenceY, add drag with dampingVelocity, and
use non-linear curves such as sizeEase: 'easeOut' or alphaEase: 'pulse'.
License
MIT
