AI Particle Fountain - Physics Particle System Click to spawn beautiful particle fountains! Particl
58 views Β· Dec 12, 2025
Create an "AI Particle Fountain" - beautiful particle system where particles shoot upward like a fou
π Embed Code
Copy this code to embed this sketch on your website:
<iframe src="https://p5js.ai/embed/ef8fa8ee-dff1-414e-b990-4d4bbafd8824" width="400" height="400" frameborder="0" allow="autoplay" title="AI Particle Fountain - Physics Particle System Click to spawn beautiful particle fountains! Particl"></iframe>
Adjust width and height as needed. Preview embed β
β About This Sketch 3 questions
What visual effects can I expect from the AI Particle Fountain sketch?
The sketch creates stunning visual displays of particles shooting upward like fountains, with beautiful color effects and motion blur that enhance the overall aesthetic.
How can I interact with the AI Particle Fountain sketch?
Users can click anywhere on the canvas to spawn new particle fountains, and adjust the gravity and speed of the particles using sliders.
What creative coding concepts does the AI Particle Fountain sketch demonstrate?
This sketch showcases a physics-based particle system, utilizing concepts like force application, collision detection, and color blending to create dynamic visual effects.
π¬ AI Chat History 2 messages
π€ You
Create an "AI Particle Fountain" - beautiful particle system where particles shoot upward like a fountain and fall with gravity. Users click to spawn new fountains at cursor position. Include particle color based on velocity, trail effects, collision with ground that creates splash particles, and adjustable gravity/initial velocity sliders. Visual style: glowing colorful particles on dark background with smooth motion blur.
π€ AI
Hereβs a complete implementation of the βAI Particle Fountainβ with:
- Multiple fountains (click to add)
- Gravity & initial velocity sliders
- Velocity-based colors
- Trails / motion blur
- Ground collision with splash particles
- Glowing particles on a dark background
---
### sketch.js
```js
// sketch.js
// Global arrays
let fountains = [];
let particles = [];
let splashes = [];
// Physics parameters (controlled via sliders)
let gravity = 0.18;
let baseInitialSpeed = 8.0;
// UI elements
let gravitySlider, speedSlider;
let gravityValueSpan, speedValueSpan;
let groundY;
function setup() {
const cnv = createCanvas(windowWidth, windowHeight);
cnv.id('p5canvas');
// Only spawn fountains when clicking the canvas, not UI
cnv.mousePressed(spawnFountainAtMouse);
// Use HSB for easier color control
// https://p5js.org/reference/#/p5/colorMode
colorMode(HSB, 360, 100, 100, 100);
groundY = height * 0.9;
setupUI();
// Start with one fountain near the "ground"
fountains.push(new Fountain(width / 2, groundY));
}
function draw() {
// Motion-blur / trail effect: semi-transparent background
// https://p5js.org/reference/#/p5/background
background(0, 0, 0, 20);
// Update parameters from sliders
gravity = gravitySlider.value();
baseInitialSpeed = speedSlider.value();
gravityValueSpan.html(gravity.toFixed(2));
speedValueSpan.html(baseInitialSpeed.toFixed(1));
const gravityForce = createVector(0, gravity);
drawGround();
// Emit particles from all active fountains
for (let i = fountains.length - 1; i >= 0; i--) {
const f = fountains[i];
f.emit();
if (f.isDead()) {
fountains.splice(i, 1);
}
}
// Additive blending for glowing particles
// https://p5js.org/reference/#/p5/blendMode
blendMode(ADD);
// Main fountain particles
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.applyForce(gravityForce);
p.update();
// Collision with ground -> create splash particles
if (p.pos.y >= groundY && p.vel.y > 0) {
createSplash(p);
particles.splice(i, 1);
continue;
}
p.display();
if (p.isDead()) {
particles.splice(i, 1);
}
}
// Splash (secondary) particles
for (let i = splashes.length - 1; i >= 0; i--) {
const s = splashes[i];
s.applyForce(gravityForce);
s.update();
s.display();
// Let splash particles bounce very slightly off the ground
if (s.pos.y >= groundY && s.vel.y > 0) {
s.pos.y = groundY;
s.vel.y *= -0.3; // small bounce
}
if (s.isDead()) {
splashes.splice(i, 1);
}
}
// Return to normal blending (for anything else that might be drawn)
blendMode(BLEND);
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
groundY = height * 0.9;
}
// =======================
// UI SETUP
// =======================
function setupUI() {
// Using p5 DOM functions (bundled in core)
// https://p5js.org/reference/#/p5/createDiv
const ui = createDiv();
ui.id('ui');
ui.style('position', 'fixed');
ui.style('top', '16px');
ui.style('left', '16px');
ui.style('padding', '10px 12px');
ui.style('border-radius', '8px');
ui.style('background', 'rgba(0, 0, 0, 0.45)');
ui.style('color', '#fff');
ui.style('font-family', 'system-ui, sans-serif');
ui.style('font-size', '13px');
ui.style('backdrop-filter', 'blur(8px)');
ui.style('-webkit-backdrop-filter', 'blur(8px)');
ui.style('z-index', '10');
ui.style('user-select', 'none');
// Gravity row
const gRow = createDiv();
gRow.parent(ui);
gRow.style('margin-bottom', '6px');
const gLabel = createSpan('Gravity:');
gLabel.parent(gRow);
gLabel.style('margin-right', '6px');
// https://p5js.org/reference/#/p5/createSlider
gravitySlider = createSlider(0, 0.6, gravity, 0.01);
gravitySlider.parent(gRow);
gravitySlider.style('width', '120px');
gravitySlider.style('vertical-align', 'middle');
gravityValueSpan = createSpan(gravity.toFixed(2));
gravityValueSpan.parent(gRow);
gravityValueSpan.style('margin-left', '6px');
gravityValueSpan.style('opacity', '0.8');
// Speed / initial velocity row
const sRow = createDiv();
sRow.parent(ui);
const sLabel = createSpan('Initial speed:');
sLabel.parent(sRow);
sLabel.style('margin-right', '6px');
speedSlider = createSlider(2, 20, baseInitialSpeed, 0.1);
speedSlider.parent(sRow);
speedSlider.style('width', '120px');
speedSlider.style('vertical-align', 'middle');
speedValueSpan = createSpan(baseInitialSpeed.toFixed(1));
speedValueSpan.parent(sRow);
speedValueSpan.style('margin-left', '6px');
speedValueSpan.style('opacity', '0.8');
}
// =======================
// FOUNTAIN & PARTICLES
// =======================
class Fountain {
constructor(x, y) {
this.origin = createVector(x, y);
this.emissionRate = 6; // particles per frame
this.lifeFrames = 60 * 4; // fountain emits for ~4 seconds
}
emit() {
if (this.lifeFrames <= 0) return;
for (let i = 0; i < this.emissionRate; i++) {
// Random angle in upward hemisphere
const angle = random(-PI, 0);
const speed = baseInitialSpeed * random(0.6, 1.2);
// Narrow cone: reduce horizontal component slightly
const vx = cos(angle) * speed * 0.5;
const vy = sin(angle) * speed;
particles.push(new Particle(this.origin.x, this.origin.y, vx, vy, false));
}
this.lifeFrames--;
}
isDead() {
return this.lifeFrames <= 0;
}
}
class Particle {
constructor(x, y, vx, vy, isSplash) {
// https://p5js.org/reference/#/p5.Vector
this.pos = createVector(x, y);
this.vel = createVector(vx, vy);
this.acc = createVector(0, 0);
this.isSplash = isSplash;
this.size = isSplash ? random(2, 5) : random(4, 8);
this.lifespan = isSplash ? 180 : 255; // frames
}
applyForce(force) {
this.acc.add(force);
}
update() {
this.vel.add(this.acc);
this.pos.add(this.vel);
this.acc.mult(0);
this.lifespan -= this.isSplash ? 5 : 3;
}
isDead() {
return this.lifespan <= 0 ||
this.pos.y < -50 ||
this.pos.x < -50 ||
this.pos.x > width + 50;
}
display() {
const speed = this.vel.mag();
const maxSpeed = 18;
// Map speed to color (slow = blue, fast = pink/white)
const hue = map(speed, 0, maxSpeed, 180, 330, true);
const brightness = map(speed, 0, maxSpeed, 60, 100, true);
const baseAlpha = map(this.lifespan, 0, 255, 0, 100, true);
noStroke();
// Draw multiple circles for a soft glow
// https://p5js.org/reference/#/p5/circle
for (let i = 3; i >= 1; i--) {
const r = this.size * i;
const a = baseAlpha / (i * i); // fade outer halos
fill(hue, 80, brightness, a);
circle(this.pos.x, this.pos.y, r);
}
}
}
// =======================
// HELPERS
// =======================
function drawGround() {
// Soft ground area
noStroke();
fill(210, 20, 15, 40);
rect(0, groundY, width, height - groundY);
// Ground line
stroke(210, 10, 60, 70);
strokeWeight(2);
line(0, groundY, width, groundY);
}
function createSplash(particle) {
const count = floor(random(8, 16));
const impactSpeed = particle.vel.mag();
for (let i = 0; i < count; i++) {
// Splash angles mostly upwards & sideways
const angle = random(-PI, 0);
const speed = impactSpeed * random(0.2, 0.7);
const vx = cos(angle) * speed;
const vy = sin(angle) * speed * 0.7;
splashes.push(
new Particle(
particle.pos.x,
groundY - 1, // slightly above ground
vx,
vy,
true
)
);
}
}
// Spawn a new fountain at the mouse position
function spawnFountainAtMouse() {
fountains.push(new Fountain(mouseX, mouseY));
}
```
---
### index.html
Make sure p5.js is loaded **before** `sketch.js`.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>AI Particle Fountain</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- p5.js core library (v1.11.3) -->
<!-- https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
<!-- Your sketch -->
<script src="sketch.js"></script>
</body>
</html>
```
---
### style.css
```css
/* style.css */
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
background: radial-gradient(circle at top, #1a1f33 0%, #050509 55%, #000 100%);
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
sans-serif;
color: #fff;
}
/* p5 canvas */
canvas {
display: block;
}
/* Optional: sliders styling for nicer look */
#ui input[type="range"] {
-webkit-appearance: none;
appearance: none;
height: 4px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.25);
outline: none;
margin: 0 4px;
}
#ui input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 14px;
height: 14px;
border-radius: 50%;
background: #ffffff;
cursor: pointer;
box-shadow: 0 0 6px rgba(0, 0, 0, 0.5);
}
#ui input[type="range"]::-moz-range-thumb {
width: 14px;
height: 14px;
border-radius: 50%;
background: #ffffff;
cursor: pointer;
border: none;
box-shadow: 0 0 6px rgba(0, 0, 0, 0.5);
}
```
---
Usage notes:
- Click anywhere on the canvas to spawn a new fountain at that position.
- Use the **Gravity** slider to control how quickly particles fall.
- Use the **Initial speed** slider to control how powerfully particles shoot upward.
- Colors shift from cooler (blue) to hotter (pink/white) with higher velocity, and trails are created via the semi-transparent background and additive blending.
- Multiple fountains (click to add)
- Gravity & initial velocity sliders
- Velocity-based colors
- Trails / motion blur
- Ground collision with splash particles
- Glowing particles on a dark background
---
### sketch.js
```js
// sketch.js
// Global arrays
let fountains = [];
let particles = [];
let splashes = [];
// Physics parameters (controlled via sliders)
let gravity = 0.18;
let baseInitialSpeed = 8.0;
// UI elements
let gravitySlider, speedSlider;
let gravityValueSpan, speedValueSpan;
let groundY;
function setup() {
const cnv = createCanvas(windowWidth, windowHeight);
cnv.id('p5canvas');
// Only spawn fountains when clicking the canvas, not UI
cnv.mousePressed(spawnFountainAtMouse);
// Use HSB for easier color control
// https://p5js.org/reference/#/p5/colorMode
colorMode(HSB, 360, 100, 100, 100);
groundY = height * 0.9;
setupUI();
// Start with one fountain near the "ground"
fountains.push(new Fountain(width / 2, groundY));
}
function draw() {
// Motion-blur / trail effect: semi-transparent background
// https://p5js.org/reference/#/p5/background
background(0, 0, 0, 20);
// Update parameters from sliders
gravity = gravitySlider.value();
baseInitialSpeed = speedSlider.value();
gravityValueSpan.html(gravity.toFixed(2));
speedValueSpan.html(baseInitialSpeed.toFixed(1));
const gravityForce = createVector(0, gravity);
drawGround();
// Emit particles from all active fountains
for (let i = fountains.length - 1; i >= 0; i--) {
const f = fountains[i];
f.emit();
if (f.isDead()) {
fountains.splice(i, 1);
}
}
// Additive blending for glowing particles
// https://p5js.org/reference/#/p5/blendMode
blendMode(ADD);
// Main fountain particles
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.applyForce(gravityForce);
p.update();
// Collision with ground -> create splash particles
if (p.pos.y >= groundY && p.vel.y > 0) {
createSplash(p);
particles.splice(i, 1);
continue;
}
p.display();
if (p.isDead()) {
particles.splice(i, 1);
}
}
// Splash (secondary) particles
for (let i = splashes.length - 1; i >= 0; i--) {
const s = splashes[i];
s.applyForce(gravityForce);
s.update();
s.display();
// Let splash particles bounce very slightly off the ground
if (s.pos.y >= groundY && s.vel.y > 0) {
s.pos.y = groundY;
s.vel.y *= -0.3; // small bounce
}
if (s.isDead()) {
splashes.splice(i, 1);
}
}
// Return to normal blending (for anything else that might be drawn)
blendMode(BLEND);
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
groundY = height * 0.9;
}
// =======================
// UI SETUP
// =======================
function setupUI() {
// Using p5 DOM functions (bundled in core)
// https://p5js.org/reference/#/p5/createDiv
const ui = createDiv();
ui.id('ui');
ui.style('position', 'fixed');
ui.style('top', '16px');
ui.style('left', '16px');
ui.style('padding', '10px 12px');
ui.style('border-radius', '8px');
ui.style('background', 'rgba(0, 0, 0, 0.45)');
ui.style('color', '#fff');
ui.style('font-family', 'system-ui, sans-serif');
ui.style('font-size', '13px');
ui.style('backdrop-filter', 'blur(8px)');
ui.style('-webkit-backdrop-filter', 'blur(8px)');
ui.style('z-index', '10');
ui.style('user-select', 'none');
// Gravity row
const gRow = createDiv();
gRow.parent(ui);
gRow.style('margin-bottom', '6px');
const gLabel = createSpan('Gravity:');
gLabel.parent(gRow);
gLabel.style('margin-right', '6px');
// https://p5js.org/reference/#/p5/createSlider
gravitySlider = createSlider(0, 0.6, gravity, 0.01);
gravitySlider.parent(gRow);
gravitySlider.style('width', '120px');
gravitySlider.style('vertical-align', 'middle');
gravityValueSpan = createSpan(gravity.toFixed(2));
gravityValueSpan.parent(gRow);
gravityValueSpan.style('margin-left', '6px');
gravityValueSpan.style('opacity', '0.8');
// Speed / initial velocity row
const sRow = createDiv();
sRow.parent(ui);
const sLabel = createSpan('Initial speed:');
sLabel.parent(sRow);
sLabel.style('margin-right', '6px');
speedSlider = createSlider(2, 20, baseInitialSpeed, 0.1);
speedSlider.parent(sRow);
speedSlider.style('width', '120px');
speedSlider.style('vertical-align', 'middle');
speedValueSpan = createSpan(baseInitialSpeed.toFixed(1));
speedValueSpan.parent(sRow);
speedValueSpan.style('margin-left', '6px');
speedValueSpan.style('opacity', '0.8');
}
// =======================
// FOUNTAIN & PARTICLES
// =======================
class Fountain {
constructor(x, y) {
this.origin = createVector(x, y);
this.emissionRate = 6; // particles per frame
this.lifeFrames = 60 * 4; // fountain emits for ~4 seconds
}
emit() {
if (this.lifeFrames <= 0) return;
for (let i = 0; i < this.emissionRate; i++) {
// Random angle in upward hemisphere
const angle = random(-PI, 0);
const speed = baseInitialSpeed * random(0.6, 1.2);
// Narrow cone: reduce horizontal component slightly
const vx = cos(angle) * speed * 0.5;
const vy = sin(angle) * speed;
particles.push(new Particle(this.origin.x, this.origin.y, vx, vy, false));
}
this.lifeFrames--;
}
isDead() {
return this.lifeFrames <= 0;
}
}
class Particle {
constructor(x, y, vx, vy, isSplash) {
// https://p5js.org/reference/#/p5.Vector
this.pos = createVector(x, y);
this.vel = createVector(vx, vy);
this.acc = createVector(0, 0);
this.isSplash = isSplash;
this.size = isSplash ? random(2, 5) : random(4, 8);
this.lifespan = isSplash ? 180 : 255; // frames
}
applyForce(force) {
this.acc.add(force);
}
update() {
this.vel.add(this.acc);
this.pos.add(this.vel);
this.acc.mult(0);
this.lifespan -= this.isSplash ? 5 : 3;
}
isDead() {
return this.lifespan <= 0 ||
this.pos.y < -50 ||
this.pos.x < -50 ||
this.pos.x > width + 50;
}
display() {
const speed = this.vel.mag();
const maxSpeed = 18;
// Map speed to color (slow = blue, fast = pink/white)
const hue = map(speed, 0, maxSpeed, 180, 330, true);
const brightness = map(speed, 0, maxSpeed, 60, 100, true);
const baseAlpha = map(this.lifespan, 0, 255, 0, 100, true);
noStroke();
// Draw multiple circles for a soft glow
// https://p5js.org/reference/#/p5/circle
for (let i = 3; i >= 1; i--) {
const r = this.size * i;
const a = baseAlpha / (i * i); // fade outer halos
fill(hue, 80, brightness, a);
circle(this.pos.x, this.pos.y, r);
}
}
}
// =======================
// HELPERS
// =======================
function drawGround() {
// Soft ground area
noStroke();
fill(210, 20, 15, 40);
rect(0, groundY, width, height - groundY);
// Ground line
stroke(210, 10, 60, 70);
strokeWeight(2);
line(0, groundY, width, groundY);
}
function createSplash(particle) {
const count = floor(random(8, 16));
const impactSpeed = particle.vel.mag();
for (let i = 0; i < count; i++) {
// Splash angles mostly upwards & sideways
const angle = random(-PI, 0);
const speed = impactSpeed * random(0.2, 0.7);
const vx = cos(angle) * speed;
const vy = sin(angle) * speed * 0.7;
splashes.push(
new Particle(
particle.pos.x,
groundY - 1, // slightly above ground
vx,
vy,
true
)
);
}
}
// Spawn a new fountain at the mouse position
function spawnFountainAtMouse() {
fountains.push(new Fountain(mouseX, mouseY));
}
```
---
### index.html
Make sure p5.js is loaded **before** `sketch.js`.
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
/>
<title>AI Particle Fountain</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<!-- p5.js core library (v1.11.3) -->
<!-- https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js -->
<script src="https://cdn.jsdelivr.net/npm/p5@1.11.3/lib/p5.min.js"></script>
<!-- Your sketch -->
<script src="sketch.js"></script>
</body>
</html>
```
---
### style.css
```css
/* style.css */
* {
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
overflow: hidden;
background: radial-gradient(circle at top, #1a1f33 0%, #050509 55%, #000 100%);
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI",
sans-serif;
color: #fff;
}
/* p5 canvas */
canvas {
display: block;
}
/* Optional: sliders styling for nicer look */
#ui input[type="range"] {
-webkit-appearance: none;
appearance: none;
height: 4px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.25);
outline: none;
margin: 0 4px;
}
#ui input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 14px;
height: 14px;
border-radius: 50%;
background: #ffffff;
cursor: pointer;
box-shadow: 0 0 6px rgba(0, 0, 0, 0.5);
}
#ui input[type="range"]::-moz-range-thumb {
width: 14px;
height: 14px;
border-radius: 50%;
background: #ffffff;
cursor: pointer;
border: none;
box-shadow: 0 0 6px rgba(0, 0, 0, 0.5);
}
```
---
Usage notes:
- Click anywhere on the canvas to spawn a new fountain at that position.
- Use the **Gravity** slider to control how quickly particles fall.
- Use the **Initial speed** slider to control how powerfully particles shoot upward.
- Colors shift from cooler (blue) to hotter (pink/white) with higher velocity, and trails are created via the semi-transparent background and additive blending.