Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
Learning

Logic for particle effect

Warning: Please note

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... =(

Link to comment
Share on other sites

Hi @Learning

 

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;
  this.radius *= 0.96;
  this.alive = this.radius > 0.5;
}

 

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.

http://www.playfuljs.com/particle-effects-are-easy/

http://www.playfuljs.com/physics-for-the-lazy/

 

And for more info about particles, canvas, and GSAP, check out this thread.

 

  • Like 1
Link to comment
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

 

  • Like 5
Link to comment
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.
Going to read the articles and links! Thanks so much~

  • Like 2
  • Thanks 1
Link to comment
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

 

 

 

  • Like 2
Link to comment
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
}

 

Link to comment
Share on other sites

Hi @Learning

 

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.

 

Also, your arc is in degrees when it be in radians.

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

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

 

  • Like 1
  • Haha 1
Link to comment
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...

Link to comment
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

 

  • Like 2
Link to comment
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?

Link to comment
Share on other sites

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

Link to comment
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

 

Link to comment
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?

Link to comment
Share on other sites

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

Link to comment
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

 

  • Like 2
Link to comment
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!

  • Like 1
Link to comment
Share on other sites

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

Link to comment
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.

Link to comment
Share on other sites

  • 1 year later...

@OSUblake,

 

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)

Screenshot 2019-08-24 23.22.45.png

Link to comment
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.

  • Like 1
Link to comment
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.

  • Like 3
Link to comment
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.

Screenshot 2019-08-25 09.34.06.png

Link to comment
Share on other sites

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

Link to comment
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!

Link to comment
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.

https://pixijs.io/pixi-filters/tools/demo/

 

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. 

 

 

  • Like 3
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×