Party 🎉 - caza.la/party
A high-performance particle physics simulation system with interactive playground, built with TypeScript and WebGPU/CPU dual runtime support.
- Dual Runtime Architecture: Auto-selection between WebGPU (GPU compute) and CPU fallback for maximum compatibility
- High Performance: Spatial grid optimization, configurable workgroup sizes, and efficient neighbor queries
- Modular Force System: Environment, boundary, collisions, behavior (flocking), fluid dynamics (SPH), sensors, joints, interaction, and grab modules
- Advanced Rendering: Trails with decay/diffusion, particle instancing, line rendering, and multiple color modes
- Playground Sessions: Save/load/share sessions (module settings, oscillators, and optional particles/joints)
- Real-time Oscillators: Animate any module parameter with configurable frequency and bounds
- Interactive Playground: React-based interface with undo/redo, hotkeys, and live parameter adjustment
- Text Spawner: Spawn particles from text in the core spawner and INIT UI
Comprehensive documentation is available in the docs/ directory:
-
User Guide: Complete guide for using the core library as an end user. Covers engine API, runtime selection, module configuration, particle management, oscillators, and all built-in force and render modules with examples.
-
Module Author Guide: Step-by-step guide for creating custom force and render modules. Covers the module lifecycle, input/output system, CPU and WebGPU implementations, and best practices.
-
Maintainer Guide: Internal architecture documentation for contributors. Explains code organization, dual runtime system (CPU/WebGPU), module system, spatial grid, pipelines, oscillators, and how to extend the system.
-
Playground User Guide: Guide for using the interactive playground application. Covers the UI, tools, session management, oscillators, hotkeys, and workflow tips.
-
Playground Maintainer Guide: Technical documentation for maintaining and extending the playground. Covers architecture, state management (Redux), component structure, hooks, tools system, and development workflows.
This is a monorepo containing:
@cazala/party - Core Engine
The heart of the system - a TypeScript particle physics engine featuring:
- Engine: Unified API with automatic WebGPU/CPU runtime selection
- Modular Architecture: Pluggable force and render modules with lifecycle management
- Spatial Optimization: Efficient neighbor queries via spatial grid partitioning
- Advanced Physics: Gravity, collisions, flocking, fluid dynamics, joints, and more
- Oscillators: Animate any module parameter over time with smooth interpolation
- Configuration Export/Import: Serialize complete simulation states
Playground - Interactive Application
A React-based web application providing:
- Visual Interface: Real-time controls for all simulation parameters
- Multiple Tools: Spawn, grab, joint, pin, remove, and draw modes
- Session System: Save, load, rename, duplicate, and reorder sessions with drag-and-drop
- Oscillator UI: Visual sliders with speed cycling and parameter automation
- Hotkeys: Comprehensive keyboard shortcuts for efficient workflow
- Undo/Redo: Full history system for non-destructive editing
worker - Cloudflare Worker
A route-scoped reverse proxy that serves the playground at caza.la/party while proxying to party.caza.la.
# Clone the repository
git clone https://github.com/cazala/party.git
cd party
# Install dependencies
npm run setup
# Start the playground in development mode
npm run devVisit http://localhost:3000 to access the interactive playground.
Note: This project uses pnpm workspaces internally for optimal monorepo management, but all commands are available through npm scripts. You only need npm installed.
import {
Engine,
// Force modules
Environment,
Boundary,
Collisions,
Behavior,
Fluids,
// Render modules
Particles,
Trails,
} from "@cazala/party";
const canvas = document.querySelector("canvas")!;
const forces = [
// Environmental physics
new Environment({
gravityStrength: 600,
gravityDirection: "down",
inertia: 0.05,
friction: 0.01,
}),
// Boundary interactions
new Boundary({
mode: "bounce",
restitution: 0.9,
friction: 0.1,
}),
// Particle collisions
new Collisions({ restitution: 0.85 }),
// Flocking behavior
new Behavior({
cohesion: 1.5,
alignment: 1.2,
separation: 12,
viewRadius: 100,
}),
// Fluid dynamics
new Fluids({
influenceRadius: 80,
pressureMultiplier: 25,
viscosity: 0.8,
}),
];
const render = [
new Trails({ trailDecay: 10, trailDiffuse: 4 }),
new Particles({ colorType: 2, hue: 0.55 }), // Hue-based coloring
];
const engine = new Engine({
canvas,
forces,
render,
runtime: "auto", // Auto-selects WebGPU when available, fallback to CPU
});
await engine.initialize();
// Add some particles
for (let i = 0; i < 100; i++) {
engine.addParticle({
position: { x: Math.random() * canvas.width, y: Math.random() * canvas.height },
velocity: { x: (Math.random() - 0.5) * 4, y: (Math.random() - 0.5) * 4 },
mass: 1 + Math.random() * 2,
size: 3 + Math.random() * 7,
color: { r: 1, g: 1, b: 1, a: 1 },
});
}
engine.play();┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Engine │ │ WebGPU │ │ CPU │
│ │ │ Runtime │ │ Runtime │
│ • Auto-select │◄──►│ • GPU Compute │ │ • Canvas2D │
│ • Unified API │ │ • Spatial Grid │ │ • JS Simulation │
│ • Module System │ │ • WGSL Shaders │ │ • Fallback │
└─────────────────┘ └─────────────────┘ └─────────────────┘
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Force Modules │ │ Render Modules │ │ Oscillators │
│ │ │ │ │ │
│ • Environment │ │ • Particles │ │ • Parameter │
│ • Boundary │ │ • Trails │ │ Animation │
│ • Collisions │ │ • Lines │ │ • Time-based │
│ • Behavior │ │ │ │ • Configurable │
│ • Fluids │ │ │ │ Frequency │
│ • Sensors │ │ │ │ │
│ • Interaction │ │ │ │ │
│ • Joints │ │ │ │ │
│ • Grab │ │ │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
The force system uses a multi-phase lifecycle for optimal performance:
- state: Global pre-computation (e.g., fluid density calculation)
- apply: Per-particle force application (acceleration/velocity changes)
- constrain: Position corrections and constraints (iterative)
- correct: Post-integration velocity corrections
- Environment: Gravity, inertia, friction, and damping with directional/radial options
- Boundary: Boundary interactions (bounce, kill, warp) with repel forces and friction
- Collisions: Particle-particle collision detection and elastic response
- Behavior: Flocking behaviors (cohesion, alignment, separation, wander, chase/avoid)
- Fluids: Smoothed Particle Hydrodynamics (SPH) with near-pressure optimization
- Sensors: Trail-following and color-based steering with configurable behaviors
- Interaction: User-controlled attraction/repulsion with falloff
- Joints: Distance constraints between particles with momentum preservation
- Grab: Efficient single-particle mouse/touch dragging
- Particles: Instanced particle rendering with multiple color modes and pinned particle visualization
- Trails: Decay and diffusion effects with performance-optimized compute passes
- Lines: Line rendering between particle pairs with configurable styling
# Install dependencies for all packages
npm run setup
# Build the core library
npm run build:core
# Start development server for playground
npm run dev
# Build everything for production
npm run build
# Type check all packages
npm run type-check
# Run tests
npm testparty/
├── packages/
│ ├── core/ # Core engine library
│ │ ├── src/
│ │ │ ├── engine.ts # Main engine facade
│ │ │ ├── index.ts # Public API exports
│ │ │ ├── interfaces.ts # Common interfaces
│ │ │ ├── modules/ # Force and render modules
│ │ │ │ ├── forces/
│ │ │ │ └── render/
│ │ │ ├── oscillators.ts
│ │ │ ├── particle.ts
│ │ │ ├── runtimes/ # WebGPU and CPU implementations
│ │ │ │ ├── cpu/
│ │ │ │ └── webgpu/
│ │ │ ├── spawner.ts
│ │ │ ├── vector.ts
│ │ │ └── view.ts
│ │ ├── package.json
│ │ ├── README.md
│ │ ├── rollup.config.js
│ │ └── tsconfig.json
│ └── playground/ # React playground application
│ ├── src/
│ │ ├── components/
│ │ ├── constants/
│ │ ├── contexts/
│ │ ├── history/
│ │ ├── hooks/
│ │ ├── slices/
│ │ ├── styles/
│ │ ├── types/
│ │ └── utils/
│ ├── index.html
│ ├── package.json
│ ├── README.md
│ ├── tsconfig.json
│ └── vite.config.js
├── docs/ # Documentation
│ ├── maintainer-guide.md
│ ├── module-author-guide.md
│ ├── playground-maintainer-guide.md
│ ├── playground-user-guide.md
│ └── user-guide.md
├── LICENSE
├── package.json # Root workspace configuration
├── package-lock.json
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── tsconfig.json
└── README.md
- GPU Compute: Parallel particle processing with configurable workgroup sizes
- Spatial Grid: GPU-accelerated neighbor queries
- Memory Efficiency: Optimized buffer layouts and minimal CPU-GPU transfers
- Scalability: Handles thousands of particles at 60+ FPS
- Fallback Compatibility: Works on all devices without WebGPU support
- Spatial Optimization: Efficient neighbor queries via spatial partitioning
- Canvas2D Rendering: Hardware-accelerated 2D graphics
- Memory Management: Minimal allocations in tight loops
- cellSize: Spatial grid resolution (8-64 typical)
- maxNeighbors: Neighbor query limit (64-256 typical)
- constrainIterations: Constraint solver iterations (CPU: ~5, WebGPU: ~50)
- workgroupSize: WebGPU compute workgroup size (32-256)
- maxParticles: Limit the number of particles processed in simulation and rendering. When set to a number, only particles with index <
maxParticlesare processed. Set tonull(default) to process all particles. Useful for performance tuning.
- WebGPU: Chrome 113+, Edge 113+, Safari 18+, Firefox (experimental)
- Mobile: iOS 18+ (Safari), Android (Chrome 113+)
- CPU Fallback: All modern browsers with Canvas2D support
- Auto-Detection: Seamless fallback when WebGPU is unavailable
This project is licensed under the MIT License - see the LICENSE file for details.
We welcome contributions! Please see our maintainer guide for architecture details and module author guide for creating custom modules.