Search Community

# Logic for particle effect

Moderator Tag

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax.

## Recommended Posts

Hi guys,

I'm trying to achieve a particle effect like this,
http://soulwire.github.io/sketch.js/examples/particles.html
I understand that the example given uses a sketch.js engine to create, but I'm looking to create this using gsap and also using image files rather than shapes, so I'm trying to find out what is the 'physics' logic that causes the particle to wiggle left/right randomly as it fades away. Anyone has any idea? I probably can come up with the particle generator, etc, but the wiggling movement is the one I'm having trouble figuring out... =(

##### Share on other sites

Sketch.js is just a helper library to reduce the amount of boilerplate code used in creative coding. It contains no logic for doing any sort of animation. That example is using nothing but vanilla JavaScript and canvas.

That animation is all physics based, so I don't think GSAP will be of much use. Tweens are just not ideal for something very dynamic like that. Sure, you can do it, but it's not worth it my opinion. It will usually require more code, and performance won't be as good.

But since you asked, here's how that animation works. On every animation frame, the velocity and heading of each particle is updated in this method.

```move() {
this.x += this.vx;
this.y += this.vy;
this.vx *= this.drag;
this.vy *= this.drag;
this.theta += random( -0.5, 0.5 ) * this.wander;
this.vx += sin(this.theta) * 0.1;
this.vy += cos(this.theta) * 0.1;
}```

The velocity, vx/vy is added to the x and y position of the particle. The velocity is then reduced by a drag/friction value. A small, but random change is added to the rotation (theta) of the particle. The velocities are updated to change direction with the updated rotation/theta value. The radius is reduced by a small amount. If the radius drops below 0.5, the particle is dead, and will be removed from the list of particles to update/render on the next animation frame. This method will be called on every animation frame until the particle is dead. Rinse and repeat.

And using images is much better than drawing shapes for performance. Ideally, all your images should be on a spritesheet/texture atlas. When you use separate images, each one has to be re-uploaded to the GPU every time you change what you're drawing, which can be a really slow process.

If you've never done a physics based animation before, here are two really good articles that you should check out. The code for those animations is incredibly short and simple.

• 1
##### Share on other sites

And here's something I was working on earlier that is pretty close to that animation.

See the Pen RLOzxo by osublake (@osublake) on CodePen

• 5
##### Share on other sites

Hi @OSUblake ,

Oh my! You're indeed a Superhero!

Yes, the explanation for the physics is exactly what I'm looking for. I want to learn the logic behind it rather than copy and paste.
Yup, I have previously used gsap for some small particle effects and also asked some questions in this forum before, it all worked awesomely, but I do find it starts to get slightly laggy if I increase the count too much, but luckily for that particular project I don't need too many particles spawning so I was able to get away with it. I probably can get away with it this round too, but definitely want to look into a better way to do this.

• 2
• 1
##### Share on other sites

If you need anymore help, just ask or post a demo. I have lots of examples and experience doing this kind of stuff.

And there's nothing wrong with using GSAP for particles, but it really depends on what you're trying to do. Here's a couple good examples.

In this one, the rotation, scale, and alpha of the faces aren't that dynamic, so I use GSAP to animate those properties. However, the position of the faces are constantly changing and are affected by gravity, so I update the position for every face in the update method on every animation frame.

See the Pen vNwRdO by osublake (@osublake) on CodePen

And in this one, I'm using tweens for everything, but it's actually physics based. I'm modifying the velocity for each particle using the ModifersPlugin

See the Pen oLGBXy by osublake (@osublake) on CodePen

• 2
##### Share on other sites

Hi @OSUblake,
I think I got a slight hang of how to generate particles in the canvas already, but trying out the movement physics you stated previously, all my particles are just moving diagonally in a straight line instead of in a sin-curve-like way. Did I miss out anything? I wanted to try to grab out from your canvas filter, but there was a lot more stuff in it so I'm not sure what is relevant and what is not. Here's what I'm using,

