A high-performance, GPU-accelerated particle system visualization that creates interactive wave-like effects using WebGL. The system demonstrates advanced particle physics and modern web technologies.
- WebGL: Core rendering engine for GPU-accelerated graphics
- TypeScript: Type-safe implementation of particle physics and system logic
- React: UI components and state management
- Tailwind CSS + shadcn/ui: Styling and UI components
The particle system is built around three main components:
-
WebGL Renderer:
- Uses custom GLSL shaders for efficient particle rendering
- Implements point sprites with smooth alpha transitions
- Handles all GPU-based computations
-
Particle Physics:
- Wave propagation physics for ripple effects
- Spring-based home position system
- Velocity and force-based movement
-
Performance Monitoring:
- Real-time FPS tracking
- Particle count monitoring
- WebGL draw call tracking
- Memory usage statistics
The system uses a wave-based interaction model:
-
Wave Sources:
- Created on mouse click/drag
- Propagate outward with decreasing intensity
- Time-based decay for natural fading
-
Particle Response:
- Each particle maintains a "home" position
- Responds to waves based on distance and time
- Uses spring forces to return to home position
- Smooth damping for natural movement
-
Wave Physics:
// Wave parameters const waveSpeed = 3; const waveRadius = time * waveSpeed; const waveWidth = 40; // Force calculation const waveIntensity = Math.exp(-wavePosition * 1.5) * Math.sin(wavePosition * Math.PI); const timeDecay = Math.exp(-time * 0.02); const distanceDecay = Math.exp(-distance * 0.001);
-
Frame Update Cycle:
- RequestAnimationFrame loop for smooth animation
- GPU-accelerated rendering using WebGL
- Efficient buffer updates for position, color, and size
-
Shader Implementation:
- Vertex shader for position and size computation
- Fragment shader for smooth particle appearance
- Alpha blending for transparent particles
-
CPU-Side Physics:
- Particle physics calculations are still CPU-bound
- Each particle's position update runs on the main thread
- Wave propagation calculations could be moved to GPU
-
Buffer Updates:
- Frequent buffer updates between CPU and GPU
- New Float32Array creation on every frame
- Could benefit from persistent buffers
-
GPU Physics Computation:
- Move physics calculations to compute shaders
- Implement transform feedback for position updates
- Use texture-based position storage
-
Memory Optimization:
- Implement object pooling for particles
- Use static Float32Arrays
- Implement frustum culling for off-screen particles
-
Advanced Features:
- Multiple wave interference patterns
- Particle color based on velocity
- Advanced fluid dynamics simulation
- Particle collision detection
- Custom wave patterns and shapes
-
Performance Enhancements:
- Implement instanced rendering
- Use WebGL 2.0 features when available
- Add adaptive particle count based on FPS
- Implement spatial partitioning for optimized updates
The fluidity and visual quality of our particle system significantly improve when we increase the number of threads and particles per thread. This enhancement occurs due to several key factors:
The number of threads and particles essentially determines the "resolution" of our fluid simulation. Similar to how a higher resolution display provides more detailed images, more particles allow for:
- Finer granularity in wave propagation
- Smoother transitions between affected areas
- More precise representation of force fields
- Better visual continuity in motion
For example, with 20 threads and 50 particles per thread (1,000 total particles), we have a moderate resolution that might show visible gaps in wave propagation. Increasing to 50 threads and 200 particles per thread (10,000 total particles) creates a much more continuous and fluid-like appearance.
The wave effect in our system propagates through particles by:
const waveSpeed = 5;
const waveRadius = time * waveSpeed;
const waveWidth = 80;
// Wave intensity calculation
const waveIntensity = Math.exp(-wavePosition * 2) * Math.sin(wavePosition * Math.PI * 2);
With more particles:
- Forces propagate more smoothly between neighboring particles
- Wave patterns appear more natural and continuous
- Energy transfer between particles becomes more gradual
- Interference patterns become more apparent and realistic
Higher particle density provides better interpolation between affected areas:
- Waves appear smoother due to more intermediate points
- Force gradients have finer transitions
- Motion appears more fluid with less visible "stepping"
- Visual artifacts from discretization become less noticeable
Our WebGL implementation actually benefits from higher particle counts:
- Modern GPUs are optimized for parallel processing of many similar elements
- The overhead of setting up WebGL contexts and shaders remains constant
- The cost per particle is minimal due to hardware optimization
- Batch processing of particles is highly efficient
More particles lead to better physical simulation because:
- Forces can propagate more accurately through the system
- Local interactions become more precise
- Edge cases and boundaries are better represented
- The overall system behaves more like a continuous medium
The improved visual quality comes from our shader implementation:
void main() {
vec2 coord = gl_PointCoord - vec2(0.5);
float distance = length(coord);
if (distance > 0.5) {
discard;
}
float alpha = 1.0 - smoothstep(0.45, 0.5, distance);
gl_FragColor = vec4(v_color, alpha);
}
This shader creates smooth particles that blend together, and with higher particle counts, these blended areas create a more continuous appearance.
While increasing particle count improves visual quality, there's a balance to strike:
- GPU memory usage increases linearly with particle count
- Frame rate needs to remain stable for smooth animation
- Browser capabilities and hardware limitations need consideration
- Diminishing returns occur at very high particle counts
For most modern devices, we've found these ranges to work well:
- Threads: 20-50
- Particles per thread: 50-200
- Total particles: 1,000-10,000
These numbers provide excellent visual quality while maintaining performance across different devices.
The improvement in visual quality with higher particle counts is not just about "more is better" - it's about reaching a density where discrete particles begin to create the illusion of a continuous medium. This threshold, combined with our physics simulation and WebGL rendering, creates the fluid, organic motion that makes the particle system so engaging.
Understanding these principles helps in optimizing the balance between visual quality and performance, ensuring the best possible user experience across different devices and contexts.
- Clone the repository
- Install dependencies:
npm install
- Start the development server:
npm run dev
- Open browser and navigate to the provided URL
- Click and drag to create wave effects
- Use the control panel to adjust:
- Thread count
- Particles per thread
- Force intensity
Requires a browser with WebGL support. Tested on:
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+