```const Particle = function () {
this.x      = random( w )
this.y      = random( h )
this.radius = random( 1, 3 )
this.angle  = random( Math.PI * 2 )
this.force  = random( 2, 6 )
this.color  = random( colors )
this.vx = Math.sin( this.angle ) * this.force
this.vy = Math.sin( this.angle ) * this.force
this.drag = random( 0.82, 0.97 )
this.wander = random( 0.5, 1 )
}

Particle.prototype.draw = function () {
context.beginPath()
context.arc( this.x, this.y, this.radius, 0, 360, false )
context.fillStyle = this.color
context.fill()

this.move()
}

Particle.prototype.move = function () {
this.x += this.vx
this.y += this.vy
this.vx *= this.drag
this.vy *= this.drag
this.angle += random( -0.5, 0.5 ) * this.wander
this.vx += Math.sin( this.angle ) * 0.1
this.vy += Math.sin( this.angle ) * 0.1
}```

##### Share on other sites

It looks like you are missing cosine from your vx and vy calculations. I used cosine for the y-component, but it's typically used for the x-component. Doesn't really matter in this case as it just changes the start angle from 3 o'clock to 12 o'clock.

```//context.arc( this.x, this.y, this.radius, 0, 360, false )

context.arc( this.x, this.y, this.radius, 0, Math.PI * 2, false )```

• 1
• 1
##### Share on other sites

Ah! Dang, I must be blind. Didn't notice that I had both with sin. Thanks!
Hmm... But when I use Math.PI * 2, the circle doesn't close properly and result in a chip off... Not sure why...

##### Share on other sites

Don't know why your circle isn't being closed. Might be something you're doing with the context somewhere else in your code. I just made a quick fork of my demo using circles, and they look fine.

See the Pen a6b00a70e0b6767ed7ddd6db4284aca5 by osublake (@osublake) on CodePen

• 2
##### Share on other sites

Hi @OSUblake ,

Weird, not sure why, but when I re-try it again, the circle now completes with Math.PI... Heh...
Want to try creating a 'tail' next. Do you have any suggestions on creating that? Should I duplicate the circles, reduce the size and follow behind, or should I try using lines to draw it?

##### Share on other sites

It depends. Can you show me what it should it look like?

##### Share on other sites

My end goal is to be able to create something like this, but definitely, want to go step by step so I can learn bits by bit, cause this is surely a huge leap... Haha.

See the Pen begkI by soulwire (@soulwire) on CodePen

##### Share on other sites

Sorry another dumb question, I'm currently dotting out a sine curve from left to right using the following code,

```this.x += this.speed
this.y = this.amp * Math.sin( this.x * this.cycles ) + this.center```

How do I add a rotation to this curve?

##### Share on other sites

Sorry, I was sick and didn't get a chance to respond. I'll look at this later tonight.

##### Share on other sites

On 10/26/2017 at 8:11 AM, Learning said:

Sorry another dumb question, I'm currently dotting out a sine curve from left to right using the following code,

```
this.x += this.speed
this.y = this.amp * Math.sin( this.x * this.cycles ) + this.center```

How do I add a rotation to this curve?

Just real quick, I don't know what other code you have there, but rotation can calculated from the velocity. Velocity would just be the change in x and y, so it might look something like this. But that's just a guess. Hard to tell without seeing your other code.

```var oldX = this.x;
var oldY = this.y;

this.x += this.speed
this.y = this.amp * Math.sin( this.x * this.cycles ) + this.center

var dx = this.x - oldX;
var dy = this.y - oldY;

var rotation = Math.atan2(dy, dx); // in radians```

• 2
##### Share on other sites

Hi @OSUblake,

Sorry, went on a week trip overseas, just got back!
Hope you are feeling better~
I'm back to playing around with particles. Haha. Will try out the rotation in abit!
Thanks again!

• 1
##### Share on other sites

Are you still trying to figure out how to create those tails?

##### Share on other sites

I haven't got to the tails part yet though, still playing around with particle movements, trying different math equations and also the rotation stuff.

##### Share on other sites

• 1 year later...

The initial "star" demo is awesome but, unlike the particle effect, it always hides any HTML elements beneath the canvas. I am somewhat new to canvas-based development but have done enough that I was surprised at this.  Is there a way to place the effect on an otherwise transparent canvas?  I tried messing around with the Blend Modes but they just either disabled the effect or otherwise made it less attractive.  In all cases, though, the underlying elements on the page were hidden.  If I changed the background alpha, I got effects like the attached (in one iteration I did also have the HTML elements showing but it still had the problem shown). Any help you can offer is greatly appreciated.

Mark B (Robot Doc)

##### Share on other sites

By the way - the effects shown above stayed on the screen permanently; it wasn't just a funny but temporary way of drawing the stars.

• 1
##### Share on other sites

9 hours ago, Robot Doc said:

The initial "star" demo is awesome but, unlike the particle effect, it always hides any HTML elements beneath the canvas. I am somewhat new to canvas-based development but have done enough that I was surprised at this.  Is there a way to place the effect on an otherwise transparent canvas?

Yep. I did that to improve performance. Read about alpha in the 2d context attributes here

Quote
• `alpha`: Boolean that indicates if the canvas contains an alpha channel. If set to `false`, the browser now knows that the backdrop is always opaque, which can speed up drawing of transparent content and images.

So you need to get rid of the alpha option on line ~109:

```// this.context = this.view.getContext("2d", { alpha: false });
this.context = this.view.getContext("2d");  ```

And use clearRect instead of fillStyle and fillRect on line ~137:

```// context.fillStyle = this.backgroundColor;
// context.fillRect(0, 0, this.width, this.height);
context.clearRect(0, 0, this.width, this.height);```

And that should allow you see through the canvas.

• 3
##### Share on other sites

Thank you!  That is moving in the right direction but now I am getting the result shown below.  It seems related to the fillRect: if I include it, your demo looks right (stuff gets removed from the canvas) but the image is hidden.  If I leave it out, the previous frame data doesn't get cleared even though I am calling clearRect.

##### Share on other sites

Ha - never mind!  I did a clearRect on the bufferContext rather than Context (the Canvas).  It works now.

##### Share on other sites

Hi @OSUblake,

I think I know the answer to this question but would love to get verification from someone more knowledgeable. The demo that you've provided is very fast on my computer (a very fast gaming rig) but, as far as I can tell, is not using WebGL and is thus not using hardware acceleration. Is that correct?

Mozilla says that "GetContext" with an argument of "2d" just returns a Canvas rendering context.  You can, in theory, use "webgl," but if I try to use it in your example, I get a number of errors about calls not supported and nothing works.

Would it make sense to try adapting your example to Pixi.js?  I'm planning something rather ambitious and I'd love to have WebGL on board if I could.

Thanks for the help!

##### Share on other sites

9 hours ago, Robot Doc said:

The demo that you've provided is very fast on my computer (a very fast gaming rig) but, as far as I can tell, is not using WebGL and is thus not using hardware acceleration. Is that correct?

My demo isn't using WebGL, but that doesn't mean it isn't using hardware acceleration.  A lot of 2d canvas drawing operations are still hardware accelerated. With WebGL you have more control over what and how stuff can be hardware accelerated.

9 hours ago, Robot Doc said:

Mozilla says that "GetContext" with an argument of "2d" just returns a Canvas rendering context.  You can, in theory, use "webgl," but if I try to use it in your example, I get a number of errors about calls not supported and nothing works.

That's expected. 2d and WebGL use completely different APIs, so the code is completely incompatible with WebGL.

9 hours ago, Robot Doc said:

Would it make sense to try adapting your example to Pixi.js?  I'm planning something rather ambitious and I'd love to have WebGL on board if I could.

PixiJS is one of the easiest ways to take advantage of WebGL, you can't beat the performance, and there are a bunch of filters to choose from.

However, some of the filters in PixiJS won't work with a transparent background. Well, it was like that in earlier versions of Pixi. I haven't tried version 5 of PixiJS, which makes use of WebGL2, so I don't know if it still has that issue.

• 3